Appearance
性能优化篇
webpack 优化
- 利用
optimization.splitChunks
根据具体规则进行拆分,提高首屏加载速度。 Vue2 项目可以在 package.json 中添加命令"report": "vue-cli-service build --report"
。然后执行命令,就会在 dist 目录下生成一个 report.html 文件,浏览器中打开即可看到打包分析报告。然后就可以根据需要进行代码和第三方包的抽离。
js
const path = require('path')
module.exports = {
optimization: {
splitChunks: {
chunks: 'all', // all 对同步和异步代码都进行分割
cacheGroups: {
// libs: { // 最好是不要把 node_modules 里面的包都放在一起,会很大
// name: 'libs', // 文件导出名称
// test(module) {
// // 返回一个布尔值
// // `module.resource` 包含磁盘上文件的绝对路径。
// // 请注意使用 `path.sep` 而不是 / 或 \,以实现跨平台兼容性。
// return (
// module.resource &&
// module.resource.includes(`${path.sep}node_modules`)
// )
// },
// priority: -10, // 该规则的优先级,数字越大,当符合多个规则的时候,优先级越高,尽量用负数,因为默认是0
// minSize: 1, // 当文件大于此大小的时候,进行分割 ,实际开发中一般为 5 * 1024 ,即 5KB
// minChunks: 1, // 最少被引用的次数,满足此条件就会进行分割
// },
echarts: {
name: 'echarts-lib',
test(module) {
return (
module.resource &&
module.resource.includes(
`${path.sep}node_modules${path.sep}echarts`
)
)
},
priority: -8,
minSize: 1,
minChunks: 1,
},
},
},
},
}
- 利用 externals 把一些第三方包进行外部扩展,通过 cdn 的方式引入,以减轻服务器压力。另外在多页面应用中,不同的页面可能依赖于不同的库。使用 externals 可以确保每个页面只包含它实际需要的库,避免不必要的重复打包。
js
// vue.config.js
module.exports = {
configureWebpack: {
externals: {
// 把下面的第三方包全部提取到外部
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios',
'element-ui': 'ELEMENT',
echarts: 'echarts',
},
},
}
// main.js 可以不用引入 element
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="referrer" content="no-referrer" />
<meta http-equiv="Pragma" content="no-cache" />
<meta
http-equiv="cache-control"
content="no-cache, no-store, must-revalidate"
/>
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<title>深圳</title>
<link rel="stylesheet" href="https://xxx.com/lib/element-ui@2.13.2.css" />
</head>
<body>
<div id="app"></div>
<script src="https://xxx.com/lib/vue@2.6.14.min.js"></script>
<script src="https://xxx.com/lib/vuex@3.4.0.min.js"></script>
<script src="https://xxx.com/lib/vue-router@3.2.0.min.js"></script>
<script src="https://xxx.com/lib/axios@0.20.0.min.js"></script>
<script src="https://xxx.com/lib/echarts@4.9.0.min.js"></script>
<script src="https://xxx.com/lib/element-ui@2.13.2.js"></script>
</body>
</html>
使用 link 标签进行资源预加载
- dns-prefetch :要求浏览器提前执行指定网址的 DNS 查询。
html
<link rel="dns-prefetch" href="https://example.com/" />
- preconnect :要求浏览器提前与给定服务器,建立 HTTP 连接。当你知道,很快就会请求该域名时,这会很有帮助。
html
<link rel="preconnect" href="https://example.com/" />
- prerender :要求浏览器提前渲染指定链接。这样的话,用户稍后打开该链接,就会立刻显示,感觉非常快。如果确定用户下一步会访问该页面,这会很有帮助。
html
<link rel="prerender" href="https://example.com/" />
- prefetch :要求浏览器提前下载并缓存指定资源,供下一个页面使用,
没法指定预加载资源的类型
。下载后浏览器不会对资源执行任何操作,脚本未执行,样式表未应用,不会阻塞浏览器线程。它的优先级较低,浏览器可以不下载
。这意味着,浏览器可以不下载该资源,比如连接速度很慢时。
html
<link rel="prefetch" href="https://example.com/1.js" />
<link rel="prefetch" href="https://example.com/1.css" />
- preload :要求浏览器提前下载并缓存指定资源,当前页面稍后就会用到,
必须指定预加载资源的类型
。下载后浏览器不会对资源执行任何操作,脚本未执行,样式表未应用,不会阻塞浏览器线程。它的优先级较高,浏览器必须立即下载
。它只是缓存,当其他地方需要它时,它立即可用。- 可以用来预加载比较大的图片资源。
html
<link rel="preload" href="https://example.com/1.js" as="script" />
<link rel="preload" href="https://example.com/1.css" as="style" />
<link rel="preload" href="https://example.com/1.png" as="image" />
<link rel="preload" href="https://example.com/1.mp4" as="video" />
<link rel="preload" href="https://example.com/1.woff2" as="font" />
动态加载非公共文件、html 标签引入的第三方 js 和 css 文件
一些需要使用 script 和 link 标签引入的第三方文件,且不是公共文件,可以在对应的页面进行动态加载。在加载完成后,再去执行页面其他代码。通过监听 script、link 标签的 onload 事件实现。
js
// 封装动态加载的方法
function loadFile(list, groupId, cb) {
// groupId 用于判断当前整组的文件是否已加载过
// {
// url: '链接地址',
// type: '文件类型'
// }
const a = document.getElementById(groupId)
if (!!a) {
setTimeout(() => {
cb && cb()
}, 500)
return false
}
let link = null
const len = list.length
for (const i in list) {
const item = list[i]
if (item.type === 'js') {
link = document.createElement('script')
link.setAttribute('type', 'text/javascript')
link.setAttribute('src', item.url)
} else if (item.type === 'css') {
link = document.createElement('link')
link.setAttribute('rel', 'stylesheet')
link.setAttribute('type', 'text/css')
link.setAttribute('href', item.url)
}
if (i == len - 1) {
link.id = groupId
}
if (link) {
document.getElementsByTagName('head')[0].appendChild(link)
if (i == len - 1) {
link.onload = () => {
cb && cb()
}
}
}
}
}
// 使用
util.loadFile(
[
{
url: 'https://xxx.com/Cesium/Widgets/widgets.css',
type: 'css',
},
{
url: 'https://xxx.com/Cesium/Cesium.js',
type: 'js',
},
],
'#DsuperMap',
() => {
console.log('加载完成')
}
)
其他优化
- Vue 路由动态加载。
- 对于一些组件可以异步加载,仅在页面需要它渲染时才会调用加载内部实际组件的函数。
- vue2 通过 import
jsexport default { components: { MyComponent: () => import('./MyComponent.vue'), }, }
- vue3 通过 defineAsyncComponent
jsimport { defineAsyncComponent } from 'vue' const AdminPage = defineAsyncComponent(() => import('./components/AdminPageComponent.vue') )
html<template> <AdminPage /> </template>
- vue 对于一些组件初始化之后,里面的数据不会再进行变更,可以利用
v-once
和v-memo
进行优化。- v-memo 接收一个数组,空数组则跟
v-once
一样,数组有值的话,里面的内容都保持不变,则对应的内容不会重新渲染。
- v-memo 接收一个数组,空数组则跟
html
<!-- 单个元素 -->
<span v-once>This will never change: {{msg}}</span>
<!-- 带有子元素的元素 -->
<div v-once>
<h1>Comment</h1>
<p>{{msg}}</p>
</div>
<!-- 组件 -->
<MyComponent v-once :comment="msg" />
<!-- `v-for` 指令 -->
<ul>
<li v-for="i in list" v-once>{{i}}</li>
</ul>
html
<div v-memo="[valueA, valueB]"></div>
<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">
<p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p>
<p>...more child nodes</p>
</div>
对于一些分页列表,如果这个列表是纯展示,不会变更列表里的任何数据。
- vue2 可以通过 Object.freeze 冻结属性,这样就不会把数据变成响应式了。
jsconst list = await fetch('http:xxxxxxx') this.tableList = Object.freeze(list)
- vue3 可以通过
shallowRef
创建浅层的响应式数据,它不会把内部数据都变成响应式的,减少响应式开销。
jsconst state = shallowRef({ count: 1 }) const list = shallowRef([{ name: 1 }]) // 不会触发更改 state.value.count = 2 list.value[0].name = 'tom' // 会触发更改 state.value = { count: 2 } list.value = [{ name: 3 }]
开启 Gzip。
一些较大的图片使用 webp 格式。
采用 keep-alive 缓存组件
vue 中的 key 不要使用索引,方便 diff
防抖、节流
长列表优化,使用虚拟列表