前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >重学webpack4之构建速度提升和体积优化

重学webpack4之构建速度提升和体积优化

作者头像
疯狂的技术宅
发布2020-12-15 11:04:01
1.1K0
发布2020-12-15 11:04:01
举报
文章被收录于专栏:京程一灯
代码语言:javascript
复制

❝本文由作者 xfz 授权发布 ❞

构建速度优化

  • 速度分析:speed-measure-webpack-plugin
  • 可以查看每个loder和插件的执行耗时
  • 红色字体表示时间过长,黄色还可以,绿色是ok的
代码语言:javascript
复制
module.exports = smg.wrap(webpackConfig)

wepback4 vs wepback3

  • webpack使用V8, for of 替代 forEach、Map和set代替Object、includes替代indexOf等
  • 默认使用更快的 md4 hash 算法
  • webpack AST 可以直接从 loader 传递给 AST,减少解析时间
  • 使用字符串方案替代正则表达式,正则表达式运算较慢

多进程/多实例构建

thread-loader(推荐:wepback4使用)

  • 每次wepback解析一个模块,thread-loader会将它及它的依赖分配给worker线程中
代码语言:javascript
复制
module: {
  rules: [
    {
      test: /.js$/,
      // include: path.resolve('src'),
      use: [
        {
          loader: 'thread-loader',
          options: {
              workers: 3 // cpu核数*2 - 1
          }
        },
        'babel-loader',
      ]
    }
  ]
},

happypack(wepback3使用,已经不在维护)

代码语言:javascript
复制
// 构造出共享进程池,进程池中包含4个子进程
const happyThreadPool = HappyPack.ThreadPool({ size: 4 });
module: {
    rules: [
      {
        test: /\.js$/,
        // 把对 .js 文件的处理转交给 id 为 babel 的 HappyPack 实例
        use: ['happypack/loader?id=happybabel'],
        exclude: /node_modules/,
      },
      {
        // 把对 .css 文件的处理转交给 id 为 css 的 HappyPack 实例
        test: /\.css$/,
        use: ['happypack/loader?id=happycss']
      },
    ]
},
plugins: [
    new HappyPack({
      id:"happybabel",
      loaders:['babel-loader?cacheDirectory'],
      threadPool:happyThreadPool,
      cache:true,
      verbose:true
    }),
    new HappyPack({
      id: 'happycss',
      // loaders: ['style-loader','css-loader','postcss-loader'],
      loaders: [
        {loader: 'style-loader'},
        {loader: 'css-loader',options:{minimize: false, importLoaders:1}},
        {loader: 'postcss-loader',options:{config: {path: path.resolve(__dirname, '../postcss.config.js')} }}
      ],
      threadPool: happyThreadPool,
    }),
]

parallel-webpack

  • 没用过...

多进程/多实例:并行压缩

方式一:terser-webpack-plugin 开启 parallel参数(推荐wepback4使用)

  • 支持ES6压缩
代码语言:javascript
复制
module.exports = {
  optimization: {
    minimizer: [
      new TerserPlugin({
        parallel: true, // 多线程
        cache: true // 开缓存
      })
    ]
  },
}

方式二:uglifyjs-wepback-plugin

  • 不支持ES6压缩
代码语言:javascript
复制
plugins: [
  new UglifyJsPlugin({
    uglifyOptions: {
      warning: false,
      parse: {},
      compress: {},
      mangle: true,
      outpu: null,
      // ...
    },
    parallel: true
  })
]

方式三:parallel-uglify-plugin

  • 不支持ES6压缩
代码语言:javascript
复制
module.exports = {
  plugins: [
    new ParallelUglifyPlugin({
      uglifyJS: {
        output: {
          beautify: false,
          comments: false
        },
        compress: {
          warnings: false,
          drop_console: ture,
          collapse_vars: true,
          reduce_vars: true
        }
      }
    })
  ]
}

缓存:提升二次构建速度(比较适合开发环境或静态打包服务器)

下面的几种方式同时使用,效果杠杠的

  • babel-loader 开启缓存('babel-loader?cacheDirectory=true')
  • TerserPlugin开启缓存(cache: true)
代码语言:javascript
复制
{
  test: /\.(js|jsx)$/,
  exclude: '/(node_modules)/',
  use: [
    {
      loader: 'thread-loader',
      options: {
          workers: 3 // cpu核数*2 - 1
      }
    },
    // here this code
    'babel-loader?cacheDirectory=true'
  ] 
}

  • 使用cache-loader或 hard-source-webpack-plugin(强烈推荐,谁用谁知道
  • 12s -> 7s 多
代码语言:javascript
复制
plugins: [
  new HardSourceWebpackPlugin()
]

减少文件搜索范围

  • 优化 resolve.modules配置(减少模块搜索层级)
  • 优化 resolve.mainFields 配置
  • 优化 resolve.extensions 配置
  • 合理使用 alias
代码语言:javascript
复制
// webpack
resolve: {
  modules: [path.resolve(__dirname, '../node_modules')],// 限定在本项目目录上,不会再向上至全局查找
  mainFields: ['main'], // 读取package.json的main字段,入口文件
  extensions: ['.js', '.jsx'], // 省略后缀
  alias: {
    'react': path.resolve(__dirname, '../node_modules/react/umd/react.production.min.js'),
    'react-dom': path.resolve(__dirname, '../node_modules/react-dom/umd/react-dom.production.min.js'),
  }
}

实验结果:优化了0.3秒


打包体积优化

打包分析工具

  • webpack-bundle-analyzer
代码语言:javascript
复制
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
plugins: [
  new BundleAnalyzerPlugin({ port: 3011 }) // 默认是 8888 端口
]

进一步分包:预编译资源模块 dll

  • 将 react、react-dom、redux、react-redux基础包和业务包打包成一个文件
  • 方法:使用DLLPlugin进行分包,DllReferencePlugin对manifest.json引用
  • 这么做的优势:开发和生产环境就可以直接跳过 react等的解析打包,加快热更新及打包速度

1.创建webpack.dev.js

代码语言:javascript
复制
const path = require('path');
const webpack = require('webpack');

module.exports = {
  mode: 'none',
  context: __dirname,
  entry: {
    vendor: [
      'react',
      'react-dom',
      'react-router-dom',
      'mobx-react',
      'mobx',
      'rc-pagination',
      'react-topbar-progress-indicator',
      'simditor'
    ]
  },
  output: {
    filename: '[name]_[hash].dll.js',
    path: path.join(__dirname, '../dist/dll'),
    library: '[name]_[hash]' // 必须与下面的 DllPlugin中的name保持一致
  },
  resolve: {
    extensions: ['.js', 'jsx', '.json'],
  },
  plugins: [
    new webpack.DllPlugin({
      name: '[name]_[hash]',
      path: path.join(__dirname, '../dist/dll', '[name]-manifest.json'),
      context: __dirname
    })
  ]
};

2.生成dll

代码语言:javascript
复制
"scripts": {
  "dll": "rimraf ./dist && webpack --config ./build/webpack.dll.js",
},
npm run dll

3.在webpack.config.js引入

代码语言:javascript
复制
const dllJson = require('../dist/dll/vendor-manifest.json')
plugins: [
  new webpack.DllReferencePlugin({
    manifest: dllJson
  })
]

4.html动态引入xxxx.dll.js

代码语言:javascript
复制
const dllJson = require('../dist/dll/vendor-manifest.json')
new HtmlWebpackPlugin({
  filename: 'index.html',
  template: resolve(__dirname, '../src/index.html'),
  // 线上环境使用CDN,不过个人觉得开发环境用dll,线上环境不要用dll
  vendor: './dll/' + dllJson.name + '.dll.js'
})

// html模版
// 一定要保证,dll.js 必须在其他js之前引用
<body>
  <div id="app"></div>
  <script type="text/javascript" src="<%= htmlWebpackPlugin.options.vendor %>"></script>
</body>

5.执行npm run build,发现那些被拆分的包,没有打进业务代码中,符合预期

缩小构建目标

tree shaking

  • js - tree-shaking,webpack4 mode=production 自动处理 ES6 模块
  • css - purgecss-webpack-plugin插件,遍历代码,识别已经用到的css,它不能独立使用,需要配合 mini-css-extract-plugin 一起使用
代码语言:javascript
复制
plugins: [
  new MiniCssExtractPlugin({
    filename: `${cf.css}/[name].[contenthash:5].css`,
  }),
  // css: tree-shaking
  new PurgecssPlugin({
    paths: glob.sync(`${PATHS.src}/**/*`,  { nodir: true }),
  }),
]

动态Polyfill

  • 基于官方自建polyfill服务:polyfill service
  • 浏览器访问service,根据 UserAgent,下发不同的Polyfill
  • polyfill service 实现按需加载 polyfill
代码语言:javascript
复制
<script src="https://polyfill.io/v3/polyfill.js"></script>

本文代码:https://github.com/xfz1987/webpack-v4-perf
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端先锋 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 构建速度优化
    • wepback4 vs wepback3
      • 多进程/多实例构建
        • thread-loader(推荐:wepback4使用)
        • happypack(wepback3使用,已经不在维护)
        • parallel-webpack
      • 多进程/多实例:并行压缩
        • 方式一:terser-webpack-plugin 开启 parallel参数(推荐wepback4使用)
        • 方式二:uglifyjs-wepback-plugin
        • 方式三:parallel-uglify-plugin
      • 缓存:提升二次构建速度(比较适合开发环境或静态打包服务器)
        • 减少文件搜索范围
        • 打包体积优化
          • 打包分析工具
            • 进一步分包:预编译资源模块 dll
              • 缩小构建目标
                • tree shaking
                • 动态Polyfill
            相关产品与服务
            内容分发网络 CDN
            内容分发网络(Content Delivery Network,CDN)通过将站点内容发布至遍布全球的海量加速节点,使其用户可就近获取所需内容,避免因网络拥堵、跨运营商、跨地域、跨境等因素带来的网络不稳定、访问延迟高等问题,有效提升下载速度、降低响应时间,提供流畅的用户体验。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档