你可以看懂的webpack5知识

1,428 阅读8分钟

webpack介绍

webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。是一种前端构建工具。

webpack 五个核心概念

Entry

入口(Entry):指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。

Loader

Loader:让 webpack 能够去处理那些非 JS 的文件 / json资源,比如样式文件、图片文件(webpack 自身只理解 JS)

Plugins

插件(Plugins):可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩, 一直到重新定义环境中的变量等。

Mode

模式(Mode):指示 webpack 使用相应模式的配置。

  • development:生产模式,能让代码在本地进行调试运行的环境
  • production:能让代码在线上优化上线的运行环境。

Output

输出(Output):指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。

简单的webpack打包测试

image.png

打包js, json文件

import data from './demo.json';
// 引入非js,json资源,进行打包会报错,所以需要通过loader来使webpack可以打包非js,json的资源。
// import "./index.css";
console.log("=========", "我是index.js文件");
console.log(data)
​
// webpack ./src/index.js -o ./build/built.js --mode=development 
// 打包js,json文件不会报错

结论:

  1. webpack能处理js/json资源,不能处理其他资源。
  2. 生产环境和开发环境都能将 es6 的模块化语法转换成浏览器能识别的语法。
  3. 生产环境会压缩打包的js代码。

image.png

让webpack支持css的打包

use 数组中 loader 执行顺序:从右到左,从下到上 依次执行

'style-loader', 创建 style 标签,将 js 中的样式资源插入进行,添加到 head 中生效 。

'css-loader' ,将 css 文件变成 commonjs 模块加载 js 中,里面内容是样式字符串 。

 // 处理css文件
{
  // 这里表示匹配的文件,都需要通过该loader转化。
  test: /.css$/,
    // 需要使用到的loader,从后往前执行
    use: [
      // 创建 style 标签,将 js 中的样式资源插入进行,添加到 head 中生效 。
      'style-loader',
      // 将 css 文件变成 commonjs 模块加载 js 中,里面内容是样式字符串 。
      'css-loader'
    ]
},

image.png

让webpack支持sass的打包

注意:这里需要下载sass和sass-loader两个包,不然报‘cannot find module sass’

 // 处理sass文件
{
  test: /.scss$/,
    use: [
      'style-loader',
      'css-loader',
      'sass-loader'
    ]
}

让webpack支持打包html资源

安装插件 html-webpack-plugin。而且需要引入才能使用。

默认创建一个空的html文件,然后引入打包后的js文件。

如果想要指定html模板,需要传入template属性。指定模板的路径。

// 引入html打包插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ]

让webpack支持打包图片资源

在 webpack5+,以上方法已经过时了,webpack5 使用了“资源模块”来代替以上 loader。 官方是这样解释“资源模块”的。

资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。

  • asset/resource: 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
  • asset/inline: 导出一个资源的 data URI。之前通过使用 url-loader 实现。
  • asset/source: 导出资源的源代码。之前通过使用 raw-loader 实现。
  • asset: 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。 具体访问juejin.cn/post/697033… 处理样式中图片的方式

问题:默认处理不了 html 中 img 图片

// 方式一
 {
   test: /.(jpe?g|png|svg|gif)/i,
     type: 'asset/resource',
 },
// 方式二
  // 这个是处理样式中引入的图片资源
  {
    test: /.(png|gif|jpg|jpeg)/,
      use: [
        {
          loader: "url-loader",
          options: {
            // 优点: 减少请求数量(减轻服务器压力) // 缺点:图片体积会更大(文件请求速度更慢) 
            limit: 8 * 1024, // 指定图片最大多少时转为base64格式。就是小于这个就被转化
            name: '[name].[hash:10].[ext]',// 重命名文件
            outputPath: 'static/img',// 文件的输出位置
            esModule: false, // 这里需要将esModule关闭,因为他与commonjs模块化冲突
          }
        }
      ],
        type: 'javascript/auto'
  },

处理html中引入的图片

他的作用是处理html中的img,然后交给url-loader处理。

// 这个是处理html中引入的图片资源
{
  test: /.html$/,
    loader: 'html-loader'
}

让webpack支持打包其他资源

file-loader

// 打包其他资源(除了 html/js/css 资源以外的资源) 
{ // 排除 css/js/html 资源 
  exclude: /.(css|js|html|sass|json|png|gif|jpg|jpeg)$/,
    loader: 'file-loader',
      options: { name: '[hash:10].[ext]' }
}

可是自己测试,如果引入字体图标。然后webpack会指定打包,不需要使用file-loader。

url-loader和file-loader的区别:前者是将非文本小文件转化为base64 URI。减少对服务器的请求。

开发环境中的完整配置

const { resolve } = require("path")
// 引入html打包插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
​
module.exports = {
  entry: "./src/js/index.js",
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      // 处理css文件
      {
        // 这里表示匹配的文件,都需要通过该loader转化。
        test: /.css$/,
        // 需要使用到的loader,从后往前执行
        use: [
          // 创建 style 标签,将 js 中的样式资源插入进行,添加到 head 中生效 。
          'style-loader',
          // 将 css 文件变成 commonjs 模块加载 js 中,里面内容是样式字符串 。
          'css-loader'
        ]
      },
      // 处理sass文件
      {
        test: /.(scss|sass)$/,
        use: ['style-loader', 'css-loader', 'sass-loader'],
      },
      // 处理图片 方式一
      // {
      //   test: /.(jpe?g|png|svg|gif)/i,
      //   type: 'asset/resource',
      // },
      // 这个是处理样式中引入的图片资源 方式二
      {
        test: /.(png|gif|jpg|jpeg)/,
        use: [
          {
            loader: "url-loader",
            options: {
              limit: 8 * 1024,
              name: '[name].[hash:10].[ext]',
              outputPath: 'static/imgs',
              esModule: false,
            }
          }
        ],
        type: 'javascript/auto'
      },
      // 这个是处理html中引入的图片资源
      {
        test: /.html$/,
        loader: 'html-loader'
      },
      // 打包其他资源(除了 html/js/css 资源以外的资源) 
      { 
        // 排除上面处理后的文件资源
        exclude: /.(css|js|html|sass|json|png|gif|jpg|jpeg)$/,
        loader: 'file-loader',
        options: { name: '[hash:10].[ext]', outputPath: "static/others" }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  // 使用的模式
  mode: 'development',
  devServer: { 
      // 项目构建后路径 。当我们在打包后的资源中,引入的文件中,未能在打包后的资源中找到,就会在contentBase指定的文件夹中查找。在开发阶段方便打包。不需要使用copyWebpackPlugin插件将资源复制到打包后的资源中。
    contentBase: resolve(__dirname, 'build'),
    // 启动 gzip 压缩 
    compress: true,
    // 端口号 
    port: 3000,
    // 自动打开浏览器 
    open: true
  }
}

让webpack将css单独打包

style-loader: 他的作用是在html中创建一个style标签,然后将js中的css写入该style中,这样就使得js文件非常大,可能出现闪屏现象,所以要将css文件提取出,单打打包。

mini-css-extract-plugin该插件就可以提供一个loader来将css单独打包。

module: {
  rules: [
    // 处理css
    {
      test: /.css$/,
      use: [
        // 将js中的css单独打包
        MiniCssExtractPlugin.loader,
        'css-loader'
      ]
    }
  ]
},
plugins: [
  new MiniCssExtractPlugin({
    // 命名打包后的文件
    filename: 'static/built.css'
  })
],

处理css的兼容性问题

需要安装postcss-loader, postcss-preset-env。

npm install --save-dev postcss-loader postcss-preset-env

需要在package.json中配置开发环境和生产环境中的兼容。

浏览器的兼容性处理请访问github.com/browserslis…

"browserslist": { 
  "development": [ 
    "last 1 chrome version", 
    "last 1 firefox version", 
    "last 1 safari version" 
  ],
  "production": [ 
    ">0.2%", "not dead", 
    "not op_mini all" 
  ] 
}

而且打包时,webpack默认是按照生产环境来做css的兼容性处理的。如果想要按照开发环境处理,我们需要在webpack.config.js中配置环境变量

process.env.NODE_ENV = 'development';
module: {
    rules: [
      // 处理css
      {
        test: /.css$/,
        use: [
          // 将js中的css单独打包
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                // webpack 4配置
                // ident: 'postcss',
                // plugins: () = [
                //   require('postcss-preset-env')()
                // ]
                // webpack 5配置
                plugins: [require('postcss-preset-env')()]
              }
            }
          }
        ]
      }
    ]
  },

image.png

压缩css文件

安装optimize-css-assets-webpack-plugin插件。然后引入使用即可

// 压缩css文件
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin")
plugins: [
  new OptimizeCssAssetsWebpackPlugin()
],

让webpack支持做js语法检查

安装包:npm install --save-dev eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import

package.json中配置检查规则

"eslintConfig": {
  "extends": "airbnb-base",
  "env": {
    "browser": true
  }
}

webpack.config.js中配置检查

  module: {
    rules: [
      {
        // webpack对js语法做检查
        test: /.js$/,
        exclude: /node_modules/,
        loader: 'eslint-loader',
        //  当一个文件需要加载多个loader时,指定谁先执行。
        enforce: 'pre',
        options: {
          // 自动修复 eslint 的错误
          fix: true,
        },
      },
    ],
  },

或者通过eslint-webpack-plugin插件

// 检查js代码
const EslintWebpackPlugin = require('eslint-webpack-plugin');
plugins: [
  new EslintWebpackPlugin(),
],

处理js的兼容性问题

安装包:npm install --save-dev babel-loader @babel/core @babel/preset-env core-js

如果不需要将js全部做兼容性:就不需要下载并引入@babel/polyfill包。

module: {
  rules: [
    // 处理js的兼容性
    {
      test: /.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader',
      options: { 
        // 预设:指示 babel 做怎么样的兼容性处理
        presets: [['@babel/preset-env', {
          // 按需加载
          useBuiltIns: 'usage',
          // 指定 core-js 版本
          corejs: { version: 3 },
          // 指定兼容性做到哪个版本浏览器
          targets: {
            chrome: '60', 
            firefox: '60', 
            ie: '9', 
            safari: '10', 
            edge: '17',
          },
        }]],
      },
    },
  ],
},

压缩js,html文件

直接将mode属性设置成production。表示在生产环境中,他会自动压缩js文件和html文件。

image.png

或者指定html中压缩项

 plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      // 压缩 html 代码 
      minify: {
        // 移除空格 
        collapseWhitespace: true,
        // 移除注释 
        removeComments: true
      }
    }),
  ],

fullhash(hash) | chunkhash | contenthash区别

  • fullhash:整个项目使用相同的hash值,当一个文件改变,整个项目的hash值将会改变。 image.png

  • chunkhash:每一个入口文件中的依赖hash值是相同的。 image.png 改变b.js文件 image.png 这里需要注意一下,当我们使用多入口打包,并且使用splitChunks进行分包时,多个文件引入的相同依赖单独打包,改变这个chunk文件时是不会影响其他打包输出的文件chunkhash值的变化的。但是如果是改变动态import导入的文件,那么不管是引入该文件的文件还是import引入的文件,他们的chunkhash都会改变。

  • contenthash:为每一个文件生成独一为二的hash值,改变一个文件,不会影响其他文件。 image.png 随便改变一个文件

image.png

生产环境中的完整配置

同一个类型文件加载loader,我们需要搞清楚他的优先级。防止不必要的错误。就好像js文件需要检查代码规范和js的兼容性。代码检查的优先级就比js兼容性的高。所以需要先加载eslint-loader,在加载babel-loader。这是我们可以分开写(可以指定enforce: ‘pre’),或者将loader写在use数组中。

const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 压缩css文件
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
// 检查js代码
// const EslintWebpackPlugin = require('eslint-webpack-plugin');
// process.env.NODE_ENV = 'development';
module.exports = {
  entry: './src/js/index.js',
  // 将打包后的文件放在哪
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build'),
  },
  module: {
    rules: [
      // 处理css
      {
        test: /.css$/,
        use: [
          // 将js中的css单独打包
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                // webpack 4配置
                // ident: 'postcss',
                // plugins: () = [
                //   require('postcss-preset-env')()
                // ]
                // webpack 5配置
                plugins: [require('postcss-preset-env')()],
              },
            },
          },
        ],
      },
      // 处理scss
      {
        test: /.sass|scss$/,
        use: [
          // 将js中的css单独打包
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                // webpack 4配置
                // ident: 'postcss',
                // plugins: () = [
                //   require('postcss-preset-env')()
                // ]
                // webpack 5配置
                plugins: [require('postcss-preset-env')()],
              },
            },
          },
          'sass-loader',
        ],
      },
      // 处理图片
      {
        test: /.(png|gif|jpg|jpeg)/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8 * 1024,
              name: '[name].[hash:10].[ext]',
              outputPath: 'static/imgs',
              esModule: false,
            },
          },
        ],
        type: 'javascript/auto',
      },
      // 处理html中的图片
      {
        test: /.html$/,
        loader: 'html-loader',
      },
      // 处理其他资源
      { // 排除 css/js/html 资源
        exclude: /.(css|js|html|sass|json|png|gif|jpg|jpeg)$/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          outputPath: 'static/others',
        },
      },
      // {
      //   // webpack对js语法做检查
      //   test: /.js$/,
      //   exclude: /node_modules/,
      //   loader: 'eslint-loader',
      //   //  当一个文件需要加载多个loader时,指定谁先执行。
      //   enforce: 'pre',
      //   options: {
      //     // 自动修复 eslint 的错误
      //     fix: true,
      //   },
      // },
      // 处理js的兼容性
      {
        test: /.js$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              // 预设:指示 babel 做怎么样的兼容性处理
              presets: [['@babel/preset-env', {
                // 按需加载
                useBuiltIns: 'usage',
                // 指定 core-js 版本
                corejs: { version: 3 },
                // 指定兼容性做到哪个版本浏览器
                targets: {
                  chrome: '60',
                  firefox: '60',
                  ie: '9',
                  safari: '10',
                  edge: '17',
                },
              }]],
            },
          },
          {
            loader: 'eslint-loader',
            options: {
              // 自动修复 eslint 的错误
              fix: true,
            },
          },
        ],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      // 压缩 html 代码
      minify: {
        // 移除空格
        collapseWhitespace: true,
        // 移除注释
        removeComments: true,
      },
    }),
    new MiniCssExtractPlugin({
      // 命名打包后的文件
      filename: 'static/css/built.css',
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    // new EslintWebpackPlugin(),
  ],
  mode: 'production',
};

构建代码已上传github github.com/zhang-glitc…

总结自b站尚硅谷webpack www.bilibili.com/video/BV1e7…