目前,至此项目只实现了加载 js (如在控制台中输出 Hello World! ),那么 webpack 是怎么加载其他类型的资源呢?如:images

在 webpack 出现之前,前端开发人员会使用 grunt 和 gulp 等工具来处理资源,并将它们从 /src 文件夹移动到/dist/build 目录中。webpack 最出色的功能之一就是,除了引入 JavaScript,还可以使用内置的资源模块 Asset Modules 引入任何其他类型的文件

资源模块(asset module)是一种模块类型,它允许我们应用Webpack来打包其他资源文件(如字体,图标等)

资源模块类型

通过添加 4 种新的模块类型,来替换所有这些 loader:

  • asset/resource 发送一个单独的文件并导出 URL
  • asset/inline 导出一个资源的 data URI
  • asset/source 导出资源的源代码
  • asset 在导出一个 data URI 和发送一个单独的文件之间自动选择

使用资源模块之前,先要配置 webpack.config.js

  1. // 配置资源文件
  2. module: {
  3. rules: [
  4. {
  5. test: /\.jpg$/,
  6. type: 'asset/resource'
  7. }
  8. ]
  9. }

1. Resource 资源

会导出一个 URL,而且打包后也可以在 dist 目录中看见导出的文件

module属性中的rules数组中配置规则

  1. // 配置资源文件
  2. module: {
  3. rules: [
  4. {
  5. // 校验文件类型
  6. test: /\.jpg$/,
  7. // 资源模块类型
  8. type: 'asset/resource'
  9. }
  10. ]
  11. }

image.png
入口文件:js/index.js

  1. import hello from './hello'
  2. import jpgSrc from '../assets/image/wlop.jpg'
  3. // 加载 js 格式文件
  4. hello()
  5. // 加载 jpg 格式文件
  6. const img1 = document.createElement('img')
  7. img1.src = jpgSrc
  8. document.body.appendChild(img1)

打包:
image.png
image.png
图片已经打包在 dist 目录下了,产生一个带有默认名字的图片;而在浏览器中以原图片格式加载出来
当然,我们也可以自定义打包输出的文件名

自定义输出文件名:

默认情况下,asset/resource 模块以 [contenthash][ext][query]文件名发送到输出目录
contenthash:根据文件的内容生产 hash
ext:扩展名

① generation
  1. rules: [
  2. {
  3. // 校验文件格式
  4. test: /\.jpg$/,
  5. // 资源模块类型
  6. type: 'asset/resource',
  7. // 定义输出文件的文件名
  8. generator: {
  9. filename: 'img/[contenthash][ext][query]'
  10. }
  11. }

② output.assetModuleFilename
  1. // 设置项目打包的出口文件
  2. output: {
  3. // 指定输出文件的文件名
  4. filename: 'bundle.js',
  5. // 指定文件的输出路径(只能是绝对路径)
  6. path: path.resolve(__dirname, './dist'),
  7. // 清除上一次打包,但这一次打包不需要的文件
  8. clean: true,
  9. // 定义输出文件的文件名(优先级小于generator)
  10. assetModuleFilename: 'img/[contenthash][ext][query]'
  11. }

两种方法都存在,则优先级:generator> assetModuleFilename

重新打包后会在 dist/img 目录下生成图片
image.png

2. inline 资源

只导出资源的 URL,不会导出一个单独的文件,所以不会在 dist 产生新的文件

  1. rules: [
  2. {
  3. test: /\.jpg$/,
  4. type: 'asset/resource',
  5. generator: {
  6. filename: 'img/[contenthash][ext][query]'
  7. }
  8. },
  9. {
  10. test: /\.svg$/,
  11. // 注:inline资源模块类型不会导出文件
  12. type: 'asset/inline'
  13. }
  14. ]
  1. import hello from './hello'
  2. import jpgSrc from '../assets/image/wlop.jpg'
  3. import svgSrc from '../assets/image/勇气徽章.svg'
  4. // 加载 js 格式文件
  5. // hello()
  6. // 加载 jpg 格式文件
  7. const img1 = document.createElement('img')
  8. img1.src = jpgSrc
  9. document.body.appendChild(img1)
  10. // 加载 svg 格式文件
  11. const img2 = document.createElement('img')
  12. img2.src = svgSrc
  13. document.body.appendChild(img2)

执行 npx webpack 打包后,没有看见产生对应的文件
执行 npx webpack-dev-server进入浏览器查看:
image.png
不是图片的 url,而是 base64 的格式
可见, .svg 文件都将作为 data URI 注入到 bundle 中

自定义 data URI 生成器 (不常用)

webpack 输出的 data URI,默认是呈现为使用 Base64 算法编码的文件内容。
如果要使用自定义编码算法,则可以指定一个自定义函数来编码文件内容

  1. 安装依赖

    npm install mini-svg-data-uri -D

  2. 修改配置文件

    1. const svgToMiniDataURI = require('mini-svg-data-uri')
    2. rules: [
    3. {
    4. test: /\.svg$/,
    5. // 注:inline资源模块类型不会导出文件
    6. type: 'asset/inline',
    7. // 自定义data url
    8. generator: {
    9. dataUrl: context => {
    10. return svgToMiniDataURI(context.toString())
    11. }
    12. }
    13. }
    14. ]

    现在,所有 .svg 文件都将通过 mini-svg-data-uri 包进行编码。重新启动服务,在浏览器查看效果:
    image.png
    确实精简了一些

3. source

只会导出资源的源代码

  1. module: {
  2. rules: [
  3. {
  4. test: /\.txt$/,
  5. // 注:source资源模块类型不会导出文件
  6. type: 'asset/source'
  7. }
  8. ]
  9. }

webpack5.txt hello Webpack5!!!

  1. import hello from './hello'
  2. import jpgSrc from '../assets/image/wlop.jpg'
  3. import svgSrc from '../assets/image/勇气徽章.svg'
  4. import txt from '../assets/text/webpack5.txt'
  5. // 加载 js 格式文件
  6. // hello()
  7. // 加载 jpg 格式文件
  8. const img1 = document.createElement('img')
  9. img1.src = jpgSrc
  10. document.body.appendChild(img1)
  11. // 加载 svg 格式文件
  12. const img2 = document.createElement('img')
  13. img2.src = svgSrc
  14. document.body.appendChild(img2)
  15. // 加载 txt 格式文件
  16. const block = document.createElement('div')
  17. block.style.cssText = 'width: 500px;height: 100px;line-height: 100px;text-align: center;background-color: skyblue'
  18. block.textContent = txt
  19. document.body.appendChild(block)

打包:并未在 dist 中导出文件,所有 .txt 文件将原样注入到 bundle 中
浏览器中:

<div style="width: 500px; height: 100px; line-height: 100px; text-align: center; background-color: skyblue;">
  hello Webpack5!!!
</div>

4. 通用资源类型

它会在导出一个 data URI 和发送一个单独的文件之间自动选择

    module: {
        rules: [
            {
                test: /\.png$/,
                type: 'asset'
            }
        ]
    }

现在,webpack 将按照默认条件,自动地在 resource 和 inline 之间进行选择:
小于 8kb 的文件,将会视为 inline 模块类型,否则会被视为 resource 模块类型
可以通过在 webpack 配置的 module rule 层级中,设置 Rule.parser.dataUrlCondition.maxSize 选项来修改此条件:
image.png

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    // 设置项目打包的入口文件
    entry: './src/js/index.js',

    // 设置项目打包的出口文件
    output: {
        // 指定输出文件的文件名
        filename: 'bundle.js',
        // 指定文件的输出路径(只能是绝对路径)
        path: path.resolve(__dirname, './dist'),
        // 清除上一次打包,但这一次打包不需要的文件
        clean: true,
        // 定义输出文件的文件名(优先级小于generator)
        assetModuleFilename: 'img/[contenthash][ext][query]'
    },

    // development--开发模式
    mode: 'development',

    // 能够准确的捕获代码方式错误的位置
    // inline-source-map:在开发模式下追踪代码(mode: 'development',)
    devtool: 'inline-source-map',

    // 插件
    plugins: [
        new HtmlWebpackPlugin({
            // 指定哪个html文件为模板
            template: './src/index.html',
            // 指定输出文件的文件名
            filename: 'index.html',
            // 指定生成的 script 标签在 head 中,还是在 body 中
            inject: 'body'
        })
    ],

    // 修改代码后实现浏览器热更新(结合 npx webpack-dev-server 命令开启本地服务器一起使用)
    devServer: {
        static: './dist'
    },

    // 配置资源文件
    module: {
        rules: [
            {
                // 校验文件格式
                test: /\.jpg$/,
                // 资源模块类型
                type: 'asset/resource',
                // 定义输出文件的文件名
                generator: {
                    filename: 'img/[contenthash][ext][query]'
                }
            },
            {
                test: /\.svg$/,
                // 注:inline资源模块类型不会导出文件
                type: 'asset/inline'
            },
            {
                test: /\.txt$/,
                // 注:source资源模块类型不会导出文件
                type: 'asset/source'
            },
            {
                test: /\.png$/,
                type: 'asset',
                parser: {
                    dataUrlCondition: {
                        // 默认4*1024(4kb)
                        maxSize: 4*1024
                    }
                }
            }
        ]
    }
}
import hello from './hello'
import jpgSrc from '../assets/image/wlop.jpg'
import svgSrc from '../assets/image/勇气徽章.svg'
import txt from '../assets/text/webpack5.txt'
import candle from '../assets/image/蜡烛.png'

// 加载 js 格式文件
// hello()

// 加载 jpg 格式文件
const img1 = document.createElement('img')
img1.src = jpgSrc
document.body.appendChild(img1)

// 加载 svg 格式文件
const img2 = document.createElement('img')
img2.src = svgSrc
document.body.appendChild(img2)

// 加载 png 格式文件
const img3 = document.createElement('img')
img3.src = candle
document.body.appendChild(img3)

// 加载 txt 格式文件
const block = document.createElement('div')
block.style.cssText = 'width: 500px;height: 100px;line-height: 100px;text-align: center;background-color: skyblue'
block.textContent = txt
document.body.appendChild(block)

文件大于阈值走 resource,低于阈值走 inline