webpack5之性能优化

1,033 阅读5分钟

这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战

(一) webpack5之初体验

(二) webpack5之核心配置

(三) webpack5之生产配置

1,HTML代码压缩

webpack对开发和生产环境的配置是不一样的,默认情况下,生产环境的代码是压缩的,无需重新配置,只需要指定, mode = "production" , 但是,如果想要压缩HTML代码,需要添加配置

new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        // 移除空格
        collapseWhitespace: true,
        // 移除注释
        removeComments: true
      }
    }),

2,生产环境配置汇总

主要包括:

  • css,less 等文件的兼容性处理,已经代码压缩
  • 对html的处理和压缩
  • 对图片和其他资源的处理
  • 对eslint语法的配置
const { resolve } = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
process.env.NODE_ENV = 'production'
// 复用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    loader: 'postcss-loader',
    options: {
     postcssOptions: {
       plugins: [
         require('postcss-preset-env')()
       ]
     }
   }
  }
]
module.exports = {
   entry: './src/js/index.js',
   output: {
     filename: 'js/bundle.js',
     path: resolve(__dirname, 'build')
   },
   module: {
     rules: [
       {
         test: /\.css$/,
         use: [...commonCssLoader]
       },
       {
        test: /\.less$/,
        use: [...commonCssLoader, 'less-loader']
       },
       {
         test: /\.js$/,
         exclude: /node_modules/,
         enforce: 'pre',
         loader: 'eslint-loader',
         options: {
           fix: true
         }
       },
       {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: [
            '@babel/preset-env',
            {
              useBuiltIns: 'usage',
              corejs: { version: 3 },
              targets: {
                chrome: '60',
                firefox: '50'
              }
            }
          ]
        }
       },
       {
         test: /\.(jpg|png|gif)/,
         loader: 'url-loader',
         options: {
           limit: 8 * 1024,
           name: '[hash:10].[ext]',
           outputPath: 'imgs',
           esModule: false
         }
       },
       {
           test: /\.html$/,
           laoder: 'html-loader'
       }, 
       {
         exclude: /\.(js|css|less|html|jpg|png|gif)/,
         loader: 'file-loader',
         options: {
           outputPath: 'media'
         }
       }
     ]
   },
   plugins: [
      new MiniCssExtractPlugin({
        filename: 'css/bundle.css'
      }),
      new OptimizeCssAssetsWebpackPlugin(),
      new HtmlWebpackPlugin({
        template: './src/index.html',
        minify: {
          collapseWhitespace: true,
          removeComments: true
        }
      })
   ],
   mode: 'production'
};

3,webpack性能优化

1,开发环境性能优化

2,生产环境性能优化

(1) 开发环境性能优化的几个方面:

  • 优化打包构建速度

       -  优化代码调试
    

(2) 生产环境性能优化

  • 优化打包构建速度
  • 优化代码运行的性能

4,热模块替换

​ 作用: 一个模块发生变化,只会重新打包这一个模块,而不是打包所有模块,极大提升构建速度,要开启HMR功能也是很简单的,只需要在devServer中配置hot: true

   devServer: {
     // 运行项目目录
     contentBase: resolve(__dirname, 'build'),
     // 启动压缩
     compress: true,
     // 端口号
     port: 3000,
     // 自动打开浏览器
     open: true,
     // 开启HMR功能
     hot : true,
   }

注意:

1,样式文件:可以使用HMR功能,因为style-laoder内部实现了

2,js文件,默认不能使用HMR功能, 需要修改JS代码,让其支持HMR功能, HMR功能对JS的处理,只能处理非入口JS文件的其他文件。

3, HTML文件:默认不能使用HMR功能,同时会导致问题,html文件不能热更新了

​ 解决方案: 修改entry入口,将html文件引入

module.exports = {
   entry: ['./src/index.js', './src/index.html'],
   output: {
     filename: 'js/bundle.js',
     path: resolve(__dirname, 'build')
   },
  }

当然也可以单独配置某一块

if(module.hot) {
// 一旦module.hot为true,说明开启了HMR功能, 
   module.hot.accept('./uitls.js', function() {
     // 会监听uitls.js文件的变化,一旦发生变化,其他默认不会重新打包构建
    print()
   })
}

4, webpack的热更新原理?

webpack的热更新又称热替换(Hot Module Replacement),缩写为HMR。

这个机制可以做到不用刷新浏览器而 将新变更的模块替换掉旧的模块 原理:

1,第一步:在 webpack 的 watch 模式下,文件系统中某一个文件发生修改,webpack 监听到文件变化,根据 配置文件对模块重新编译打包,并将打包后的代码通过简单的 JavaScript 对象保存在内存中

2,第二步: webpack-dev-server 和 webpack 之间的接口交互,而在这一步,主要是 dev-server 的中间 件 webpack-dev-middleware 和 webpack 之间的交互,webpack-dev-middleware 调用 webpack 暴露 的 API对代码变化进行监控,并且告诉 webpack,将代码打包到内存中。

3,第三步: webpack-dev-server 对文件变化的一个监控,这一步不同于第一步,并不是监控代码变化重新打 包。当我们在配置文件中配置了devServer.watchContentBase 为 true 的时候,Server 会监听这些配置文 件夹中静态文件的变化,变化后会通知浏览器端对应用进行 live reload。注意,这儿是浏览器刷新,和 HMR 是两个概念。

4,第四步: 该步骤主要是通过在浏览器端和服务端之间建立一个 websocket 长连接,将 webpack 编译打包的各个阶段的状态信息告知浏 览器端,同时也包括第三步中 Server 监听静态文件变化的信息。浏览器端根据这些 socket 消息进行不同的操 作。当然服务端传递的最主要信息还是新模块的 hash 值,后面的步骤根据这一 hash 值来进行模块热替换。

5,第五步:webpack-dev-server/client 端并不能够请求更新的代码,也不会执行热更模块操作,而把这些工作又交回 给了 webpack,webpack/hot/dev-server 的工作就是根据 webpack-dev-server/client 传给它的信息 以及 dev-server 的配置决定是刷新浏览器呢还是进行模块热更新。当然如果仅仅是刷新浏览器,也就没有后面那些步

6,第六步:HotModuleReplacement.runtime 是客户端 HMR 的中枢,它接收到上一步传递给他的新模块的 hash 值,它通过 JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求,服务端返回一个 json,该 json 包 含了所有要更新的模块的 hash 值,获取到更新列表后,该模块再次通过 jsonp 请求,获取到最新的模块代码

7,第七步:在该步骤中,HotModulePlugin 将会对新旧模块进行对比,决 定是否更新模块,在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用

8,第八步:当 HMR 失败后,回退到 live reload 操作,也就是进行浏览器刷新来获取最新打包代码。

5,webpack解决性能问题

总结一下上面的生产配置:

1 ,压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPlugin和 ParallelUglifyPlugin来压缩JS文件, 利用cssnano(css-loader?minimize)来压缩css

2,利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output 参数和各loader的publicPath参数来修改资源路径

3,删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数-- optimize-minimize来实现

4,提取公共代码。

6,源码下载

webpack5学习源码