一、编译速度优化
1.使用缓存
- 在webpack3中为了提高构建速度,我们往往会用到 DllPlugin 和 DllReferencePlugin 插件,但是配置复杂,更新文件还需要手动重新生成dll,比较繁琐。还有一种autodll-webpack-plugin插件会好用一些。
- 在webpack4之后可以使用
HardSourceWebpackPlugin
插件,它通过在磁盘中设置缓存来提升编译加载速度,第一次正常编译并缓存,第二次有缓存后能减少80%-90%的时间,需要安装插件后使用。 - webpack5主要使用内置的
cache
配置。
1.1 autodll-webpack-plugin插件
autodll-webpack-plugin
插件可以将项目中的第三方库(如lodash、moment等)单独打包成一个DLL文件,从而减少主文件的体积,提高编译速度。 运行webpack
命令进行构建,生成的DLL文件会存放在dist
目录下。通过使用autodll-webpack-plugin
插件,可以有效地优化Vue项目,提高编译速度和运行效率。
安装
npm install autodll-webpack-plugin --save-dev
引入
const AutoDllPlugin = require('autodll-webpack-plugin');
配置
module.exports = {
// ...其他配置
plugins: [
// ...其他插件
new AutoDllPlugin({
inject: true, // 自动注入生成的DLL文件到HTML中
filename: '[name].dll.js', // DLL文件名格式
entry: {
vendor: ['vue', 'vue-router', 'vuex'] // 需要单独打包的第三方库
}
})
]
};
1.2 HardSourceWebpackPlugin插件
HardSourceWebpackPlugin
是一个用于优化Webpack构建速度的插件。它可以为模块代码创建内存缓存,这样在每次构建时,只需要处理发生更改的模块,而不是重新处理所有模块。 为了查看结果,需要使用此插件运行 webpack 两次:第一次构建将花费正常的时间。第二个构建速度将明显加快。
安装
npm install hard-source-webpack-plugin --save-dev
引入
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
配置
module.exports = {
// ...
configureWebpack: smp.wrap({
plugins: [
// 为模块提供中间缓存,缓存路径是:node_modules/.cache/hard-source
new HardSourceWebpackPlugin()
]
})
// ...
}
-
注意:这里有另一个插件 speed-measure-webpack-plugin插件,作用是分析构建时各模块耗时,不能与hard-source-webpack-plugin一起使用
1.3 webpack5配置cache
webpack5
内置了 cache 缓存机制。直接配置即可。cache 会在开发模式下被设置成 type: memory 而且会在生产模式把cache 给禁用掉。
配置
// webpack.config.js 或 vue.config.js
const path = require('path')
module.exports = {
transpileDependencies: true,
configureWebpack: {
cache: {
type: 'filesystem', // 设置缓存类型为文件系统
cacheDirectory: path.resolve(__dirname, '.webpack_cache') // 设置缓存目录(根路径下)
// cacheDirectory: path.join(__dirname, 'node_modules/.cache/webpack_cache') // 设置缓存目录(node_modules/.cache路径下)
}
}
}
2.多线程打包
2.1 thread-loader
thread-loader 是一个 Webpack 的 loader,它可以将一些开销较大的工作放到 worker 池中,并在 worker 池中执行,以提高构建速度。 注意,thread-loader 并不是适用于所有场景的,它只对一些开销较大的任务有效。如果任务本身就非常快速并且非常简单,则使用 thread-loader 可能会比直接在主线程中执行更慢。因此可以使用上文提到的speed-measure-webpack-plugin插件对构建时各模块的耗时进行分析,对开销较大的模块使用thread-loader以提高构建速度。
安装
npm install thread-loader --save-dev
使用示例(具体配置写法根据项目不同写法各异)
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /.js$/,
include: path.resolve('src'),
exclude: /node_modules/,
use: [
'thread-loader', // 将后续 loader 放在 worker 池中执行耗时的 loader (例如 babel-loader)
'babel-loader'
]
}
]
}
}
// vue.config.js
module.exports = {
// ...
configureWebpack: {
module: {
rules: [
{
test: /.js$/,
include: path.resolve('src'),
exclude: /node_modules/,
use: [
'thread-loader', // 将后续 loader 放在 worker 池中执行耗时的 loader (例如 babel-loader)
'babel-loader'
]
}
]
}
}
}
2.2 HappyPack
HappyPack 是一个用于将 Webpack 进行多线程编译的工具,它能够将一个复杂的 Loader 或者 Plugin 任务分解成多个子进程并行执行,以此来加快构建速度。
安装
npm install happypack --save-dev
配置
const HappyPack = require('happypack');
module.exports = {
// ...
module: {
rules: [
{
test: /.js$/,
exclude: /node_modules/,
use: 'happypack/loader?id=js'
}
]
},
plugins: [
new HappyPack({
id: 'js',
threads: 4,
loaders: ['babel-loader']
})
],
// ...
};
3.开启热更新/热重载
- 热更新(Hot Update):在代码文件被修改后,自动编译出新的代码,并将新代码注入到浏览器运行的 JavaScript 中。热更新并没有重新加载页面,而是更新了部分改动的内容,保留了页面的状态信息,因此可以保持用户的操作状态。
- 热重载(Hot Reload):在代码文件被修改后,以最快的速度重新编译、打包,并刷新浏览器窗口。相比于热更新,热重载不仅更新了 JavaScript 代码,还重新渲染了整个页面,因此会有一短暂的闪屏,更适用于 UI 相关的修改。
- 模块热替换(Hot Module Replacement):类似于热更新,但是只替换变化的部分,不会重载整个模块或者应用程序,因此速度更快,体验更好。模块热替换不仅适用于 JavaScript 模块,还可以用于 CSS、图片等资源的替换。
3.1 HotModuleReplacementPlugin 插件
HotModuleReplacementPlugin
是 Webpack 提供的一个插件,用于启用模块热替换(HMR)功能。HMR 是一种开发模式,它可以在不刷新整个页面的情况下,只更新修改过的部分,提高开发效率。 通过上述配置,就可以开启 Webpack 的模块热替换功能,使得在开发过程中能够实时预览修改的效果,提高开发效率。需要注意的是,HMR 功能只适用于开发环境,在生产环境中不应该使用。
const webpack = require('webpack');
module.exports = {
// ...
mode: 'development',
devtool: 'eval-source-map', // 使用 source-map 方便调试
devServer: {
port: 8080,
hot: true, // 开启 HMR 功能
open: true // 自动打开浏览器
},
plugins: [
new webpack.HotModuleReplacementPlugin() // 启用 HMR 功能
]
}
3.2 babel-plugin-dynamic-import-node 插件
babel-plugin-dynamic-import-node
是一个 Babel 插件,用于在开发环境中动态导入模块,从而帮助 Hot Module Replacement (HMR) 更快地处理模块的更新。要使用这个插件,你需要按照以下步骤操作:
1.安装插件: 首先,通过 npm 安装 babel-plugin-dynamic-import-node
插件:
npm install --save-dev babel-plugin-dynamic-import-node
2.配置 Babel: 在你的项目根目录下找到或者创建一个 Babel 配置文件(通常是 .babelrc
或 babel.config.js
文件),然后在 "plugins" 部分添加 'dynamic-import-node'
:
{
"plugins": ["dynamic-import-node"]
}
如果你使用的是 babel.config.js
,则配置如下:
module.exports = {
plugins: ['dynamic-import-node'],
}
3.在代码中使用: 在你的代码中,你可以使用 ES2020 的动态 import()
语法来导入模块。例如:
if (condition) {
import('./module.js').then((module) => {
// 使用 module
});
}
4.Webpack 配置: 确保你的 Webpack 配置中已经启用了 HMR。具体配置参考3.1所述。
4.exclude & include
在 Vue 项目中使用 exclude 和 include 可以帮助提升编译速度,这是因为它们可以减少不必要的文件检查和编译。
exclude 和 include 是通过 webpack 的 module.rules 配置项来设置的。其中,exclude 表示排除某些目录或文件不被编译,而 include 表示包含某些目录或文件进行编译。
下面以 Vue 项目的 Babel 编译为例,介绍如何使用 exclude 和 include。
安装依赖:其中,babel-loader 是用于编译 JavaScript 代码的工具,@babel/core 是 Babel 核心库,@babel/preset-env 是一种预设包,可以根据目标环境自动选择转换插件。
npm install --save-dev babel-loader @babel/core @babel/preset-env
配置 webpack.config.js:
以下配置中,exclude: /node_modules/ 表示排除 node_modules 目录下的 JavaScript 文件不被编译,以提高编译速度。
module.exports = {
// ...其他配置
module: {
rules: [
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
}
有时候,如果我们只需要编译项目中某个目录下的 JavaScript 文件,可以使用 include 来指定相应的目录。例如,我们只需要编译 src/components 目录下的 JavaScript 文件,可以将配置改为:
module.exports = {
// ...其他配置
module: {
rules: [
{
test: /.js$/,
include: path.resolve(__dirname, 'src/components'),//表示只包含 src/components 目录下的 JavaScript 文件进行编译
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
}
5.提高 webpack版本
升级 Webpack 版本是提高构建速度的一个重要方式,每个新版本都会带来新的优化和性能改进。
更新 Webpack 到最新版本: 首先需要确定当前使用的 Webpack 版本并检查是否需要更新,可以通过 npm 命令安装最新版本的 Webpack:
- 检查 Webpack 相关的插件和 loader 的版本: 升级 Webpack 时,应该同时检查相关的插件和 loader 是否需要更新,需要确保其与 Webpack 的版本兼容。
- 移除废弃的插件和 loader: 升级可能有些插件和loader 已经随着升级废弃了,因此需要移除对应的插件和 loader。
三、打包体积优化
1.分析打包结果
webpack-bundle-analyzer
是一个 Webpack 插件和命令行工具,用于分析 Webpack 的打包文件。在 Vue CLI 项目中使用它,可以帮助你理解并优化你的应用程序包的大小。
要在 Vue CLI 项目中使用 webpack-bundle-analyzer
,你需要按照以下步骤操作:
1.安装: 首先,你需要在你的 Vue CLI 项目中安装 webpack-bundle-analyzer
插件。你可以通过运行以下命令来安装:
npm install --save-dev webpack-bundle-analyzer
2.配置: 接下来,你需要在你的 Webpack 配置文件中添加 webpack-bundle-analyzer
插件。对于 Vue CLI 3 项目,你可以在 vue.config.js
文件中进行配置。如果文件不存在,你需要创建它。在 vue.config.js
文件中,你可以添加以下内容:
// javascript复制代码运行
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
configureWebpack: {
plugins: [
new BundleAnalyzerPlugin()
]
}
};
对于 Vue CLI 4 及更高版本的项目,你需要在 vue.config.js
文件中稍微修改配置:
// javascript复制代码运行
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
module.exports = {
configureWebpack: {
plugins: [
new BundleAnalyzerPlugin()
]
}
};
-
构建: 现在,当你运行 Vue CLI 项目的构建命令时,
webpack-bundle-analyzer
将会被触发。它将分析你的打包文件,并自动打开一个网页,显示各个包的大小和组成。 -
查看报告: Webpack Bundle Analyzer 会生成一个交互式的可视化报告,你可以在浏览器中查看。报告将展示每个包的内容,以及它们的大小。这有助于识别可以优化的地方,例如未使用的代码或过大的依赖项。
-
优化: 根据报告的结果,你可以采取相应的优化措施,例如移除不必要的依赖、按需导入组件或模块、使用 Tree-shaking 和 Code Splitting 等 Webpack 功能。
2.第三方库按需引入
如:element-ui按需引用,echart按需引用, lodash按需引用。
3.生产环境去掉onsole.log
3.1 babel-plugin-transform-remove-console插件
这是一个Babel插件,可以在构建期间删除所有console.*语句
安装
npm install --save-dev babel-plugin-transform-remove-console
配置: babel.config.js
或.babelrc
文件中添加以下代码
const plugins = [];
// remove console.* in production
if (process.env.NODE_ENV === 'production') {
plugins.push('transform-remove-console');
// plugins.push(['transform-remove-console', { 'exclude': ['error', 'warn'] }]);
}
module.exports = {
// ...
plugins: plugins,
};
3.2 main.js里面通过方法实现
// main.js
function isProduction() {
return process.env.NODE_ENV === 'production';
}
if (isProduction()) {
console.log = function () {};
}
// 在需要的地方调用console.log
console.log("这是一个测试信息"); // 如果当前环境是生产环境,则不会输出任何内容
4.合理使用source-map
productionSourceMap的作用在于定位问题,打包时会生成.map文件,在生产环境就可以在浏览器查看到输出的信息具体是在哪一行,但相应的包的体积也会变大,也会影响构建速度,将其设置为false则不生成.map文件,既可以减少包大小,也可以加密源码。
module.exports = {
productionSourceMap: false
}
5.压缩css、js、图片资源,删除无用代码
5.1 css/js压缩
安装插件
npm install css-minimizer-webpack-plugin terser-webpack-plugin --save-dev
配置
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
configureWebpack: {
optimization: {
minimizer: [
new CssMinimizerPlugin(),
new TerserPlugin()
]
}
},
css: {
extract: true, //将 CSS 提取到单独的文件中
minimize: true, //启用 CSS 压缩
sourceMap: false, //禁用 CSS source map
loaderOptions: { //特定的加载器(如 Sass 或 Less)提供额外的配置
sass: {
prependData: `@import "~bootstrap/scss/functions"; @import "~bootstrap/scss/variables";`, //
},
},
},
};
5.2 图片资源压缩
安装loader
npm install --save-dev image-webpack-loader
配置
module.exports = {
...
// 配置别名
chainWebpack: config => {
// 图片压缩
config.module
.rule('images')
.exclude.add(resolve('src/assets/icons')) // 排除icons目录,这些图标已用 svg-sprite-loader 处理,打包成 svg-sprite 了
.end()
.use('url-loader')
.tap(options => ({
limit: 10240, // 稍微改大了点
fallback: {
loader: require.resolve('file-loader'),
options: {
// 在这里修改file-loader的配置
// 直接把outputPath的目录加上,虽然语义没分开清晰但比较简洁
name: 'static/img/[name].[hash:8].[ext]',
esModule: false, //低版本默认为false,高版本默认为true 这里为6.2.0为高本版本所以要手动设置为false
}
}
}))
.end()
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
mozjpeg: { progressive: true, quality: 50 }, // 压缩JPEG图像
optipng: { enabled: true }, // 压缩PNG图像
pngquant: { quality: [0.5, 0.65], speed: 4 }, // 压缩PNG图像
gifsicle: { interlaced: false } // 压缩GIF图像
})
.end()
.enforce('post') // 表示先执行配置在下面那个loader,即image-webpack-loader
},
}
四、性能优化
1.路由懒加载
路由懒加载是指在需要的时候才加载对应的路由模块,而不是在项目初始化时就加载所有的路由模块。这样可以减少初始加载时间和资源消耗,提高页面加载速度和用户体验。在 Vue 项目中,可以使用动态导入的方式来实现路由懒加载。
在路由配置文件(如 router.js)中定义路由时,使用 import 函数懒加载路由组件
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const Home = () => import('./views/Home.vue')
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
},
]
})
2.合理使用watch & conputed
1.优化 watch 的性能
- 尽量避免在 watch 中进行异步操作,因为异步操作通常会阻塞 JS 线程,影响页面性能。如果必须进行异步操作,可以使用
vm.$nextTick
或Promise.then
方法,将异步操作推迟到下一次 DOM 更新时执行。 - 在监听数组或对象等复杂数据类型时,尽量使用
deep
选项来监听其子属性的变化,而不是使用immediate
选项来立即执行回调函数。因为immediate
会立即执行回调函数,导致监听的数据被多次计算和渲染,影响页面性能。 - 避免在 watch 中执行过多的计算和渲染操作,尽量将这些操作放在
computed
属性中进行处理。
2.优化 computed 的性能
- 避免在
computed
中引用其他computed
,因为这样会导致多余的计算和渲染,降低页面性能。 - 对于数据量较大或计算量较大的
computed
,可以使用lazy
选项,将其设置为惰性计算,只有在需要计算时才会重新计算。 - 对于一些频繁变化的响应式数据,可以考虑使用
watch
来监听其变化,而不是使用computed
来计算。
3.合理使用防抖、节流函数
函数节流(throttle)与 函数防抖(debounce)都是为了限制函数的执行频次,以优化函数触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象,是应对频繁触发事件的优化方案。
4.合理使用v-if、v-show
1.v-if
- 作用:根据绑定的表达式的真假值来决定是否渲染 DOM 元素。当绑定的表达式为真值时,该元素会被渲染到页面中;当绑定的表达式为假值时,该元素不会被渲染到页面中,相当于从 DOM 树中移除该元素及其子元素。
- 优点:在条件不成立时,可以减少不必要的 DOM 元素的渲染和加载,从而提高页面的加载速度和性能表现。
- 缺点:每次切换条件,都会重新创建和销毁对应的组件或元素,因此在程序运行时可能会比
v-show
慢。
2.v-show
- 作用:根据绑定的表达式的真假值来控制元素的显示和隐藏。与
v-if
不同,v-show
并不会从 DOM 树中移除元素,而是通过修改其 CSS 样式来控制元素的显示和隐藏。 - 优点:在条件不成立时,并不会从 DOM 树中移除该元素及其子元素,因此在切换条件时,可以保留该元素的状态和属性,提高页面切换的平滑度和体验。
- 缺点:在条件不成立时,该元素仍会被加载到页面中,因此可能会增加页面的渲染和加载时间。所以,对于需要频繁切换的元素或组件,建议使用
v-show
。