Vue 性能优化 和 最佳实践
说说 Vue 项目相关的性能优化
编码阶段
详细信息
- 对象层级不能过深(数据递归等,影响性能)
- 减少 data 中的数据(会增加 getter 和 setter,会收集对应的 watcher)
- 确定不需要响应式数据,可不放到 data 中,单独存储(如 Object.freeze())
- 如果使用 v-for 给每项元素绑定事件,可改为使用事件委托
- SPA 页面有效采用 keep-alive 缓存组件
- 区分场景,正确使用 v-if、v-show
- 区分场景,正确使用 computed 和 watch
- v-for 遍历时加 key,最好为 id,且 key 值唯一
- 使用异步组件,提升用户交互
- 图片懒加载
- 路由懒加载,按需加载
- 第三方插件尽可能按需引入
- 考虑 DOM 操作性能 - 虚拟列表 / 虚拟表格
- 防止内存泄露,组件销毁要把全局变量和事件销毁
- 使用路由懒加载
- 节流、防抖的运用
打包优化
详细信息
- 代码压缩、混淆
- Tree Shaking 模块按需加载
- 使用 Scope Hoisting(作用域提升)Webpack 配置 ModuleConcatenationPlugin
- 使用 cdn 加载第三方模块
- 多线程打包 happypack
- splitChunks 抽离公共文件
- sourceMap 策略
请求相关优化
详细信息
- 客户端缓存
- 服务端缓存
- 开启 gzip
用户体验
详细信息
- 白屏、首屏优化
- 使用骨架屏 Skeleton
- PWA
其他方案
详细信息
- 预渲染
- 服务端渲染 SSR
属性
组件中的 name 属性有什么用
详细信息
- 项目使用 keep-alive 时,可搭配组件 name 进行缓存过滤
- DOM 做递归组件时需要调用自身 name
- Vue-devtools 调试工具里显示的组见名称是由 Vue 中组件 name 决定的
- 动态切换组件
Vue 中的 Key 的作用是什么
详细信息
key 的作用主要是为了高效的更新虚拟 DOM
。另外 vue 中在使用相同标签名元素的过渡切换
时,也会使用到 key 属性,其目的也是为了让 vue 可以区分它们
,否则 vue 只会替换其内部属性而不会触发过渡效果。
用过 is 吗,有什么作用
详细信息
解决 html 模板的限制
比如 ul 里面嵌套 li 的写法是 html 语法的固定写法,如果想在 ul 里面嵌套自己的组件,但是 html 在渲染 dom 的时候,组件对 ul 来说并不是有效的 dom
<ul>
<li is="my-component"></li>
</ul>
2
3
动态组件 - 用于组件切换
componentName 可以是在本页面已经注册的局部组件名和全局组件名, 也可以是一个组件的选项对象。当控制 componentName 改变时就可以动态切换选择组件。
<component :is="componentName"></component>
场景
在 Vue 中要获取当前时间你会放到 computed 还是 methods 里
详细信息
放在 computed 里面。
- 因为 computed 只有在它的相关依赖发生改变时才会重新求值
- 相比而言,方法只要发生重新渲染,methods 调用总会执行所有函数
什么使用需要用到 vuex
详细信息
为什么需要 vuex
由于
组件只维护自身的状态
(data),组件创建时或者路由切换时,组件会被初始化
,从而导致 data 也随之销毁。什么场景下用到 vuex
如果是 vue 的小型应用,那么没有必要使用 vuex,这个时候使用 vuex 反而会带来负担。组件之间的状态传递使用 props、自定义事件来传递即可。 但是如果涉及到 vue 的大型应用,那么就需要类似于 vuex 这样的
集中管理状态的状态机来管理所有组件的状态
。例如登录状态、加入购物车、音乐播放等,总之只要是开发 vue 的大型应用都推荐使用vuex 来管理所有组件状态
。
使用 vue 渲染大量数据时应该怎么优化?说下你的思路
详细信息
渲染大量数据,此时很容易出现卡顿的情况。比如大数据量的表格、树、多项下拉选项等,在处理时需根据不同情况做相应处理:
可以采取分页(或到底部刷新)的方式获取,
避免渲染大量数据
使用虚拟列表滚动等解决方案,只
渲染视口范围内的数据
若确定数据不需要更新的场景,可以使用
v-once
方式只渲染一次
可以采用懒加载的方式,在用户
需要的时候再加载数据
,比如 tree 组件子树的懒加载、Table 二级表格当用户点击时再获取数据,级联选择的二级、三级(数据较多时)用户点击后再获取更多数据更多等待补充
Vue data 中某一个属性的值发生改变后,视图会立即同步执行重新渲染吗
详细信息
不会立即同步执行重新渲染。Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。Vue 在更新 DOM 时是异步执行的
。只要侦听到数据变化
, Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。
如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环tick中
,Vue 刷新队列并执行实际(已去重的)工作。
样式
Vue 初始化页面闪动问题
/* 如类似于 {{ msg }} 这样的出现在页面上 */
[v-cloak] {
display: none;
}
2
3
4
scoped 样式隔离的原理
详细信息
在当前组件的 .vue 文件中,如果 style 标签加了 scoped
属性,那么在组件渲染为 DOM 时,会对每个组件中的 DOM 元素添加格式为:data-v-[hash:8]
的属性,然后该组件的所有选择器也会添加上对应的[data-v-[hash:8]
属性选择器来只对自身组件产生影响
,以此来实现样式隔离。
scoped 是如何实现样式穿透的
首先说一下什么场景下需要 scoped 样式穿透
当项目引用了第三方组件,需要在组件中局部修改第三方组件的样式,而又不想去除 scoped 属性造成组件之间的样式污染。此时只能通过特殊的方式,scoped 样式穿透。
有三种常用的方法来实现样式穿透:
- 使用
::v-deep
操作符( >>> 的别名);vue3 为:deep
使用 scoped 标签
,再用一个不写 scoped 的来添加样式覆盖- 在组件的外层 DOM 上添加唯一的 class 来区分不同组件,在书写样式时就可以正常针对针对这部分 DOM 书写样式
如何修改组件样式又不去掉 scoped
详细信息
样式穿透,使用 /deep/ , vue3 为 :deep
使用两个 style 标签
函数式组件
函数式组件原理
详细信息
- 函数式组件需要在声明组件是指定
functional:true
不需要实例化
,所以没有 this,this 通过 render 函数的第二个参数 context 来代替没有生命周期
钩子函数,不能使用计算属性,watch不能通过 $emit 对外暴露事件
,调用事件只能通过 context.listeners.click 的方式调用外部传入的事件- 因为函数式组件是没有实例化的,所以在外部通过 ref 去引用组件时,
实际引用的是 HTMLElement
- 函数式组件的 props 可以不用显示声明,所以没有在 props 里面声明的属性都会被自动隐式解析为 prop,而普通组件所有未声明的属性都解析到 $attrs 里面,并自动挂载到组件根元素上面(可以通过 inheritAttrs 属性禁止)
函数式组件的优势及使用场景
详细信息
优势:
- 由于函数式组件
不需要实例化
,无状态
,没有生命周期
,所以渲染性能要好于普通组件 - 函数式组件结构比较简单,代码结构更清晰
使用场景:
- 一个简单的展示组件,作为容器组件使用 比如
router-view
就是一个函数式组件 - “高阶组件”——用于接收一个组件作为参数,返回一个被包装过的组件
构建
assets 和 static 的区别
详细信息
相同点:存放静态资源文件
不同点: 1.assets:走打包压缩流程
,压缩后的文件会放置在 static 文件中跟着 index.html 一同上传至服务器 2.static:不走打包压缩
等流程,而是直接进入打包好的目录,直接上传至服务器(打包效率高但体积大
)
建议:
- 将项目中 template 需要的样式文件、js 文件等都可以放置在 assets 中
- 项目引入的第三方资源文件如 iconfoont.css 等放置在 static 中(第三方文件已经被处理过了)