标签:nod jobs 多次 vue ret 性能优化 连接 ora rap
1.提高 Webpack 打包速度
(1)优化loader的文件搜索范围
Babel 是编写下一代 JavaScript 的编译器
对于 Loader
来说,影响打包效率首当其冲必属 Babel
了。因为 Babel
会将代码转为字符串生成 AST
,然后对 AST
继续进行转变最后再生成新的代码,项目越大,转换代码越多,效率就越低
首先我们可以优化 Loader 的文件搜索范围,在使用loader
时,我们可以指定哪些文件不通过loader
处理,或者指定哪些文件通过loader
处理。
module.exports = {
module: {
rules: [
{
// js 文件才使用 babel
test: /\.js$/,
use: [‘babel-loader‘],
// 只处理src文件夹下面的文件
include: path.resolve(‘src‘),
// 不处理node_modules下面的文件
exclude: /node_modules/
}
]
}
}
(2)将babel编译过的文件缓存起来
对于babel-loader
,我们还可以将 Babe
l 编译过的文件缓存起来,下次只需要编译更改过的代码文件即可,这样可以大幅度加快打包时间。
通过使用 cacheDirectory
选项,将 babel-loader
提速至少两倍。这会将转译的结果缓存到文件系统中。
{
test: /\.js$/,
use: ‘babel-loader?cacheDirectory‘,
include: [resolve(‘src‘), resolve(‘test‘) ,resolve(‘node_modules/webpack-dev-server/client‘)] //缓存客户端(浏览器)这个文件夹
}
cache-loader:缓存其他loader
除了 babel-loader
,如果我们想让其他的 loader
的处理结果也缓存,该怎么做呢?
答案是可以使用 cache-loader
。在一些性能开销较大的 loader
之前添加 cache-loader
,以便将结果缓存到磁盘里
安装
npm install --save-dev cache-loader
module.exports = {
module: {
rules: [
{
// js 文件才使用 babel
test: /\.js$/,
use: [‘cache-loader‘, ...loaders], //...loader是要指定的要缓存的loader的一个统称
include: path.resolve(‘src‘), //前面babel-loader 时候,已经统一指定只处理src下的文件,这里就不用单独再写了
},
],
},
};
那这么说的话,我给每个loder
前面都加上cache-loader
,然而凡事物极必反,保存和读取这些缓存文件会有一些时间开销,所以请只对性能开销较大的 loader
使用 cache-loader
。关于这个cache-loader
更详细的使用方法请参照这里cache-loader
那上面说了,我可以给指定的loader做缓存,那么我怎么知道是哪些loader比较慢,需要给其做缓存呢?因为不能无脑的全部缓存,这样保存和读取有时间开销,我们要知己知彼才能对症下药。所以我们需要一个分析速度的插件:
(3)分析各loader和plugin速度插件:speed-measure-webpack-plugin
// 安装
npm install --save-dev speed-measure-webpack-plugin
// 使用方式
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
const webpackConfig = smp.wrap({
module: {},
plugins: [
new MyPlugin(),
new MyOtherPlugin()
]
});
运行时候如下图所示
但需要注意的是:HardSourceWebpackPlugin 和 speed-measure-webpack-plugin 不能一起使用。
二、使用多线程打包
1.HappyPack :就是能够让Webpack
把打包任务分解给多个子线程去并发的执行,子线程处理完后再把结果发送给主线程。
在使用 Webpack 对项目进行构建时,webpack构建过程中的有两个部分是直接影响构建效率的,一个是文件的编译,另一个则是文件的分类打包。相较之下文件的编译更为耗时,而且在Node环境下文件只能一个一个去处理,因此这块的优化需要解决。
由于 JavaScript 是单线程模型,要想发挥多核 CPU 的能力,只能通过多进程去实现,而无法通过多线程实现。
而 Happypack 的作用就是将文件解析任务分解成多个子进程(多个子进程也就意味着多个子线程,一个进程里面有一个线程)并发执行。子进程处理完任务后再将结果发送给主进程。所以可以大大提升 Webpack 的项目构件速度
由于HappyPack 对file-loader、url-loader 支持的不友好,所以不建议对该loader使用。
module: {
rules: [
{
test: /\.js$/,
// 把对 .js 文件的处理转交给 id 为 babel 的 HappyPack 实例
use: [‘happypack/loader?id=babel‘],
exclude: path.resolve(__dirname, ‘node_modules‘),
},
{
test: /\.css$/,
// 把对 .css 文件的处理转交给 id 为 css 的 HappyPack 实例
use: [‘happypack/loader?id=css‘]
}
]
},
plugins: [
new HappyPack({
id: ‘js‘, //ID是标识符的意思,ID用来代理当前的happypack是用来处理一类特定的文件的
threads: 4, //你要开启多少个子进程去处理这一类型的文件
loaders: [ ‘babel-loader‘ ]
}),
new HappyPack({
id: ‘css‘,
threads: 2,
loaders: [ ‘style-loader‘, ‘css-loader‘ ]
})
]
2.thread-loader:在worker
池(worker pool)中运行加载器loader
。把thread-loader
放置在其他 loader
之前, 放置在这个 thread-loader
之后的 loader
就会在一个单独的 worker
池(worker pool)中运行。
module.exports = {
module: {
rules: [
{
test: /\.js$/,
include: path.resolve(‘src‘),
use: [
{
loader: "thread-loader",
// 有同样配置的 loader 会共享一个 worker 池(worker pool)
options: {
// 产生的 worker 的数量,默认是 cpu 的核心数
workers: 2,
// 一个 worker 进程中并行执行工作的数量
// 默认为 20
workerParallelJobs: 50,
// 额外的 node.js 参数
workerNodeArgs: [‘--max-old-space-size‘, ‘1024‘],
// 闲置时定时删除 worker 进程
// 默认为 500ms
// 可以设置为无穷大, 这样在监视模式(--watch)下可以保持 worker 持续存在
poolTimeout: 2000,
// 池(pool)分配给 worker 的工作数量
// 默认为 200
// 降低这个数值会降低总体的效率,但是会提升工作分布更均一
poolParallelJobs: 50,
// 池(pool)的名称
// 可以修改名称来创建其余选项都一样的池(pool)
name: "my-pool"
}
},
{
loader:‘babel-loader‘
}
]
}
]
}
}
同样,thread-loader
也不是越多越好,也请只在耗时的loader
上使用。
3.webpack-parallel-uglify-plugin :开启多个子进程
在Webpack3
中,我们一般使用 UglifyJS
来压缩代码,但是这个是单线程运行的,也就是说多个js
文件需要被压缩,它需要一个个文件进行压缩。所以说在正式环境打包压缩代码速度非常慢(因为压缩JS
代码需要先把代码解析成AST
语法树,再去应用各种规则分析和处理AST
,导致这个过程耗时非常大)。为了加快效率,我们可以使用 webpack-parallel-uglify-plugin
插件,该插件会开启多个子进程,把对多个文件压缩的工作分别给多个子进程去完成,但是每个子进程还是通过UglifyJS
去压缩代码。无非就是变成了并行处理该压缩了,并行处理多个子任务,提高打包效率。来并行运行 UglifyJS
,从而提高效率。
在 Webpack4
中,我们就不需要以上这些操作了,只需要将 mode
设置为 production
就可以默认开启以上功能。代码压缩也是我们必做的性能优化方案,当然我们不止可以压缩JS
代码,还可以压缩HTML
、CSS
代码,并且在压缩 JS
代码的过程中,我们还可以通过配置实现比如删除 console.log
这类代码的功能。
let ParallelUglifyPlugin = require(‘webpack-parallel-uglify-plugin‘);
module.exports = {
module: {},
plugins: [
new ParallelUglifyPlugin({
workerCount:3,//开启几个子进程去并发的执行压缩。默认是当前运行电脑的cPU核数减去1
uglifyJs:{
output:{
beautify:false,//不需要格式化
comments:false,//不保留注释
},
compress:{
warnings:false,//在Uglify]s除没有用到的代码时不输出警告
drop_console:true,//删除所有的console语句,可以兼容ie浏览器
collapse_vars:true,//内嵌定义了但是只用到一次的变量
reduce_vars:true,//取出出现多次但是没有定义成变量去引用的静态值
}
},
})
]
}
4.DllPlugin&DllReferencePlugin :将特定的类库提前打包成动态链接库
DllPlugin
可以将特定的类库提前打包成动态链接库,在一个动态链接库中可以包含给其他模块调用的函数和数据,把基础模块独立出来打包到单独的动态连接库里,当需要导入的模块在动态连接库里的时候,模块不用再次被打包,而是去动态连接库里获取。这种方式可以极大的减少打包类库的次数,只有当类库更新版本才有需要重新打包,并且也实现了将公共代码抽离成单独文件的优化方案。
这里我们可以先将react
、react-dom
单独打包成动态链接库,首先新建一个新的webpack
配置文件:webpack.dll.js
个人理解的dll基本过程:
1、第一次npm run的时候,把请求的内容存储起来(存储在映射表中)
2、再次请求时,先从映射表中找,看请求的内容是否有缓存,有则加载缓存(类似浏览器的缓存策略,命中缓存),没有就正常打包。
3、直接从缓存中读取。
配置:
const path = require(‘path‘);
const DllPlugin = require(‘webpack/lib/DllPlugin‘);
module.exports = {
// 想统一打包的类库
entry:[‘react‘,‘react-dom‘],
output:{
filename: ‘[name].dll.js‘, //输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称
path:path.resolve(__dirname,‘dll‘), // 输出的文件都放到 dll 目录下
library: ‘_dll_[name]‘,//存放动态链接库的全局变量名称,例如对应 react 来说就是 _dll_react
},
plugins:[
new DllPlugin({
// 动态链接库的全局变量名称,需要和 output.library 中保持一致
// 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
// 例如 react.manifest.json 中就有 "name": "_dll_react"
name: ‘_dll_[name]‘,
// 描述动态链接库的 manifest.json 文件输出时的文件名称
path: path.join(__dirname, ‘dll‘, ‘[name].manifest.json‘)
})
]
}
然后我们需要执行这个配置文件生成依赖文件:
webpack --config webpack.dll.js --mode development
接下来我们需要使用 DllReferencePlugin
将依赖文件引入项目中
const DllReferencePlugin = require(‘webpack/lib/DllReferencePlugin‘)
module.exports = {
// ...省略其他配置
plugins: [
new DllReferencePlugin({
// manifest 就是之前打包出来的 json 文件
manifest:path.join(__dirname, ‘dll‘, ‘react.manifest.json‘)
})
]
}
5. noParse:可以用于配置那些模块文件的内容不需要进行解析(即无依赖) 的第三方大型类库(例如jquery
,lodash
)等,使用该属性让 Webpack
不扫描该文件,以提高整体的构建速度。
module.exports = {
module: {
noParse: /jquery|lodash/, // 正则表达式
// 或者使用函数
noParse(content) {
return /jquery|lodash/.test(content)
}
}
}
6.IgnorePlugin :IgnorePlugin
用于忽略某些特定的模块,让webpack
不把这些指定的模块打包进去。
module.exports = {
// ...省略其他配置
plugins: [
new webpack.IgnorePlugin(/^\.\/locale/,/moment$/)
]
}
webpack.IgnorePlugin()
参数中第一个参数是匹配引入模块路径的正则表达式,第二个参数是匹配模块的对应上下文,即所在目录名。
三、值的注意的小点点
resolve.extensions
:用来表明文件后缀列表,默认查找顺序是 [‘.js‘, ‘.json‘]
,如果你的导入文件没有添加后缀就会按照这个顺序查找文件。我们应该尽可能减少后缀列表长度,然后将出现频率高的后缀排在前面resolve.alias
:可以通过别名的方式来映射一个路径,能让 Webpack
更快找到路径module.exports ={
// ...省略其他配置
resolve: {
extensions: [".js",".jsx",".json",".css"],
alias:{
"jquery":jquery
}
}
};
四、减少Wwebpack打包后的文件体积
1.image-webpack-loader 对图片进行压缩和优化
image-webpack-loader这个loder
可以帮助我们对打包后的图片进行压缩和优化,例如降低图片分辨率,压缩图片体积等。
module.exports ={
// ...省略其他配置
module: {
rules: [
{
test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
use: [
‘file-loader‘,
{
loader: ‘image-webpack-loader‘,
options: {
mozjpeg: {
progressive: true,
quality: 65
},
optipng: {
enabled: false,
},
pngquant: {
quality: ‘65-90‘,
speed: 4
},
gifsicle: {
interlaced: false,
},
webp: {
quality: 75
}
}
}
]
}
]
}
};
2.删除无用的CSS样式
有时候一些时间久远的项目,可能会存在一些CSS
样式被迭代废弃,需要将其剔除掉,此时就可以使用purgecss-webpack-plugin
插件,该插件可以去除未使用的CSS
,一般与 glob
、glob-all
配合使用。
注意:此插件必须和CSS
代码抽离插件mini-css-extract-plugin
配合使用。
例如我们有样式文件style.css
body{
background: red
}
.class1{
background: red
}
这里的.class1
显然是无用的,我们可以搜索src
目录下的文件,删除无用的样式。
const glob = require(‘glob‘);
const PurgecssPlugin = require(‘purgecss-webpack-plugin‘);
module.exports ={
// ...
plugins: [
// 需要配合mini-css-extract-plugin插件
new PurgecssPlugin({
paths: glob.sync(`${path.join(__dirname, ‘src‘)}/**/*`,
{nodir: true}), // 不匹配目录,只匹配文件
})
}),
]
}
3.以CDN方式加载资源
我们知道,一般常用的类库都会发布在CDN
上,因此,我们可以在项目中以CDN
的方式加载资源,这样我们就不用对资源进行打包,可以大大减少打包后的文件体积。
以CDN
方式加载资源需要使用到add-asset-html-cdn-webpack-plugin
插件。我们以CDN
方式加载jquery
为例:
const AddAssetHtmlCdnPlugin = require(‘add-asset-html-cdn-webpack-plugin‘)
module.exports ={
// ...
plugins: [
new AddAssetHtmlCdnPlugin(true,{
‘jquery‘:‘https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js‘
})
],
//在配置文件中标注jquery是外部的,这样打包时就不会将jquery进行打包了
externals:{
‘jquery‘:‘$‘
}
}
4.摇树:Tree Shaking
5.开启Scope Hoisting
Scope Hoisting
可以让 Webpack
打包出来的代码文件更小、运行的更快, 它又译作 "作用域提升",是在 Webpack3
中新推出的功能。
由于最初的webpack
转换后的模块会包裹上一层函数,import
会转换成require
,因为函数会产生大量的作用域,运行时创建的函数作用域越多,内存开销越大。而Scope Hoisting 会分析出模块之间的依赖关系,尽可能的把打包出来的模块合并到一个函数中去,然后适当地重命名一些变量以防止命名冲突。这个功能在webpack4
中,当我们将mode
设置为production
时会自动开启。
比如我们希望打包两个文件
let a = 1;
let b = 2;
let c = 3;
let d = a+b+c
export default d;
// 引入d
import d from ‘./d‘;
console.log(d)
最终打包后的结果会变成 console.log(6)
,这样的打包方式生成的代码明显比之前的少多了,并且减少多个函数后内存占用也将减少。如果你希望在开发模式development
中开启这个功能,只需要使用插件 webpack.optimize.ModuleConcatenationPlugin()
就可以了。
module.exports = {
// ...
plugins: [
// 开启 Scope Hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
]
}
6.按需加载&动态加载
大家在开发单页面应用项目的时候,项目中都会存在十几甚至更多的路由页面。如果我们将这些页面全部打包进一个文件的话,虽然将多个请求合并了,但是同样也加载了很多并不需要的代码,耗费了更长的时间。那么为了首页能更快地呈现给用户,我们肯定是希望首页能加载的文件体积越小越好,这时候我们就可以使用按需加载,将每个路由页面单独打包为一个文件。在给单页应用做按需加载优化时,一般采用以下原则:
chunk
动态加载目前并没有原生支持,需要babel
的插件:plugin-syntax-dynamic-import
。安装此插件并且在.babelrc
中配置:
{
// 添加
"plugins": ["transform-vue-jsx", "transform-runtime"],
}
例如如下示例:
index.js
let btn = document.createElement(‘button‘);
btn.innerHTML = ‘点击加载视频‘;
btn.addEventListener(‘click‘,()=>{
import(/* webpackChunkName: "video" */‘./video‘).then(res=>{
console.log(res.default);
});
});
document.body.appendChild(btn);
webpack.config.js
module.exports = {
// ...
output:{
chunkFilename:‘[name].min.js‘
}
}
这样打包后的结果最终的文件就是 video.min.js
,并且刚启动项目时不会加载该文件,只有当用户点击了按钮时才会动态加载该文件。
标签:nod jobs 多次 vue ret 性能优化 连接 ora rap
原文地址:https://www.cnblogs.com/haoqiyouyu/p/14779192.html