webpack基础(二) | 青训营笔记

117 阅读6分钟

这是我参与「第四届青训营 」笔记创作活动的的第8天

webpack 中为我们提供了很多很好用的工具,这些工具能带给我们更好的开发体验和更高代码质量,webpack 的工具非常之多,下面的内容也仅仅只是冰山一角。

HMR

MHR:hot module replacement 热模板替换 / 热模板替换

开发的时候,当我们修改了其中一个模块代码,webpack 默认会将所有模块全部重新打包编译,速度很慢。

HMR 能够在程序运行中,当一个模块发生变化,能智慧重新打包这一个模块(而不是打包所有模块),能够极大的提升构建速度

  devServer:{
    host:'localhost',
    port:3000,
    open:true,
    //添加这一行代码即可。
    hot: true,
}

此时 css 文件可以使用HMR功能:因为style-loader内部实现了。

但是,js 文件和 html 文件没有使用 HMR

js HMR实现

//在index.js入口js文件中最后添加下面的代码
if (module.hot) {
  // 之后有了其他的 js 文件,往后面加就可以了
  module.hot.accept("./index.js");
  module.hot.accept("./add.js");
}

那么,如何实现 html 的热重载呢?

SourceMap

当我们使用 webpack 打包代码后,所有的 css文件和 js文件合并成了一个文件,并且多了很多的代码。此时代码提示的错误位置是打包之后的文件,一旦代码量的增大,我们很难定位到源码的错误位置,这时,我们就需要 SourceMap 为我们提供更加准确的代码提示。

SourceMap 是一个用来生成源代码与构建后代码一一映射的文件方案

它会生成一个 xxx.map 文件,里面包含源代码和构建后代码的每一行,每一列的映射关系,当构建后代码出错了,会通过 xxx.map 文件,从构建后代码出错位置找到映射后源代码出错的位置,从而让浏览器提示源代码出错位置,帮助我们更快的找到出错根源。

使用

SourceMap 的种类有很多,详情请见官方文档。webpack.js.org/configurati…

SourceMap.png

实际开发中,我们常用两种形式即可:

  • 开发模式:cheap-module-source-map

    • 优点:打包编译速度快,值包含行映射
    • 缺点:没有列映射
    module.exports = {
        mode:'development',
        devtool:'cheap-module-source-map'
    }
    
  • 生产模式:source-map

    • 优点:包含行/列映射
    • 缺点:打包编译速度更慢
    module.exports = {
        mode:'development',
        devtool:'source-map'
    }
    

oneOf

在rules中,一种文件,往往只需要匹配一个loader,但是,浏览器会将该文件经过所有loader

使用oneOf就可以解决这样的问题

module: {
    rules: [
      {
        oneOf: [
          {
            test: /.css$/,
            use: [
              MiniCssExtractPlugin.loader,
              "css-loader",
              {
                loader: "postcss-loader",
                options: {
                  postcssOptions: {
                    plugins: [["postcss-preset-env"]],
                  },
                },
              },
            ],
          },
          {
            test: /.less$/,
            use: [
              MiniCssExtractPlugin.loader,
              "css-loader",
              {
                loader: "postcss-loader",
                options: {
                  postcssOptions: {
                    plugins: [["postcss-preset-env"]],
                  },
                },
              },
              "less-loader",
            ],
          },
          {
            test: /.s[ac]ss$/i,
            use: [
              MiniCssExtractPlugin.loader,
              "css-loader",
              {
                loader: "postcss-loader",
                options: {
                  postcssOptions: {
                    plugins: [["postcss-preset-env"]],
                  },
                },
              },
              "sass-loader",
            ],
          },
          {
            test: /.(png|jpe?g|gif|webp|svg)$/,
            type: "asset",
            parser: {
              dataUrlCondition: {
                maxSize: 10 * 1024,
              },
            },
            generator: {
              filename: "image/[hash][ext][query]",
            },
          },
          {
            test: /.js$/,
            exclude: /node_modules/,
            use: {
              loader: "babel-loader",
              options: {
                presets: ["@babel/preset-env"],
              },
            },
          },
        ],
      },
    ],
  },

Cache缓存

每一次打包时 js 文件都要经过 Eslint 检查 和 Babel 编译,速度比较慢。

我们可以缓存之前的 Eslint 检查 和 Babel 编译结果,这样第二次打包的速度就会更快了

下面以缓存 Babel 为例

{
    test: /.js$/,
    exclude: /node_modules/,
    use: {
        loader: "babel-loader",
        options: {
            presets: ["@babel/preset-env"],
                cacheDirectory:true,
                    cacheCompression:false,
        },
    },
},

Tree-Shaking树摇(去除无用代码)

开发时我们定义了一些工具函数库,或者应用了第三方工具库或组件库。

如果没有特殊处理的话,我们打包时会引入整个库,但是实际上可能我们只用上了其中极小部分的功能。

这样将整个库都打包进来,体积就太大了。这时,Tree-Shaking 就会帮我们去除不需要的代码。

前提:1.开启production环境 2.在引入时只能用import而不能用require

压缩图片

开发项目中引用了较多图片,那么图片体积会比较大,将来请求的速度比较慢。

我们可以对图片进行压缩,减少图片的体积。

注意:如果项目中的图片时在线连接,那么就不需要了。本地项目静态图片才需要进行压缩。

安装

npm install image-minimizer-webpack-plugin imagemin --save-dev

无损压缩

npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo --save-dev

有损压缩

npm install imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo --save-dev

如果下载不下来,可以使用 cnmp 试一下

使用

const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const { extendDefaultPlugins } = require("svgo");
​
module.exports = {
  module: {
    rules: [
      {
        test: /.(jpe?g|png|gif|svg)$/i,
        type: "asset",
      },
    ],
  },
  optimization: {
    minimizer: [
      "...",
      new ImageMinimizerPlugin({
        minimizer: {
          implementation: ImageMinimizerPlugin.imageminMinify,
          options: {
            // Lossless optimization with custom option
            // Feel free to experiment with options for better result for you
            plugins: [
              ["gifsicle", { interlaced: true }],
              ["jpegtran", { progressive: true }],
              ["optipng", { optimizationLevel: 5 }],
              // Svgo configuration here https://github.com/svg/svgo#configuration
              [
                "svgo",
                {
                  plugins: extendDefaultPlugins([
                    {
                      name: "removeViewBox",
                      active: false,
                    },
                    {
                      name: "addAttributesToSVGElement",
                      params: {
                        attributes: [{ xmlns: "http://www.w3.org/2000/svg" }],
                      },
                    },
                  ]),
                },
              ],
            ],
          },
        },
      }),
    ],
  },
};

code split代码分离

多入口起点配置

  entry:{
    main:'./src/main.js',
    app:'./src/app.html'
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name].js",
  },

多入口提取公共模块

如果上面的 main.jsapp.js 都引入并且使用了 math.js 中的 某些函数(比如一个最简单的sum函数),那么,打包的时候,这些函数会被打包两份,如果代码量庞大且引用过多的话,会极大的降低打包的速度。

这时候我们就需要配置提取这些公共模块的内容

配置

  optimization: {
    splitChunks: {
      chunks: "all", // 对所有模块都进行分隔
      // 以下是默认值
      //   minSize: 20000,// 分割代码最小的大小
      //   minRemainingSize: 0,// 类似于 minSize ,最后确保提取的文件大小不能为0
      //   minChunks: 1,// 至少被应用的次数,瞒住条件待会代码分割
      //   maxAsyncRequests: 30,// 按需加载时并行加载的文件的最大数量
      //   maxInitialRequests: 30,// 入口js文件最大并行请求数量
      //   enforceSizeThreshold: 50000,// 超过50kb一定会单独打包(此时会忽略minRemainingSize,maxAsyncRequest,maxInitialRequests)
      //   cacheGroups: {// 组,哪些模块要打包到一个组
      //     defaultVendors: {//组名
      //       test: /[\/]node_modules[\/]/,// 需要打包到一起的模块
      //       priority: -10,// 权重(越大越高)
      //       reuseExistingChunk: true,// 如果当前 chunk 包含已从 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块
      //     },
      //     default: {// 其他没有写的配置会使用上面的默认值
      //       minChunks: 2,// 这里的minChunks权重更大
      //       priority: -20,
      //       reuseExistingChunk: true,
      //     },
      //   },
​
      // 修改配置
      cacheGroups: {
        default: {
          minSize: 0,
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },

公共模块.png

多入口按需加载

如果有一个功能,用户很可能用不到,那我们可以采取按需加载,当用户触发某个事件的时候,在获取特定功能的文件。

比如,我创建了一个 count.js ,里面有一些函数,当我们点击页面上面的某个按钮的时候,就加载这个js文件,反之,不加载。

document.getElementById("btn").onclick = function(){
    // import 动态导入,会将动态导入的文件代码分割(拆分成为单独模块),在需要使用的时候自动加载
    import('./count').then((res)=>{
        console,log('模块加载成功',res);
    }).catch((err)=>{
        console.log('模块加载失败',err)
    })
}

在未点击按钮的时候

按需加载1.png

点击按钮后(新增了一个293.js)

按需加载2.png

单入口(SPA)

如果是单入口文件,那么我们只需要:

  optimization: {
    splitChunks: {
      chunks: "all", // 对所有模块都进行分隔
    }
  },

给模块命名

在 webpack.config.js 文件中 新增 chunkFilename 节点

output: {
    path: path.resolve(__dirname, "dist"),
    filename: "js/[name].js",
    chunkFilename:'js/[name].chunk.js',
    clean: true,
  },

在引入的时候

document.getElementById("btn").onclick = function(){
  // import 动态导入,会将动态导入的文件代码分割(拆分成为单独模块),在需要使用的时候自动加载
  import(/* webpackChunkName:"count" */ './count').then((res)=>{
      console,log('模块加载成功',res);
  }).catch((err)=>{
      console.log('模块加载失败',err)
  })
}

PWA渐进式网络开发应用程序(离线可访问)

PWA 主要目的是 在离线的状态下能够继续运行功能

安装

npm install workbox-webpack-plugin --save-dev

使用

const WorkboxPlugin = require('workbox-webpack-plugin');
plugins: [
    new HtmlWebpackPlugin({
        title: 'Output Management',
        title: 'Progressive Web Application',
    }),
    new WorkboxPlugin.GenerateSW({
        clientsClaim: true,
        skipWaiting: true,
    }),
],

在index.js入口文件中注册servicworker

 if ('serviceWorker' in navigator) {
   window.addEventListener('load', () => {
     navigator.serviceWorker.register('/service-worker.js').then(registration => {
       console.log('SW registered: ', registration);
     }).catch(registrationError => {
       console.log('SW registration failed: ', registrationError);
     });
   });
 }

代码必须运行在服务器上

可以使用nodejs,也可以使用其他的,这里以serve包为例

//安装
npm i serve -g
//运行
serve dist

结语

文章如果有不正确的地方,欢迎指正,共同学习,共同进步。

若有侵权,请联系作者删除。