Webpack5基础与高级配置

13 阅读9分钟

为什么需要打包工具?

  1. 编译功能:我们在项目使用的框架、less、sass、es6等浏览器都不能识别,需要打包工具编译成css js等语法才能运行
  2. 还可以压缩代码、做兼容性处理、提高代码性能
  3. 主要的打包工具:webpack vite rollup

对webpack的理解?

  1. 是一个静态资源打包工具,它以一个或多个文件作为打包的入口,将整个项目的文件编译成一个或多个文件输出出去,输出文件bundle就是编译好的文件,浏览器可以运行。本身功能有限制,只能编译js的ES Module语法.
  2. 可以做什么?
    ① 编译代码,编译高级的语言或语法(TS、ES6+、sass等);
    ② 模块整合,在项目中有很多模块和文件,合并之后可以,解决浏览器频繁请求文件的问题;
    ③ 兼容性和错误检查(Polyfill、postcss、eslint);
    ④ 代码体积更小,加载更快(Tree-shaking、压缩、合并

webpack怎么使用

  1. 安装webpack 初始化package.json npm init -y
  2. 启用webpack:npx webpack ./src/main.js --mode=development,指定Webpack从main.js文件开始打包,并且打包 main.js其依赖也一起打包进来。
  3. npx是通过检查本地缓存或临时下载的包来执行命令,执行后会清理临时文件。首先它会在当前目录下的node_modules.bin文件去查找是否有可执行的命令,如果没有就从全局查找是否有安装对应的模块,全局也没有就会自动下载对应的模块,用完就删。
  4. 总结:webpack本身功能比较少,只能处理js资源,一旦遇到css等其他资源就会报错。所以学习webpack,就是主要学习如何处理其他资源。

常见的Loader?

  1. css-loader:加载css,支持模块化
  2. style-loader:将css添加到页面的style标签里面
  3. postcss-loader:css的兼容性处理
  4. less-loader:处理less文件
  5. file-loader:把文件输出到文件夹,代码可以通过url去引用文件
  6. url-loader:和file-loader类似,但是能在⽂件很⼩的情况下以base64的⽅式把⽂件内容注⼊到代码中去
  7. babel-loader:转换es6为es5
  8. eslint-loader:通过 ESLint 检查 JavaScript 代码
  9. vue-loader:处理vue文件

常见的Plugin?

  1. DefinePlugin: 定义环境变量,内置插件
  2. clean-webpack-plugin: 清理构建目录
  3. html-webpack-plugin: 打包后自动生成一个html文件,并把打包生成的js自动引入到模块
  4. mini-css-extract-plugin:将CSS提取到单独的⽂件中
  5. css-minimizer-webpack-plugin: 压缩css,代替webpack4的optimize-css-assets-webpack-plugin
  6. terser-webpack-plugin:压缩js,代替webpack4的uglifyjs-webpack-plugin
  7. copy-webpack-plugin:复制静态文件到输出目录
  8. image-minimizer-webpack-plugin:自动压缩图片

基础配置

核心概念
  1. entry(入口):提示文件从哪里打包
  2. output(输出):提示文件打包完的文件输出到哪里去,如何命名
  3. loader(加载器):webpack本身只能处理js资源,需要借助loader处理其他资源
  4. plugins(插件):扩展webpack功能
  5. mode(模式):分为开发模式与生产模式;
开发模式与生产模式
  1. 开发模式主要关注:① 编译代码,使浏览器能识别运行。图片、样式、字体、html资源等,webpack默认都不能处理这些资源,所以需要配置来编译这些资源;②检查代码质量、代码规范,统一团队的编码规范、让代码更加优雅美观。
  2. 生产模式需要代码将来部署上线,主要关注:优化代码运行性能;优化代码打包速度
处理css资源
开发模式
  1. 安装插件:less-loader css-loader style-loader
  2. less-loader: 将less文件编译成css文件
  3. css-loader:将css文件编译成webpack可以识别的模块
  4. style-loader:通过动态创造一个style标签,把css添加到html页面上
  5. 使用,从右到左执行loader:rules: [{test: /\.less$/, use: ["style-loader", "css-loader", "less-loader"]} ]
生产模式
  1. mini-css-extract-plugin:提取css为单独文件。在开发模式css文件被打包到js文件中,当js文件加载时,会创建一个style标签来生成样式。但是在生产模式实际打开有可能会出现闪屏现象,用户体验不好,新建单独的css文件,通过link标签加载性能才好。
  2. 使用:提取css成单独文件,定义输出文件名和目录:plugins: [new MiniCssExtractPlugin({filename: "static/css/main.css",})]
  3. postcss-loader:css兼容性处理,下载插件:npm i postcss-loader postcss postcss-preset-env -D,然后在 package.json 文件中添加 browserslist 来控制样式的兼容性做到什么程度;
  4. css-minimizer-webpack-plugin:压缩css,使用plugins: [new CssMinimizerPlugin()]
module: {
  rules: [{
    test: /\.less$/,
    //从右到左 less-loader postcss-loader css-loader MiniCssExtractPlugin.loader
    use: [
      // 1 生产环境style-loader替换成 MiniCssExtractPlugin.loader,
      MiniCssExtractPlugin.loader,
      "css-loader",
      {
        loader: "postcss-loader",
        options: {
          // 2 postcss-loader 能解决大多数样式兼容性问题
          postcssOptions: {
            plugins: [
              "postcss-preset-env",
            ],
          },
        },
      },
      // 3 将less文件编译成css文件
      "less-loader",
    ],
  ]}
},
plugins: [
    new HtmlWebpackPlugin({
      // 以 public/index.html 为模板创建文件
      // 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
      template: path.resolve(__dirname, "../public/index.html"),
    }),
    // 提取css成单独文件
    new MiniCssExtractPlugin({
      // 定义输出文件名和目录
      filename: "static/css/main.css",
    }),
    // css压缩
    new CssMinimizerPlugin(),
  ],
处理图片资源
  1. 在 Webpack4 时,我们处理图片资源通过file-loader url-loader进行处理;
  2. Webpack5已经将两个Loader功能内置到Webpack里了,只需简单配置即可处理图片资源rules: [{test:/.[png|jpe?g|gif|webp]$/, type: "asset"},]
  3. type: "asset" 相当于url-loader, 将文件转化成Webpack能识别的资源,同时小于某个大小的资源会处理成data URI形式;
  4. 将小于10kb的图片转为base64格式, 优点是减少请求数量。rules: [{test:/\.[png|jpe?g|gif|webp]$/, type: "asset"},perser:{dataUrlCondition:maxSize: 10 *1024}]
处理字体图标、其他资源如音视频
  1. 阿里巴巴矢量图标库添加字体图标需要下面文件:src/fonts/iconfont.ttf、src/fonts/iconfont.woff、 src/css/iconfont.css;引入后使用字体图标<i class="iconfont icon-arrow-down"></i>
  2. 需要在webpack处理ttf、woff2?结尾的文件,不然浏览器识别不出来。[{test: /\.(ttf|woff2?|map4|map3|avi)$/, type: "asset/resource", generator: {filename: "static/media/[hash:8][ext][query]",}}]
  3. type: "asset/resource":相当于file-loader, 将文件转化成 Webpack 能识别的资源,其他不做处理
开发服务器&自动化
  1. 安装 npm i webpack-dev-server -D
  2. 开发服务器配置 devServer: { host: "localhost", port: 3000, open: true}
  3. 含义:启动服务器域名、服务器端口号、是否自动打开浏览器,当使用开发服务器时,所有代码都会在内存中编译打包,并不会输出到 dist 目录下。
  4. 运行的指令为npx webpack serve
处理html资源
  1. 下载html-webpack-plugin
  2. 作用: dist 目录输出一个 index.html 文件会自动引入打包生成的js等资源
  3. 使用:plugins: [new HtmlWebpackPlugin({template: path.resolve(__dirname, "public/index.html"),})],以public/index.html为模板创建文件。
  4. 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
处理js资源
  1. Webpack对js处理是有限的,只能编译js中ES 模块化语法不能编译其他语法,导致js不能在IE等浏览器运行,所以需要做一些兼容性处理,先完成Eslint检测代码格式无误后,再由Babel做代码兼容性处理。
  2. Eslint:它是用来检测 js 和 jsx 语法的工具,可以配置各项功能;需要新建配置文件.eslintrc,里面有各种rules,运行Eslint时就会以写的规则对代码进行检查。
  3. 使用:安装eslint-webpack-plugin,webpack配置:plugins: [new ESLintWebpackPlugin({ context: path.resolve(__dirname, "src")}) //指定检查文件的根目录
  4. Babel:主要将es6语法转为向后兼容的js语法,可以让它们在旧版浏览器也能用,需要新建.babelrc.
  5. Babel使用:下载插件:babel-loader @babel/core @babel/preset-env,webpack配置 [{test: /.js$/, exclude: /node_modules/, loader: "babel-loader",}]
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
 module: {
    rules: [
      {
        test: /\.(jsx|js)$/,
        include: path.resolve(__dirname, "../src"),
        loader: "babel-loader",
        options: {
          cacheDirectory: true,
          cacheCompression: false,
          plugins: [
            "@babel/plugin-transform-runtime"
          ],
        },
      }]
 },
 plugins: [
    new ESLintWebpackPlugin({
      context: path.resolve(__dirname, "../src"),
      exclude: "node_modules",
    }),
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "../public/index.html"),
    })
],

提升打包构建速度-HMR/Cache/Thread

Cache
  1. Cache是什么?每次打包文件都要经过Eslint Bebal处理,速度比较慢;可以缓存之前的Eslint检查的结果和Babel编译的结果,这样第二次打包速度就比较快。
  2. babel: 开启babel编译缓存{test: /.js$/, loader: "babel-loader", option:{cacheDirectory: true, }}
  3. Eslint: cache开启缓存,cacheLocation缓存目录。new ESLintWebpackPlugin({ context: path.resolve(__dirname, "../src"), exclude: "node_modules", cache: true, cacheLocation: path.resolve( __dirname,"../node_modules/.cache/.eslintcache")})
Include/Exclude
  1. 在开发时候使用的第三方的库或插件,所有文件都下载到 node_modules 中了。而这些文件是不需要编译可以直接使用的。所以我们在对 js 文件处理时,要排除 node_modules 下面的文件。
  2. include(包含):include: path.resolve(__dirname, "../src")
  3. exclude(排除):exclude: "node_modules"
Thread
  1. 当项目越来越庞大时,打包速度越来越慢,提升打包速度,其实就是要提升 js 的打包速度,因为其他文件都比较少,而对 js 文件处理主要就是eslint 、babel、Terser三个工具,主要是提升这三个的速度。开启多进程同时处理 js 文件。
  2. 多进程打包是开启多个进程同时干一件事,速度更快。仅在特别耗时的操作中使用,因为每个进程启动就有大约为 600ms 左右开销。
  3. 下载thread-loader, 获取CPU的核数,启动进程的数量就是CPU的核数:const os = require("os"); const threads = os.cpus().length;
  4. loader开启多进程: {test: /\.js$/,use: [{loader: "thread-loader",options: {workers: threads}}, {loader: "babel-loader", options: {cacheDirectory: true}}]}
  5. eslint开启多进程: new ESLintWebpackPlugin({cache: true, cacheLocation:...,threads})
  6. Terser开启多进程: optimization: {minimize: true, minimizer: [new CssMinimizerPlugin(), new TerserPlugin({parallel: threads})]},生产模式会默认开启TerserPlugin,但是要进行其他配置,就要重新写了
module: {
    // 开启babel编译缓存
    rules: [
      {
        test: /\.(jsx|js)$/,
        include: path.resolve(__dirname, "../src"),
        loader: "babel-loader",
        options: {
          cacheDirectory: true,
          cacheCompression: false,
          plugins: [
             "@babel/plugin-transform-runtime" 
          ],
        },
      }]
},
plugins: [
    // ESLint开启多线程以及缓存
    new ESLintWebpackPlugin({
      context: path.resolve(__dirname, "../src"),
      exclude: "node_modules",
      cache: true,
      threads,
      cacheLocation: path.resolve(
        __dirname,
        "../node_modules/.cache/.eslintcache"
      ),
    }),
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "../public/index.html"),
    }),
    new MiniCssExtractPlugin({
      filename: "static/css/[name].[contenthash:10].css",
      chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
    }),
],
optimization: {
    // 压缩的操作开启多进程
    minimizer: [
      new CssMinimizerPlugin(),
      new TerserWebpackPlugin({parallel: threads}),
    ]
}
HotModuleReplacement
  1. HMR是什么?在开发过程中修改某一块代码,会默认把所有的模块重新打包,速度很慢。HMR可以在修改某个模块的代码时候,只打包编译某个模块,这样速度就会很快。
  2. 热模块替换就是在程序运行中,添加、删除或替换模块,无需重新加载整个页面
  3. 怎么用:在devserver里面设置hot:true就是开启了HMR功能,这个时候css经过style-loader处理就可以做到修改后不加载整个页面了。js哪个文件需要热更新需要额外写上module.hot.accept("./js/count.js")
  4. 实际开发我们会使用其他loader来解决,比如vue-loader react-hot-loader

减少代码体积-Babel/图片压缩/code split

Image Minimizer
  1. 项目如果中引用了较多图片,那么图片体积会比较大,将来请求速度比较慢。所以可以对图片进行压缩,减少图片体积
  2. 下载npm i image-minimizer-webpack-plugin imagemin -D;以及压缩各种格式图片的插件
  3. 无损压缩npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D
  4. 有损压缩npm install imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D
  5. 配置 optimization: {minimize: true, minimizer: [new CssMinimizerPlugin(), new ImageMinimizerPlugin({minimizer: {...配置压缩各种格式图片的插件}}) ]}
Code Split
  1. 代码分割是什么?打包代码时会将所有 js 文件打包到一个文件中,体积太大了。所以我们需要将打包生成的文件进行代码分割,生成多个 js 文件,渲染哪个页面就只加载某个 js 文件,这样每次加载的资源就少,速度就更快。
  2. 种类:多入口打包;提取插件代码或重复代码到公共模块;使用import按需加载,动态导入
  3. 多入口打包:在entry配置了几个入口,输出几个 js 文件。入口文件:entry: {main: "./src/main.js",app: "./src/app.js"} ;出口文件:output: {path: path.resolve(__dirname, "./dist"), filename: "js/[name].js",}
  4. 提取插件代码或重复代码到公共模块:optimization: {splitChunks: {chunks: "all", cacheGroups: { default: {minSize: 0, minChunks: 2,priority: -20,}}}},cacheGroups 修改配置组,配置哪些模块要打包到一个组。
  5. 使用import按需加载,动态导入:import(/* webpackChunkName: "math" */ "./js/math.js").then(({ count }) => {...})
Core-js
  1. 使用babel对js进行兼容性处理,使用@babel/preset-env智能预设处理兼容性问题,它可以对ES6的扩展运算符、箭头函数进行处理,但是版本更高的promise对象、async函数没办法处理。
  2. core-js是专门来做es6及以上的api polyfill,叫做垫片/补丁。就是用社区上提供的一段代码,可以在不兼容某些新特性的浏览器上,使用该新特性。
  3. 如何使用? 在babel.config.js文件,直接 import "@babel-polyfill"会过大,只使用一部分功能可以按需引用。
module.exports = {
  // 智能预设:能够编译ES6语法
  presets: [
    [
      "@babel/preset-env",
      // 按需加载core-js的polyfill
      // { useBuiltIns: "usage", corejs: { version: "3", proposals: true } },
      { useBuiltIns: "usage", corejs: 3 },
    ],
  ],
};
Tree Shaking
  1. Webpack开发模式已经默认开启了这个功能,无需其他配置。
  2. 开发时引用第三方工具函数库或组件库,如果没有特殊处理的话我们打包时会引入整个库,但是实际上可能我们可能只用上极小部分的功能;
  3. Tree Shaking移除 JavaScript 中的没有使用上的代码。它是基于es6模块静态编译思想,在编译时候确定模块的依赖关系、输入输出的变量。在编译阶段利用ES Module判断哪些模块已经加载,哪些未被使用进而删除对应的代码。

链接

尚硅谷Webpack5入门到原理