webpack性能优化

webpack性能优化主要有两方面:构建速度优化,产出速度优化

对生产环境产出代码合理分包,不重复加载,使其体积更小,速度更快,内存使用更少。浏览器加载页面时用的时间越短越好所以构建出的文件越小越好,减少浏览器需要发起请求的数量,减少下载静态资源的时间。webpack在生产环境构建时会做一些额外工作如代码压缩等,可优化前端资源加载性能。

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

优化babel-loader
开启babel-loader缓存。babel-loader后加?cacheDirectory如果es6代码没有改,不会重新编译
控制babel编译范围。用include确定包含的范围,或者exclude确定不包括的范围。(排除node_modules)
项目中配置babel-loader
webpack.config.common.js文件 webpack生产环境中配置

module: {
	rules: [
		{
			test: /.\ts$/,
			exclude: /node_modules/,
			use: [
				'babel-loader',
				{
					loader: 'ts-loader',
					options: {
						// 此处为了ts-loader第一次命中就把所有options被缓存到
						appendTsSuffixTo: [/\.vue$/],
					},
				},
			],
		},
		{
			test: /.\js$/,
			exclude: /node_modules/,
			loader: 'babel-loader',
			options: {
				cacheDirectory:true,
			},
		},
	]
}

IgnorePlugin避免引入无用模块
如引入moment,js,只需要中文和英文的模块
业务代码中手动引入中文和英文的语言包,webpack配置中

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

忽略掉moment里的locale语言包,可以减少无用模块引入

noParse避免重复打包
如社区中min.js这种一般是已经打包过的,无需再次打包。可在module.noParse中配置不需打包的文件。注意,react.min.js不能使用,没有采用模块化,需要打包

noParse和Ignore区别:Ignore直接用不引入,代码中没有。noParse会引入,但不打包。两个都能提高构建速度,Ignore还可以优化线上性能。

happyPack多进程打包 是plugin
js是单线程,webpack实际也是单线程打包。

比如要将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
自动刷新和热更新区别:
自动刷新速度较慢,状态会丢失
热更新不会刷新,状态不丢失,新代码可立刻生效

webpack.config.dev.js文件 webpack本地环境中配置
plugin中实例化,devSever中设置hot为true

devServer: {
      host: '0.0.0.0',
      port,
      openPage: `http://${ip}:${port}/${publicPath}`,
      hot: true,
      inline: true,
      liveReload: true,
      historyApiFallback: {
        rewrites: [
          {
            from: /.*/g,
            to: `/${publicPath}`,
          },
        ],
      },
},
plugins: [
      // Only update what has changed on hot reload
      new webpack.HotModuleReplacementPlugin(),
      new UrlLogPlugin(),
],

DLLPlugin动态链接库插件
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文件即可

产出速度优化

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

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

小图片用base64编码
url-loader 设置limit值控制图片大小,(小于该文件大小的图片转为base64格式)将处理后的图片引入项目,减少http请求

{
    test: /\.(png|svg|jpg|gif)$/,
    use: {
        loader: 'url-loader',
        options: {
            limit: 3*1024 // 3k
        }
    }
}

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

bundle:webpack打包后的各个文件,一般和chunk(代码块)一对一关系,bundle就是对chunk进行编译压缩打包等处理后的产出。

懒加载
webpack支持异步加载模块的特性。

按需加载:如一个应用有3个页面,首页加载时只加载首页的逻辑,其他两个页面跳转到页面后在异步加载。

原理:动态向页面找那个插入script标签。

webpack支持实现方式有两种:
commonjs形式的require
es6的异步import()

// 普通加载
import xxx from './xxx'
懒加载(按需加载)
const xxx = () => import('./xxx')

提取公共代码
抽离一些通用代码和第三方的。

webpack 3.x前版本用CommonsChunkPlugin做代码分离,4.x把相关功能包在optimization里的splitChunks

参数:
chunks选项,决定要提取那些模块。
默认是async:只提取异步加载的模块出来打包到一个文件中。
异步加载的模块:通过import(‘xxx’)或require([‘xxx’],() =>{})加载的模块。

initial:提取同步加载和异步加载模块,如果xxx在项目中异步加载了,也同步加载了,那么xxx这个模块会被提取两次,分别打包到不同的文件中。
同步加载的模块:通过 import xxx或require(‘xxx’)加载的模块。

all:不管异步加载还是同步加载的模块都提取出来,打包到一个文件中。

minSize选项:规定被提取的模块在压缩前的大小最小值,单位为字节,默认为30000,只有超过了30000字节才会被提取。
maxSize选项:把提取出来的模块打包生成的文件大小不能超过maxSize值,如果超过了,要对其进行分割并打包生成新的文件。单位为字节,默认为0,表示不限制大小。
minChunks选项:表示要被提取的模块最小被引用次数,引用次数超过或等于minChunks值,才能被提取。
maxAsyncRequests选项:最大的按需(异步)加载次数,默认为 6。
maxInitialRequests选项:打包后的入口文件加载时,还能同时加载js文件的数量(包括入口文件),默认为4。
先说一下优先级 maxInitialRequests / maxAsyncRequests <maxSize<minSize。
automaticNameDelimiter选项:打包生成的js文件名的分割符,默认为~。
name选项:打包生成js文件的名称。
cacheGroups选项,核心重点,配置提取模块的方案。里面每一项代表一个提取模块的方案。下面是cacheGroups每项中特有的选项,其余选项和外面一致,若cacheGroups每项中有,就按配置的,没有就使用外面配置的。
test选项:用来匹配要提取的模块的资源路径或名称。值是正则或函数。
priority选项:方案的优先级,值越大表示提取模块时优先采用此方案。默认值为0。
reuseExistingChunk选项:true/false。为true时,如果当前要提取的模块,在已经在打包生成的js文件中存在,则将重用该模块,而不是把当前要提取的模块打包生成新的js文件。
enforce选项:true/false。为true时,忽略minSize,minChunks,maxAsyncRequests和maxInitialRequests外面选项

项目中配置

optimization: {
    splitChunks: {
      chunks: 'async',  // 决定提取哪些模块
      minSize: 0, // 超过该值才会被提取,单位字节,默认30000
      maxSize: 30000, // 打包生成的文件最大值,如果超过则分隔打包生成新文件。单位字节,默认0表不限制大小
      minChunks: 1, // 被提取的模块最小被引用次数,引用次数超过或等于该值才能被提取
      maxAsyncRequests: 6, // 最大按需加载次数,默认6
      maxInitialRequests: 4, // 打包后入口文件加载时同时加载的js文件数量(包括入口文件)默认4
      automaticNameDelimiter: '~', // 打包生成的js文件名的分隔符,默认~
      cacheGroups: {  // 核心重点!配置提取模块的方案,每一项代表一个提取模块的方案
        vendors: {
          name: `chunk-vendors`, // 打包生成文件名
          test: /[\\/]node_modules[\\/]/,  // 匹配提取的模块资源路径或名称,值为正则或函数
          priority: -10,  // 优先级,值越大优先级越高。默认值0
          chunks: 'initial',
        },
        common: {
          name: `chunk-common`,
          minChunks: 2,
          priority: -20,
          chunks: 'initial',
          reuseExistingChunk: true, // true时,如果要提取的模块,打包文件已存在,则重用该模块,不重新打包
          enforce: false, // true时,忽略minSize,minChunks,maxAsyncRequests,maxInitialRequests外面选项
        },
      },
    },
  },

IngorePlugin减少打包内容

cdn加速 内容分发网络
原理:优化物理链路层传输过程中的网速有限,丢包等问题提升网速。通过在各地部署服务器,形成cdn集群,提高访问速度,把资源部署到各地,用户访问是就近原则向离用户最近的服务器获取资源。

tree-shaking移除没用的js代码
打包过程中移除js上下文没引用的代码。依赖于es6的import和export语句,用来检测代码模块是否被导入,导出,被 js文件引用。

es6 Module的模块依赖解析在代码静态分析过程中进行,可在编译阶段就获取到整个依赖树(不是运行时)这一点webpack提供tree-shaking功能进行代码静态分析层面的优化。
webpack.config.js里Module为production自动开启tree-shaking。但只有用es6 Module才可生效,不能用commonjs。
原因:commonjs动态引入,执行时引入。es6 Module静态引入,编译时引入。

用Scope Hosting
该插件只适用于webpack直接处理es6模块。
针对NPM中第三方模块优先采用jsnext:main中指向es6模块化语法的文件

大型工程中模块引用层级一般较深,产生较长引用链,Scope Hosting可将纵深的引用链拍平,使得模块本身和其引用的其他模块作用域处同级,可去掉一部分webpack附加代码减小资源体积。

作用:代码体积更小,创建函数作用域更少,代码可读性更好。

使用方式:
引入webpack内部插件 ModuleConcatenationPlugin插件(默认在生产模式下已启用,若要在其他模式下启用concatenation,可手动添ModuleConcatenationPlugin或用optimization.concatenateModules选项)

module.exports = {
    resolve: {
        mainFields: ['jsnext:main', 'browser', 'main'] // 针对npm中的第三方模块优先采用jsnext:main中指向的ES6模块化语法的文件
    },
    plugins: [
        new webpack.optimize.ModuleConcatenationPlugin() // 开启Scope Hoisting
    ]
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值