Webpack性能优化「六」-- 优化打包构建速度 ***

本篇讲的是 Webpack 对于优化打包构建速度,也就是对于开发体验和效率的优化。
有如下几处可以优化:

优化 babel-loader
IgnorePlugin 避免引入无用模块
noParse 避免重复打包
happyPack //多进程打包工具
ParalleUglifyPlugin //多进程打包压缩
自动刷新
热更新
DLLPlugin

1.优化 babel-loader

在babel-loader后加cacheDirectory,开启缓存,
没有改动的es6代码会启用缓存,不会重新编译

[webpack.dev.js]:

module: {
  rules: [{
      test: /\.js$/,
      loader: ['babel-loader?cacheDirectory'],
      include: srcPath,
      exclude: /node_modules/
    },
  ]
},

2.IgnorePlugin 避免引入无用模块

使用场景:
日期处理类库moment中,包含很多种语言,默认引入所有语言的JS代码,代码体积过大;只需要使用中文,那就需要配置忽略对整个类库的引入,手动按需引入中文。

当全部引入配置

[index.js]配置

import moment from 'moment' //全部引入
moment.locale('zh-cn') //设置语言为中文
当按需引入配置

① 忽略 moment 引用的所有语言包,忽略 moment 下的 /locale 目录
[webpack.prod.js]配置:

plugins: [
  // 忽略 moment 下的 /locale 目录
  new webpack.IgnorePlugin(/\.\/locale/, /moment/),
]

② 手动动态引入中文语言
[index.js]:

import moment from 'moment' // moment的引入被忽略
import 'moment/locale/zh-cn' //手动引入中文语言包

综上,IgnorePlugin 避免引入无用模块,减少了打包体积,提升了打包速度。

3.noParse 避免重复打包

像vue.min.js已经模块化处理过了,需要忽略对此类型文件的打包。

webpack.prod.js:

rules: [{
  test: /\.vue$/,
  loader: ['vue-loader'],
  include: srcPath
}, ]
IgnorePlugin vs noParse
  • IgnorePlugin 直接不引入,代码中没有,减少了产出代码体积;
  • noParse 引入了,但是未打包;

4.happyPack 多进程打包

js是单线程,开启多线程打包,提高构建速度(特别是多核CPU)。

webpack.prod.js:
① 安装 HappyPack,并引入:

const HappyPack = require('happypack')

② plugins中引入HappyPack规则

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

③ rules 中 配置js 转换规则为happypack:

rules: [{
  test: /\.js$/,
  // 把对 .js 文件的处理转交给 id 为 babel 的 HappyPack 实例
  use: ['happypack/loader?id=babel'],
  include: srcPath,
  // exclude: /node_modules/
}, ]

5.ParalleUglifyPlugin 多进程压缩JS

JS单线程, 开启多进程压缩更快,webpack 内置 Uglify工具压缩JS
webpack.prod.js:
① 安装webpack-parallel-uglify-plugin 并引入配置文件中

const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')

② plugins中引入ParallelUglifyPlugin规则

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

happyPack 放在生产环境下,也可以放在本地环境下;
但是 ParallelUglifyPlugin 的压缩就只能放在生产环境下,开发环境下没有必要去做压缩

项目较大, 打包较慢, 开启多进程能提高速度
项目较小, 打包很快, 开启多进程会降低速度( 进程开销)

6.自动刷新

自动刷新用于开发环境
webpack.dev.js:

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

若开发环境开启devserver,自动刷新便默认不开启

7.热更新

webpack.dev.js:
① 引用webpack的插件HotModuleReplacementPlugin

const HotModuleReplacementPlugin = require('webpack/lib/HotModuleReplacementPlugin');

② 配置入口

entry: {
  // index: path.join(srcPath, 'index.js'),
  index: [
    'webpack-dev-server/client?http://localhost:8080/',
    'webpack/hot/dev-server',
    path.join(srcPath, 'index.js')
  ],
  other: path.join(srcPath, 'other.js')
},

③ 在 plugins中 new HotModuleReplacementPlugin()

④ 配置 devServer中 的 hot: true

devServer: {
  port: 8080,
  progress: true, // 显示打包的进度条
  contentBase: distPath, // 根目录
  open: true, // 自动打开浏览器
  compress: true, // 启动 gzip 压缩

  hot: true,

  // 设置代理
  proxy: {
    // 将本地 /api/xxx 代理到 localhost:3000/api/xxx
    '/api': 'http://localhost:3000',

    // 将本地 /api2/xxx 代理到 localhost:3000/xxx
    '/api2': {
      target: 'http://localhost:3000',
      pathRewrite: {
        '/api2': ''
      }
    }
  }
},

⑤ 开发环境中注册哪些模块能触发热更新
index.js:

// 增加,开启热更新之后的代码逻辑
if (module.hot) {
  module.hot.accept(['./math'], () => {
    const sumRes = sum(10, 30)
    console.log('sumRes in hot', sumRes)
  })
}

自动刷新 VS 热更新
自动刷新:整个网页全部刷新,速度较慢;状态会丢失
热更新:新代码生效,网页不刷新,状态不丢失

8.DLLPlugin 动态链接库插件

背景:

前端框架如 vue React ,体积大,构建慢
较稳定,不常升级版本
同一个版本之构建一次就可以,不用每次都重新构建

DLLPlugin 将常用框架打包成dll文件,然后再引用

webpack 已内置DLLPlugin支持:

  • DLLPlugin 打包出 dll 文件
  • DllReferencePlugin -使用 dll 文件
Stage1. 打包生成 dll 文件

① 单独配置 webpack.dll.js,用于生成dll文件(DllPlugin插件)

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'),
    }),
  ],
}

② 在package.json里配置dll,并运行:npm run dll
package.json:

script :{
  "dll":"webpack --config build/webpack.dll.js"
}

产出文件:
react.dll.js文件、//react打包后的dll文件
react.manifast.json.js文件//索引 找到对应关系

Stage2. 使用 dll 文件

webpack.dev.js配置引入:
① 引入插件 DllReferencePlugin

const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');

② 忽略 /node_modules/ 代码的loader

module: {
  rules: [
    {
      test: /\.js$/,
      loader: ['babel-loader'],
      include: srcPath,
      exclude: /node_modules/ // 第二,不要再转换 node_modules 的代码
    },
  ]
},

③ plugin配置 DllReferencePlugin 插件,告诉 Webpack 使用了哪些动态链接库

plugins: [
  // 第三,告诉 Webpack 使用了哪些动态链接库
  new DllReferencePlugin({
      // 描述 react 动态链接库的文件内容
    manifest: require(path.join(distPath, 'react.manifest.json')),
  }),
],

④ index.html 里引用 react.dll.js:

<script src="./react.dll.js"></script>

webpack优化构建速度总结:

可用于生产环境:

  • 优化 babel-loader 缓存(主要用于开发)
  • IgnorePlugin // 避免引入无用模块
  • noParse // 避免重复打包
  • happyPack // 多进程打包
  • ParalleUglifyPlugin(主要用于生产环境) // 多进程压缩JS

不能用于生产环境:

  • 自动刷新
  • 热更新
  • DLLPlugin(开发)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容