blog
blog copied to clipboard
从前端工程的角度进行性能优化
举例以一个 Vue 全家桶 项目为例~
构建体积相关 + 首屏性能优化
0. 优化前使用 LightHouse(灯塔)评测各项性能指标
各项指标说明:https://mp.weixin.qq.com/s?__biz=Mzg5MDY1MjIxMA==&mid=2247496148&idx=1&sn=e7afda57d67a1d87ee0a42a907c6ef52&source=41#wechat_redirect
1. 使用 webpack-bundle-analyzer 查看冗余代码
- 配置 external 不参与打包使用外部环境依赖 (『Webpack 系列』—— externals 用法详解 : https://juejin.cn/post/6844904190083350542)
- 如 vue、vue-router、vuex 相关资源变动频率低,可以放到 cdn(建议三个资源合成一个
vue.all.runtime.js
减少请求数量)
- 如 vue、vue-router、vuex 相关资源变动频率低,可以放到 cdn(建议三个资源合成一个
- UI 组件库及其 Css 文件 配置按需引入
- 工具库 如 lodash 配置按需引入或按需导入
- 使用 unused-files-webpack-plugin 查找无用文件
2. 使用 splitChunks 插件拆分公共代码
『Webpack 系列』—— SplitChunks 插件用法详解 :https://juejin.cn/post/6844904198023168013
用 SplitChunks 插件来控制 Webpack 打包生成的 js 文件的内容的精髓就在于,防止模块被重复打包,拆分过大的 js 文件,合并零散的 js 文件。最终的目的就是减少请求资源的大小和请求次数。因这两者是互相矛盾的,故要以项目实际的情况去使用 SplitChunks 插件,需切记中庸之道。
{
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
}
}
3. 关闭 prefetch、preload
prefetch:用来告诉浏览器在页面加载完成后,利用空闲时间提前获取用户未来可能会访问的内容 preload:用来指定页面加载后很快会被用到的资源,所以在页面加载的过程中,我们希望在浏览器开始主体渲染之前尽早 preload
// 移除 preload(预载) 插件
config.plugins.delete("preload");
// 移除 prefetch(预取) 插件
config.plugins.delete("prefetch");
4. 前端开启 gzip(双端开启指南:https://cloud.tencent.com/developer/article/1507153)
打包产物新增 gzip 资源,页面加载优先使用(network 请求多出 gzip 资源)
const CompressionPlugin = require("compression-webpack-plugin");
new CompressionPlugin({
algorithm: "gzip",
test: /\.js$|\.html$|\.css$/, // 匹配文件名
minRatio: 1, // 压缩率小于1才会压缩
threshold: 10240, // 对超过10k的数据压缩
deleteOriginalAssets: false,
});
5. 图片资源优化
- 图片有损压缩
- 使用 CDN 图片
- 图片懒加载
- 雪碧图资源, 在 webpack 中,webpack-spritesmith 插件提供了自动合并雪碧图的功能并且可以自动生成对应的样式文件
6. 资源加载
- 尽量提前获取首屏需用数据 (可选 SSG 模式)
- 路由按需加载
- 组件懒加载
- css 优选加载,js 延迟加载
- 服务异步加载(弹窗类
.vue
文件异步加载)
7. 生产环境
- 过滤 console、debugger 代码
- extract 样式分离、productionSourceMap 和 sourceMap 可以关联环境,生产环境关闭,其他情况打开
建议 js、css、图片 单个文件资源大小不超过 100k
构建速度相关
1. 查看构建时间
说明:https://www.npmjs.com/package/speed-measure-webpack-plugin
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
module.exports = {
configureWebpack: (config) => {
config.plugins.push(new SpeedMeasurePlugin());
},
};
2. vue-cli-plugin-dll
npm install --save-dev vue-cli-plugin-dll
接下来就是 dll 的相关配置,将我们项目中的依赖使用 dll 插件进行动态链接,这样依赖就不会进行编译,从而极大地提高编译速度,因为这些插件没有编译,在 vue.config.js 中进行配置,也很简单
const path = require("path");
module.exports = {
pluginOptions: {
dll: {
//这里放的是你的依赖插件,就是你项目安装的其他的插件,将这些插件的名字依次加在后面,我建议将所有项目依赖插件全部放在后面
//注意这里不能放webpack,gulp等需要node环境的插件,我尝试将babel等放到这里报错提示没有V8环境
entry: ["vue", "vue-router", "view-design"],
//dll 编译后的链接库的地址
cacheFilePath: path.resolve(__dirname, "./public"),
// 是否开启 DllReferencePlugin,
open: true,
// 在执行 `dev` , `build` 等其他指令时,程序会自动将 `dll` 指令生成的 `*.dll.js` 等文件自动注入到 index.html 中。
inject: true,
},
},
};
多入口
const path = require("path");
module.exports = {
pluginOptions: {
dll: {
// 入口配置
entry: {
vue: ["vue", "vue-router", "vuex"],
ui: ["view-design"],
},
// 输出目录
output: {
path: path.join(__dirname, "public/dll"),
filename: "[name].dll.js",
// vendor.dll.js中暴露出的全局变量名
// 保持与 webpack.DllPlugin 中名称一致
library: "[name]_[hash]",
},
// 是否开启 DllReferencePlugin,
open: true,
// 在执行 `dev` , `build` 等其他指令时,程序会自动将 `dll` 指令生成的 `*.dll.js` 等文件自动注入到 index.html 中。
inject: true,
},
},
};
配置好之后然后运行,进行你上面配置插件动态链接库的编译
npx vue-cli-service dll
dll 编译完成后会在上面配置的目录下生成 dll 文件夹,就可以开始跑项目了,因为这些插件都不需要编译,跑起来很流畅,修改后的热更新速度更是显著提升。我以前修改一行代码热更新编译在 30 秒以上,使用这个以后基本十秒以内搞定。
npm run serve
3. thread-loader 多进程打包
Vue-Cli
Vue-Cli 已经内置,开启
module.exports = { parallel: true };
parallel
- Type: boolean
- Default: require('os').cpus().length > 1
是否为 Babel 或 TypeScript 使用 thread-loader。 该选项在系统的 CPU 有多于一个内核时自动启用,仅作用于生产构建。
Webpack
npm install --save-dev thread-loader
配置
const path = require("path");
module.exports = {
module: {
rules: [
{
test: /\.js$/,
include: path.resolve("src"),
use: [
"thread-loader",
// 耗时的 loader (例如 babel-loader)
],
},
],
},
};
4. import 优化
运用这个插件能在代码使用了 import 语法的情况下,大大提高代码的编译速度。
安装 babel-plugin-dynamic-import-node
npm install --save-dev babel-plugin-dynamic-import-node
vue-cli3
修改 babel.config.js 文件
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"],
env: { development: { plugins: ["dynamic-import-node"] } },
};
5. 利用缓存提升二次构建速度
Webpack 中几种缓存方式:
- cache-loader
- hard-source-webpack-plugin
以上这些缓存方式都有首次启动时的开销,即它们会让 “冷启动” 时间会更长,但是二次启动能够节省很多时间.
Vue-Cli 已经内置了 cache-loader
进行以下两个的缓存,未添加 hard-source-webpack-plugin
- babel-loader 的 cacheDirectory 标志
- vue-loader 的 cacheDirectory 标志
[
{
test: /\.vue$/,
use: [
{
loader: "project_path\\node_modules\\cache-loader\\dist\\cjs.js",
options: {
cacheDirectory: "project_path\\node_modules\\.cache\\vue-loader",
cacheIdentifier: "036da057",
},
},
{
loader: "project_path\\node_modules\\vue-loader\\lib\\index.js",
options: {
compilerOptions: {
whitespace: "condense",
},
cacheDirectory: "project_path\\node_modules\\.cache\\vue-loader",
cacheIdentifier: "036da057",
},
},
],
},
{
test: /\.m?jsx?$/,
include: ["/packages"],
exclude: [
function () {
/* omitted long function */
},
],
use: [
{
loader: "project_path\\node_modules\\cache-loader\\dist\\cjs.js",
options: {
cacheDirectory: "project_path\\node_modules\\.cache\\babel-loader",
cacheIdentifier: "5217b3ec",
},
},
{
loader: "project_path\\node_modules\\babel-loader\\lib\\index.js",
},
{
loader: "babel-loader",
},
],
},
];
提示:Vue-Cli 自带的 cache-loader 会默认为 Vue/Babel/TypeScript 编译开启。文件会缓存在 node_modules/.cache 中。如果你遇到了编译方面的问题,记得先清缓存目录之后再试试看。
HardSourceWebpackPlugin
详细说明 :https://www.npmjs.com/package/hard-source-webpack-plugin
在启动项目时会针对项目生成缓存,若是项目无 package 或其他变化,下次就不用花费时间重新构建,直接复用缓存。
安装
npm install --save-dev hard-source-webpack-plugin
配置 vue.config.js
为模块提供中间缓存,缓存路径是:node_modules/.cache/hard-source
const HardSourceWebpackPlugin = require("hard-source-webpack-plugin");
module.exports = {
configureWebpack: (config) => {
config.plugin.push(
new HardSourceWebpackPlugin({
root: process.cwd(),
directories: [],
environmentHash: {
root: process.cwd(),
directories: [],
// 配置了files的主要原因是解决配置更新,cache不生效了的问题,配置后有包的变化,plugin会重新构建一部分cache
files: ["package.json", "yarn.lock"],
},
})
);
},
};
参考
- https://cloud.tencent.com/developer/article/2125535