Webpack性能优化

webpack常见性能优化

最近学习webpack,深感webpack的魅力,总结一下性能优化相关的内容

优化打包构建速度 - 开发体验和效率

优化babel-loader

在babel-loader后加一个?cacheDirectory,开启babel-loader的缓存。这样如果es6代码没有改,就不会再重新编译

还有就是通过include确定babel编译范围,通常为src。(排除node_modules)

IgnorePlugin

这个plugin作用是避免引入无用模块。比如我们通常会使用moment.js,但默认会引入所有语言的js代码,代码过大。可能只需要引入中文和英文的模块,这个插件就可以帮助。

那么就可以在业务代码中手动引入中文和英文的语言包,然后再webpack配置中写入

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

忽略掉moment里的locale语言包,这样就能减少无用的模块引入。

noParse

避免重复打包。社区中常有的min.js通常都是已经打包了的,因此没必要再次打包,在module.noParse中配置不需要打包的文件即可。只是react.min.js不能这么搞,这个没有采用模块化,还是需要打包的。

和Ignore的区别是,Ignore直接不引入,代码中没有。但noParse会引入,但不打包。都能够提升构建速度,Ignore还能够优化线上的性能。

happyPack多进程打包

因为js是单线程的,所以webpack实际上也是进行的单线程打包。所以如果我们能够开启多进程进行打包,那么就能够大幅提高构建速度,happyPack就能够做到这个

注:这是个plugin

比如要将babel的解析放在新进程中,那么module.rule中对js的解析需要改成:

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

然后需要在plugin中配置babel的happyPack实例:

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

ParallelUglifyPlugin多进程压缩JS

webpack本身内置了uglify压缩JS,但压缩JS本身是比较高成本的,这里也可以进行多进程开启。
直接在Plugin中实例化就行。根据配置要求进行配置。

当然,关于开启多进程是否需要,并不是一定需要的。

如果项目较大,打包较慢,开启多进程能够提高速度;但开启多进程本身就需要成本和时间,小项目没必要配置,反而会造成打包时间增加。

热更新HotModuleReplacementPlugin

自动刷新是速度比较慢,且状态会丢失。
热更新的话,则不会刷新,状态不丢失,且新代码能够立刻生效。

主要引入HotModuleReplacementPlugin即可。
首先entry需要修改:

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

Plugin中实例化即可,并在devServer中设置hot为true即可。

最后,JS代码需要还需要判断一下是否是热更新,注册监听范围

if (module.hot) {
    module.hot.accept(['./math'), () => {});
}

所以热更新有成本,需要手动注册。

实际使用中没有遇见需要这样的,通常只用了css的逻辑(默认支持热更新)。

DllPlugin动态链接库插件

前端框架通常会使用vue、React,体积大,构建慢。但是比较稳定,开发情况下不会经常升级版本。

因此,这种内容同一个版本只需要构建一次就可以了,没必要每次都重新构建。

webpack已经内置了DllPlugin,主要包含两个插件:

DllPlugin:打包出dll文件,先进行一遍预打包,生成dll文件

DllReferencePlugin:在开发环境使用dll文件。

一般需要单独定义一个dll.config.js

output: {
    path: path.join(__dirname, '../build'), // 放在项目的/build目录下面
    filename: '[name].dll.js', // 打包文件的名字
    library: '[name]_library' // 可选 暴露出的全局变量名
    // vendor.dll.js中暴露出的全局变量名。
    // 主要是给DllPlugin中的name使用,
    // 故这里需要和webpack.DllPlugin中的`name: '[name]_library',`保持一致。
},

plugin

    new webpack.DllPlugin({
      path: path.join(__dirname, '../build', '[name]_manifest.json'), // 生成上文说到清单文件,放在当前build文件下面,这个看你自己想放哪里了。
      name: '[name]_library',
      context: __dirname,
    }),

然后定义dll的命令,进行dll打包

最后输出了dll和manifest文件。dll文件就是所有打包的文件内容,而manifest的作用就是索引,负责引导实际对于dll里模块的引用。

实际应用的话,就是需要在dev的html中去进行script进行引用。这里就可以用dllReferencePlugin插件去引用manifest文件,用AddAssetHtmlPlugin引用dll文件即可

优化产出代码 - 产品性能提高

核心:让代码体积更小、合理分包,不重复加载、速度更快,内存使用更少

小图片用base64的编码

使用url-loader,对小于xkb的图片做base64格式进行产出,没有必要再做一次网络请求

bundle需要加hash

根据文件内容算hash值,客户端可以实现缓存管理使用。js代码如果没有变化,那么即便再次上线也能复用没变的bundle块

懒加载

import()

提取公共代码

一些通用代码和第三方应该被抽离。在optimization里的splitChunks进行设置

IngorePlugin

减少打包代码内容

使用cdn加速

使用cdn加速,修改output里的publicPath,能够修改所有静态文件url的前缀
当然需要在部署上线脚本里加上上传到cdn的操作

图片则在图片的url-loader里加上publicPath就行

使用production

在mode中使用production,会自动开启代码压缩,减少代码体积

VueReact等成熟的库会自动删掉调试代码比如开发环境的warning

其次production还会开启Tree Shaking:可以移除 JavaScript 上下文中的未引用代码,删掉用不着的代码,能够有效减少 JS 代码文件的大小。

假设我们不使用 production mode,而是用 development mode,那么我们需要在配置文件中新增:

module.exports = {
  mode: 'development',
  //...
  optimization: { 
    useExports: true, // 模块内未使用的部分不进行导出
  },
}

必须用ES6 module才能让tree shaking生效,commonjs不行

仅仅因为 Webpack 看不到一段正在使用的代码,并不意味着它可以安全地进行 tree-shaking。有些模块导入,只要被引入,就会对应用程序产生重要的影响。一个很好的例子就是全局样式表,或者设置全局配置的JavaScript 文件。
Webpack 认为这样的文件有“副作用”。具有副作用的文件不应该做 tree-shaking,因为这将破坏整个应用程序。Webpack 的设计者清楚地认识到不知道哪些文件有副作用的情况下打包代码的风险,因此默认地将所有代码视为有副作用。这可以保护你免于删除必要的文件,但这意味着 Webpack 的默认行为实际上是不进行 tree-shaking。
幸运的是,我们可以配置我们的项目,告诉 Webpack 它是没有副作用的,可以进行 tree-shaking。

package.json 有一个特殊的属性 sideEffects,就是为此而存在的。它有三个可能的值:
true 是默认值,如果不指定其他值的话。这意味着所有的文件都有副作用,也就是没有一个文件可以 tree-shaking。
false 告诉 Webpack 没有文件有副作用,所有文件都可以 tree-shaking。
第三个值 […] 是文件路径数组。它告诉 webpack,除了数组中包含的文件外,你的任何文件都没有副作用。因此,除了指定的文件之外,其他文件都可以安全地进行 tree-shaking。

使用Scope Hosting

当存在多个文件时,实际上在webpack中会打包成多个函数。但文件越多,函数越多,函数越多占用内存越多(作用域)。
是否可以把打包后的函数进行合并?这就是Scope Hosting的作用:代码体积更小、创建函数作用域更少、代码可读性更少

引用ModuleConcatenationPlugin插件并使用,代价是必须要用ES6模块化组织代码才能用这个效果,所以针对NPM中的第三方模块优先采用 jsnext:main 中指向ES6模块化语法的文件

配置resolve:

resolve: {
    mainFields: ['jsnext:main', 'browser', 'main']
}
 小程序的介绍以及底层渲染原理
关于setState是异步还是同步 
上一篇:小程序的介绍以及底层渲染原理
下一篇:关于setState是异步还是同步


如果我的文章对你有帮助,或许可以打赏一下呀!

支付宝
微信
QQ