记录webpack优化

479 阅读4分钟

主要分为优化构建速度优化产出代码两个方面说

优化构建速度

  • 优化构建速度
    • babel-loader 开启缓存
    • IgnorePlugin 忽略无用模块
    • noParse 避免重复打包
    • happypack 多进程打包
    • ParallelUglifyPlugin 多进程压缩js
    • 自动刷新
    • 热更新
    • DllPlugin 动态链接库插件
  • 可用于生产环境的
    • babel-loader
    • IgnorePlugin
    • noParse
    • happypack
    • ParallelUglifyPlugin

babel-loader 开启缓存

{
    test: /\.js$/,
    loader: ['babel-loader?cacheDirectory'], // 开启缓存
    include: srcPath, // 选择范围
    // exclude: /node_modules/  // 排除
},

IgnorePlugin 忽略无用模块

为什么要使用

  • moment会支持多语言,有很多不需要的语言包被引用了
  • 使用IgnorePlugin忽略引用

使用

new webpack.IgnorePlugin(/\.\/locale/, /moment/)

noParse 避免重复打包

如 react.min.js 是已经打包过的文件,是不需要再打包的

module: {
  noParse: [/react\.min\.js$/]
}

happypack 多进程打包

  • js单线程,开启多进程打包
  • 提高构建速度
// 对 babel 开启多进程
// 步骤1 module.rules下-将对.js的文件交给id为label的HappyPack实例
{
    test: /\.js$/,
    use: ['happypack/loader?id=babel'],
},

// 步骤2 happyPack开启多进程打包
new HappyPack({
    // 用唯一的标识符 id 来代表当前的 HappyPack 是用来处理一类特定的文件
    id: 'babel',
    // 如何处理 .js 文件,用法和 Loader 配置中一样
    loaders: ['babel-loader?cacheDirectory']
}),

ParallelUglifyPlugin 多进程压缩js

  • webpack内置Uglify工具压缩js
  • js单线程,开启多进程压缩更快
// 使用 ParallelUglifyPlugin 并行压缩输出的 JS 代码
new ParallelUglifyPlugin({
    // 传递给 UglifyJS 的参数
    // (还是使用 UglifyJS 压缩,只不过帮助开启了多进程)
    uglifyJS: {
        output: {
            beautify: false, // 最紧凑的输出
            comments: false, // 删除所有的注释
        },
        compress: {
            // 删除所有的 `console` 语句,可以兼容ie浏览器
            drop_console: true,
            // 内嵌定义了但是只用到一次的变量
            collapse_vars: true,
            // 提取出出现多次但是没有定义成变量去引用的静态值
            reduce_vars: true,
        }
    }
})

自动刷新 (知道就行。 webpack-dev-server 有这功能)

module.export = {
  // 开启监听
  watch: true, // 默认 false
  // 注意:开启监听之后,webpack-dev-server 会自动开启刷新浏览器

  // 监听配置
  watchOptions: {
    ignored: /node_mdules/, // 忽略哪些
    // 监听到变化发生后会等300ms再去执行动作,防止文件更新太快导致重新编译频率太高
    aggregateTimeout: 300, // 默认 300ms
    // 判断文件是否发生变化是通过不停的去询问系统指定文件有没有变化实现的
    poll: 1000 // 默认每间隔1000毫秒询问一次
  }
}

热更新

自动刷新与热更新的区别

  • 自动刷新
    • 整个网页都刷新,速度较慢
    • 状态会丢失(表单内数据会丢失)
  • 热更新
    • 部分刷新
    • 状态不丢失
// HMR 作为一个 Webpack 内置的功能,可以通过 --hot 或者 HotModuleReplacementPlugin 开启。

// 配置 在entry中的index中
 index: [
    'webpack-dev-server/client?http://localhost:8080/',
     'webpack/hot/dev-server',
      path.join(srcPath, 'index.js')
 ]
 // 配置2
在plugins中new HotModuleReplacementPlugin()

// 配置3
在devServer加hot: true

// 配置4
业务中加module.hot来确定哪些范围需要触发热更新

DllPlugin 动态链接库插件

解决了什么问题

  • 前端框架如 vue、react,体积大,构建慢

  • 较稳定,不常升级版本

  • 同一个版本只构建一次即可

  • webpack已经内置DllPlugin支持

  • DllPlugin-打包出dll文件

  • DllReferencePlugin-使用dll文件

const path = require('path')
const DllPlugin = require('webpack/lib/DllPlugin')
const { srcPath, distPath } = require('./paths')

module.exports = {
  mode: 'development',
  // JS 执行入口文件
  entry: {
    // 把 React 相关模块的放到一个单独的动态链接库
    react: ['react', 'react-dom']
  },
  output: {
    // 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称,
    // 也就是 entry 中配置的 react 和 polyfill
    filename: '[name].dll.js',
    // 输出的文件都放到 dist 目录下
    path: distPath,
    // 存放动态链接库的全局变量名称,例如对应 react 来说就是 _dll_react
    // 之所以在前面加上 _dll_ 是为了防止全局变量冲突
    library: '_dll_[name]',
  },
  plugins: [
    // 接入 DllPlugin
    new DllPlugin({
      // 动态链接库的全局变量名称,需要和 output.library 中保持一致
      // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
      // 例如 react.manifest.json 中就有 "name": "_dll_react"
      name: '_dll_[name]',
      // 描述动态链接库的 manifest.json 文件输出时的文件名称
      path: path.join(distPath, '[name].manifest.json'),
    }),
  ],
}

优化产出代码

目标

  • 打包出的体积更小
  • 合理分包,不重复加载
  • 速度更快,内存使用更少

实践

  • 小图片base64编码
    • (对图片使url-loader配置options.limit)
  • bundle加hash值
    • ([name].[contentHash:8].js)
  • 懒加载
    • ()=>import('')
  • 提取公共代码
    • splitChunks:公共代码和三方代码
  • IgnorePlugin
    • 忽略文件不引入打包
  • 用cdn加速
    • 在output中加publicPath在url-loader中的options中也加入publicPath
    • 上传使用cdn的文件
  • 使用production
  • Scope Hosting
    • 改变作用域

使用production

  • 自动开启代码压缩
  • vue、react会自动删除调试代码
  • 启动 tree-shaking,删除无用代码
    • ES6 module才能生效
    • commonjs不能
    • 原因:
      • ES6 module 是静态引入,编译时引入
      • commonjs 是动态引入,执行时引入

Scope Hosting

一个js文件打包成一个函数,文件多了打包后的函数也会多。使用 Scope Hosting 合并、减少打包后的函数

  • 代码体积更小
  • 创建函数作用域更少