vue项目优化技巧【重点】
# 简介
Vue 框架通过数据双向绑定和虚拟 DOM 技术,帮我们处理了前端开发中最脏最累的 DOM 操作部分, 我们不再需要去考虑如何操作 DOM 以及如何最高效地操作 DOM;但 Vue 项目中仍然存在项目首屏优化、Webpack 编译配置优化等问题,所以我们仍然需要去关注 Vue 项目性能方面的优化,使项目具有更高效的性能、更好的用户体验。
# 优化角度
# 运行时性能优化
# 代码层面的优化
- data优化;不要将所有的数据放在data中,data中的数据都会增加getter和setter,会收集对应的watcher;
v-if
当值为false
时内部指令不会执行,具有阻断功能,很多情况下使用v-if
替代v-show
;、- 合理使用
computed
、watch
及方法; vue
在v-for
时给每项元素绑定事件需要用事件代理;key
确保唯一性(默认vue
采用就地复用策略);- 长列表只显示的话,
Object.freeze
冻结数据; SPA
页面采用keep-alive
缓存组件;- 扁平化 Store 数据结构;
- 数据持久化的问题(防抖,节流);
- 事件的销毁;
- 拆分组件(提高复用性、增加代码的可维护性、减少不必要的渲染);
- 尽量采用runtime运行时版本;
# Webpack层面的优化
详见【webpack的配置及使用】中的优化介绍;
# 打包优化
- 使用
cdn
的方式加载第三方模块,压缩大小; - 多线程打包
happypack
; splitChunks
抽离公共文件;sourceMap
生成;- 对于 Webpack4,打包项目使用 production 模式,这样会自动开启代码压缩
- 使用 ES6 模块来开启 tree shaking,这个技术可以移除没有使用的代码
- 优化图片,对于小图可以使用 base64 的方式写入文件中
- 按照路由拆分代码,实现按需加载
- 给打包出来的文件名添加哈希,实现浏览器缓存文件
# 开发环境
- HRM (热替换)
- webpack-dev-server (本地服务器)
- soure-map (调试)
- webpack-bundle-analyzer(打包生成代码块分析视图)
- size-plugin(监控打包资源的体积变量化)
- speed-measure-webpack-plugin(分析loader和plugin打包的耗时)
# 生产环境
# 体积优化
- css提取(mini-css-extract-plugin)
- css压缩 (optimize-css-assets-webpack-plugin)
- html压缩 (html-webpack-plugin )
- externals (排除不需要被打包的第三方)
- js压缩 (production模式自动开启)
- tree-shake ( production模式自动开启(webpack4限EsModule;webpack5不限EsModule,CommonJs,优秀得很) )
- code-split ( optimization )
- import(懒加载,预加载(预加载慎用))
# 打包速度优化
- 多线程打包(thread-loader 、happyPack)
- 动态链 ( DLL )
- babel缓存( 缓存cacheDirectory )
- exclude / exclude (排除一些不需要编译的文件)
- module.noParse (排除不需要被loader编译的第三方库)
# 加载时性能优化
- 第三方模块按需导入(
babel-plugin-component
),尽量不要全局引入; - 使用更轻量级的工具库;
- vue路由组件懒加载;
- 滚动到可视区域动态加载(
vue-virtual-scroll-list
);始终加载上中下屏;优化无限列表性能; - 图片懒加载(
vue-lazyload
);
# 用户体验优化
# SEO
优化
- 预渲染插件
prerender-spa-plugin
; - 服务器渲染
ssr
;
# 加载
- 骨架屏
app-skeleton
; - app壳
app-shell
; - service worker
pwa
;
# 基础的 Web 技术的优化
# 缓存压缩优化
- 开启
gzip
压缩; - 浏览器缓存、服务端缓存;
# 详细分析
# 运行时性能优化
# 代码层面的优化
# 不要将所有的数据放在data中,data中的数据都会增加getter和setter,会收集对应的watcher;
# 拆分组件(提高复用性、增加代码的可维护性、减少不必要的渲染);
#
v-if
当值为false
时内部指令不会执行,具有阻断功能,很多情况下使用v-if
替代v-show
;
v-if 是 真正 的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
v-show 就简单得多, 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 display 属性进行切换。
v-if
适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show
则适用于需要非常频繁切换条件的场景;
# 合理使用
computed
、watch
及方法;
computed: 是计算属性,依赖其它属性值(A),并且 computed 的值有缓存(B),只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;
运用场景:
- 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
- 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
#
key
确保唯一性(默认vue
采用就地复用策略);
v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
(1)v-for 遍历必须为 item 添加 key
在列表数据进行遍历渲染时,需要为每一项 item 设置唯一 key 值,方便 Vue.js 内部机制精准找到该条列表数据。当 state 更新时,新的状态值和旧的状态值对比,较快地定位到 diff 。
(2)v-for 遍历避免同时使用 v-if
不推荐同时使用 v-if
和 v-for
; 当 v-if
与 v-for
一起使用时,v-for
具有比 v-if
更高的优先级。这意味着 v-if
将分别重复运行于每个 v-for
循环中。
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
<!-- 上面的代码将只渲染未完成的 todo -->
<!-- 优化后 -->
<!--而如果你的目的是有条件地跳过循环的执行,那么可以将 v-if 置于外层元素 (或 <template>)上。-->
<ul v-if="todos.length">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
<p v-else>No todos left!</p>
2
3
4
5
6
7
8
9
10
11
12
13
v-for 比 v-if 优先级高,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候,必要情况下应该替换成 computed 属性。
<ul>
<li
v-for="user in activeUsers"
:key="user.id">
{{ user.name }}
</li>
</ul>
computed: {
activeUsers: function () {
return this.users.filter(function (user) {
return user.isActive
})
}
}
//不推荐:
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id">
{{ user.name }}
</li>
</ul>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#
vue
在v-for
时给每项元素绑定事件需要用事件代理;
# 长列表只显示的话,
Object.freeze
冻结数据;
Vue 会通过 Object.defineProperty 对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 Vue 来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,那如何禁止 Vue 劫持我们的数据呢?可以通过 Object.freeze 方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了。对于长列表还可以通过分页的优化;
export default {
data: () => ({
users: {}
}),
async created() {
const users = await axios.get("/api/users");
this.users = Object.freeze(users);
}
};
2
3
4
5
6
7
8
9
#
SPA
页面采用keep-alive
缓存组件;
# 扁平化 Store 数据结构
手动去把接口里的信息通过类似数据的表一样像这样存起来,也可以借助一些工具,这里就需要提到一个概念叫做 JSON数据规范化(normalize)
, Normalizr 是一个开源的工具,可以将上面的深层嵌套的 JSON 对象通过定义好的 schema 转变成使用 id 作为字典的实体表示的对象。
需要了解更多请参考 normalizr 的文档 github.com/paularmstro… (opens new window)
{
"id": "123",
"author": {
"id": "1",
"name": "Paul"
},
"title": "My awesome blog post",
"comments": [
{
"id": "324",
"commenter": {
"id": "2",
"name": "Nicole"
}
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
假如直接把这样的结构存储在 store 中,如果想修改某个 commenter 的信息,我们需要一层层去遍历找到这个用户的信息,同时有可能这个用户的信息出现了多次,还需要把其他地方的用户信息也进行修改,每次遍历的过程会带来额外的性能开销。
假设我们把用户信息在 store 内统一存放成 users[id]
这样的结构,修改和读取用户信息的成本就变得非常低。
举个例子,针对上面的 JSON 数据,我们定义 users
comments
articles
三种 schema:
import {normalize, schema} from 'normalizr';
// 定义 users schema
const user = new schema.Entity('users');
// 定义 comments schema
const comment = new schema.Entity('comments', {
commenter: user,
});
// 定义 articles schema
const article = new schema.Entity('articles', {
author: user,
comments: [comment],
});
const normalizedData = normalize(originalData, article);
2
3
4
5
6
7
8
9
10
11
12
13
normalize 之后就可以得到下面的数据,我们可以按照这种形式存放在 store 中,之后想修改和读取某个 id 的用户信息就变得非常高效了,时间复杂度降低到了 O(1)。
{
result: "123",
entities: {
"articles": {
"123": {
id: "123",
author: "1",
title: "My awesome blog post",
comments: [ "324" ]
}
},
"users": {
"1": { "id": "1", "name": "Paul" },
"2": { "id": "2", "name": "Nicole" }
},
"comments": {
"324": { id: "324", "commenter": "2" }
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 数据持久化的问题(防抖,节流);
避免持久化 Store 数据带来的性能问题
当你有让 Vue App 离线可用,或者有接口出错时候进行灾备的需求的时候,你可能会选择把 Store 数据进行持久化,这个时候需要注意以下几个方面:
持久化时写入数据的性能问题
Vue 社区中比较流行的 vuex-persistedstate,利用了 store 的 subscribe 机制,来订阅 Store 数据的 mutation,如果发生了变化,就会写入 storage 中,默认用的是 localstorage 作为持久化存储。
也就是说默认情况下每次 commit 都会向 localstorage 写入数据,localstorage 写入是同步的,而且存在不小的性能开销,如果你想打造 60fps 的应用,就必须避免频繁写入持久化数据
我们应该尽量减少直接写入 Storage 的频率:
- 多次写入操作合并为一次,比如采用函数节流或者将数据先缓存在内存中,最后在一并写入
- 只有在必要的时候才写入,比如只有关心的模块的数据发生变化的时候才写入
避免持久化存储的容量持续增长
由于持久化缓存的容量有限,比如 localstorage 的缓存在某些浏览器只有 5M,我们不能无限制的将所有数据都存起来,这样很容易达到容量限制,同时数据过大时,读取和写入操作会增加一些性能开销,同时内存也会上涨。
尤其是将 API 数据进行 normalize 数据扁平化后之后,会将一份数据散落在不同的实体上,下次请求到新的数据也会散落在其他不同的实体上,这样会带来持续的存储增长。
因此,当设计了一套持久化的数据缓存策略的时候,同时应该设计旧数据的缓存清除策略,例如请求到新数据的时候将旧的实体逐个进行清除。
# 事件的销毁;
Vue 组件销毁时,会自动清理它与其它实例的连接,解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。 如果在 js 内使用 addEventListener
等方式是不会自动销毁的,我们需要在组件销毁时手动移除这些事件的监听,以免造成内存泄露,如:
created() {
addEventListener('click', this.click, false)
},
beforeDestroy() {
removeEventListener('click', this.click, false)
}
2
3
4
5
6
相同事件绑定和解除,需要使用共用函数;绑定和解除事件时 事件没有"on" 即onclick写成click; 共用函数不能带参数;
document.body.addEventListener('touchmove', function (event) {
event.preventDefault();
},false);
document.body.removeEventListener('touchmove', function (event) {
event.preventDefault();
},false);
//以上为错误写法;下面为正确写法;
function bodyScroll(event){
event.preventDefault();
}
document.body.addEventListener('touchmove',bodyScroll,false);
document.body.removeEventListener('touchmove',bodyScroll,false);
2
3
4
5
6
7
8
9
10
11
12
# 尽量采用runtime运行时版本;
引入生产环境的 Vue 文件
开发环境下,Vue 会提供很多警告来帮你对付常见的错误与陷阱。而在生产环境下,这些警告语句没有用,反而会增加应用的体积。有些警告检查还有一些小的运行时开销。
当使用 webpack 或 Browserify 类似的构建工具时,Vue 源码会根据 process.env.NODE_ENV 决定是否启用生产环境模式,默认情况为开发环境模式。在 webpack 与 Browserify 中都有方法来覆盖此变量,以启用 Vue 的生产环境模式,同时在构建过程中警告语句也会被压缩工具去除。
使用单文件组件预编译模板
当使用 DOM 内模板或 JavaScript 内的字符串模板时,模板会在运行时被编译为渲染函数。通常情况下这个过程已经足够快了,但对性能敏感的应用还是最好避免这种用法。
预编译模板最简单的方式就是使用单文件组件——相关的构建设置会自动把预编译处理好,所以构建好的代码已经包含了编译出来的渲染函数而不是原始的模板字符串。
提取组件的 CSS 到单独到文件
当使用单文件组件时,组件内的 CSS 会以 `` 标签的方式通过 JavaScript 动态注入。这有一些小小的运行时开销,将所有组件的 CSS 提取到同一个文件可以避免这个问题,也会让 CSS 更好地进行压缩和缓存。
查阅这个构建工具各自的文档来了解更多:
- webpack + vue-loader (opens new window) (
vue-cli
的 webpack 模板已经预先配置好)
# Webpack层面的优化
# 开发环境
- HRM (热替换)
- webpack-dev-server (本地服务器)
- soure-map (调试)
- webpack-bundle-analyzer(打包生成代码块分析视图)
- size-plugin(监控打包资源的体积变量化)
- speed-measure-webpack-plugin(分析loader和plugin打包的耗时)
# 生产环境
# 体积优化
- css提取(mini-css-extract-plugin)
- css压缩 (optimize-css-assets-webpack-plugin)
- html压缩 (html-webpack-plugin )
- externals (排除不需要被打包的第三方)
- js压缩 (production模式自动开启)
- tree-shake ( production模式自动开启(webpack4限EsModule;webpack5不限EsModule,CommonJs,优秀得很) )
- code-split ( optimization )
- import(懒加载,预加载(预加载慎用))
# 打包速度优化
- 多线程打包(thread-loader 、happyPack)
- 动态链 ( DLL )
- babel缓存( 缓存cacheDirectory )
- exclude / exclude (排除一些不需要编译的文件)
- module.noParse (排除不需要被loader编译的第三方库)
# 使用
cdn
的方式加载第三方模块;
# 多线程打包
happypack
;
#
splitChunks
抽离公共文件;
#
sourceMap
生成;
生成环境不生成 map 文件;找到 config/index.js,修改为 productionSourceMap: false
# 加载性能优化
# 第三方模块按需导入(
babel-plugin-component
);
在项目中经常会需要引入第三方插件,如果我们直接引入整个插件,会导致项目的体积太大,我们可以借助 babel-plugin-component
,然后可以只引入需要的组件,以达到减小项目体积的目的。以下为项目中引入 element-ui 组件库为例:
//安装
npm install babel-plugin-component -D
//将 .babelrc 修改为
{
"presets": [["es2015", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
//在 main.js 中引入部分组件:
import Vue from 'vue';
import { Button, Select } from 'element-ui';
Vue.use(Button)
Vue.use(Select)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 使用更轻量级的工具库;
# vue路由组件懒加载;合理使用路由懒加载,异步组件;
通过组件懒加载方案可以优化初始渲染的运行性能,其实,这对于优化应用的加载性能也很有帮助。
组件粒度的懒加载结合异步组件和 webpack 代码分片,可以保证按需加载组件,以及组件依赖的资源、接口请求等,比起通常单纯的对图片进行懒加载,更进一步的做到了按需加载资源。
Vue 是单页面应用,可能会有很多的路由引入 ,这样使用 webpcak 打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面的速度就会降下来。
const Foo = () => import('./Foo.vue')
const router = new VueRouter({
routes: [
{ path: '/foo', component: Foo }
]
})
import Vue from 'vue'
import Router from 'vue-router'
//import Login from '../components/login/login.vue'
const login = () => import(/* webpackChunkName: "login" */ '../pages/login')
Vue.use(Router)
const routes = [{
path: '/login',
name: '登陆',
component: login,
meta: { requiresAuth: false }
}]
const router = new Router({
mode: 'history',
base: __dirname,
routes
})
export default router
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 滚动到可视区域动态加载(
vue-virtual-scroll-list
);始终加载上中下屏;优化无限列表性能
如果你的应用存在非常长或者无限滚动的列表,那么需要采用 窗口化 的技术
来优化性能,只需要渲染少部分区域的内容,减少重新渲染组件和创建 dom 节点的时间。 你可以参考以下开源项目 vue-virtual-scroll-list (opens new window) 和 vue-virtual-scroller (opens new window) 来优化这种无限列表的场景的。你也可以参考 Google 工程师的文章Complexities of an Infinite Scroller (opens new window) 来尝试自己实现一个虚拟的滚动列表来优化性能,主要使用到的技术是 DOM 回收、墓碑元素和滚动锚定。
# 图片懒加载(
vue-lazyload
);
对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。这样对于页面加载性能上会有很大的提升,也提高了用户体验。我们在项目中使用 Vue 的 vue-lazyload (opens new window)插件:
import VueLazyload from 'vue-lazyload'
//配置一:然后再 vue 中直接使用
Vue.use(VueLazyload)
//配置二:或者添加自定义选项
Vue.use(VueLazyload, {
preLoad: 1.3,
error: 'dist/error.png',
loading: 'dist/loading.gif',
attempt: 1
})
//使用;在 vue 文件中将 img 标签的 src 属性直接改为 v-lazy ,从而将图片显示方式更改为懒加载显示:
<img v-lazy="/static/img/1.png">
//template:
<ul>
<li v-for="img in list">
<img v-lazy="img.src" >
</li>
</ul>
//use v-lazy-container work with raw HTML
<div v-lazy-container="{ selector: 'img' }">
<img data-src="//domain.com/img1.jpg">
</div>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 用户体验优化
# SEO
优化
# 预渲染插件
prerender-spa-plugin
;
# 服务器渲染
ssr
;
首页单独做服务端渲染;利用服务端渲染(SSR)和预渲染(Prerender)来优化加载性能
一些概念
- 服务端渲染:用户访问 url,服务端根据访问路径请求所需数据,拼接成 html 字符串,返回给前端。前端接收到 html 时已有部分内容;
- 预渲染:构建阶段生成匹配预渲染路径的 html 文件(注意:每个需要预渲染的路由都有一个对应的 html)。构建出来的 html 文件已有部分内容。
- 客户端渲染:用户访问 url,请求 html 文件,前端根据路由动态渲染页面内容。关键链路较长,有一定的白屏时间;
服务端渲染、预渲染和客户端渲染的对比
这种情况下可以采用服务端渲染(SSR)和预渲染(Prerender)来提升加载性能,这两种方案,用户读取到的直接就是网页内容,由于少了节省了很多 RTT(往返时延),同时,还可以对一些资源内联在页面,可以进一步提升加载的性能。
如果项目的 SEO 和 首屏渲染是评价项目的关键指标,那么项目就需要服务端渲染来帮助实现最佳的初始加载性能和 SEO
# 加载
# 骨架屏
app-skeleton
;
通过组件懒加载优化超长应用内容初始渲染性能
上面提到的无限列表的场景,比较适合列表内元素非常相似的情况,不过有时候,你的 Vue 应用的超长列表内的内容往往不尽相同,例如在一个复杂的应用的主界面中,整个主界面由非常多不同的模块组成,而用户看到的往往只有首屏一两个模块。在初始渲染的时候不可见区域的模块也会执行和渲染,带来一些额外的性能开销。
使用组件懒加载在不可见时只需要渲染一个骨架屏,不需要真正渲染组件
可以对组件直接进行懒加载,对于不可见区域的组件内容,直接不进行加载和初始化,避免初始化渲染运行时的开销。具体可以参考专栏文章 性能优化之组件懒加载: Vue Lazy Component 介绍 (opens new window),了解如何做到组件粒度的懒加载。
# app壳
app-shell
;
# service worker
pwa
;
# 基础的 Web 技术的优化
# 缓存压缩优化
# 开启
gzip
压缩;
gzip 是 GNUzip 的缩写,最早用于 UNIX 系统的文件压缩。HTTP 协议上的 gzip 编码是一种用来改进 web 应用程序性能的技术,web 服务器和客户端(浏览器)必须共同支持 gzip。
目前主流的浏览器,Chrome,firefox,IE等都支持该协议。常见的服务器如 Apache,Nginx,IIS 同样支持,gzip 压缩效率非常高,通常可以达到 70% 的压缩率,也就是说,如果你的网页有 30K,压缩之后就变成了 9K 左右;
重启服务,观察网络面板里面的 response header,如果看到如下红圈里的字段则表明 gzip 开启成功
//服务器压缩
var compression = require('compression');
var app = express();
app.use(compression())
//nginx中的设置; 详见【nginx运维部署相关】设置; 推荐在nginx中统一设置;
2
3
4
5
gzip on;
gzip_http_version 1.1;
gzip_comp_level 5;
gzip_min_length 1000;
gzip_types text/csv text/xml text/css text/plain text/javascript application/javascript application/x-javascript application/json application/xml;
gzip_disable "MSIE [1-6]\."; # IE6无效
2
3
4
5
6
# 浏览器缓存、服务端缓存;
详见【http协议】中缓存的介绍;
为了提高用户加载页面的速度,对静态资源进行缓存是非常必要的,根据是否需要重新向服务器发起请求来分类,将 HTTP 缓存规则分为两大类(强制缓存,对比缓存)
版本 | 内容 |
---|---|
http0.9 | 只允许客户端发送 GET 这一种请求;且不支持请求头,协议只支持纯文本;无状态性,每个访问独立处理,完成断开;无状态码 |
http1.0 | 解决 0.9 的缺点,增加 If-modify-since(last-modify)和 expires 缓存属性 |
http1.x | 增加 cache-control 和 If-none-match(etag)缓存属性 |
http2.0 | 采用二进制格式传输;多路复用;报头压缩;服务器推送 |
http3.0 | 采用 QUIC 协议,自定义连接机制;自定义重传机制;无阻塞的多路复用 |
tips:
- 请求头中如果有 If-Modified-Since,服务器会将时间与 last-modified 对比,相同返回 304;
- expires 是响应头内容,返回一个固定的时间, 缺陷是时间到了服务器要重新设置;
常用请求头:1.请求和响应报文的通用Header;2.常用的响应Header;
# 缓存的优点
- 减少了冗余的数据传递,节省宽带流量
- 减少了服务器的负担,大大提高了网站性能
- 加快了客户端加载网页的速度 这也正是HTTP缓存属于客户端缓存的原因。
# 缓存流程图
- 会先判断强缓存,Expires, Cache-Control(max-age) no-cache=true, public ;
- 再判断协商缓存
etag
及last-modified
是否存在;存在利用属性 If-None-match(etag)If-Modified-since(last-modified)携带值(这一步叫做数据签名); - 请求服务器,服务器对比 etag(last-modified),生效返回 304;
# 不同刷新的请求执行过程
# 定位 Vue 应用性能问题
Vue 应用的性能问题可以分为两个部分,第一部分是运行时性能问题,第二部分是加载性能问题。
# Chrome Devtool/TODO
和其他 web 应用一样,定位 Vue 应用性能问题最好的工具是 Chrome Devtool,通过 Performance 工具可以用来录制一段时间的 CPU 占用、内存占用、FPS 等运行时性能问题,通过 Network 工具可以用来分析加载性能问题。
例如,通过 Performance 工具的 Bottom Up 标签我们可以看出一段时间内耗时最多的操作,这对于优化 CPU 占用和 FPS 过低非常有用,可以看出最为耗时的操作发生在哪里,可以知道具体函数的执行时间,定位到瓶颈之后,我们就可以做一些针对性的优化。
更多 Chrome Devtool 使用方式请参考使用 Chrome Devtool 定位性能问题 的指南 (opens new window)
# 参考链接
https://juejin.cn/post/6922641008106668045