1、路由懒加载
老生常谈的话题,很简单。
const router = new VueRouter({
routes: [
{ path: '/', component: () => import('./home.vue') }
]
})
2、keep-alive缓存页面
可以通过在router设置meta来区分哪些页面需要缓存
<template>
<div id="app">
<keep-alive v-if="$route.meta.keep">
<router-view/>
</keep-alive>
<router-view v-else/>
</div>
</template>
3、v-show复用dom
对于需要反复切换dom的操作,可以用v-show,比v-if更好。
4、v-for 遍历避免同时使用 v-if
<tempalte v-is="">
<div v-for="item in items" :key="item.id"></div>
</template>
5、静态长列表性能优化
对于静态展示的长列表,没有数据交互的,可以设置list数组属性为configurable为false,或者直接冻结该数组。
export default {
data: () => ({
users: []
}),
async created() {
const users = await axios.get("/api/users");
this.users = Object.freeze(users);
// 或者
},
methond: {
freezeObj(obj) {
// 传入的不是数组或者对象,则不操作
if(!typeof obj === 'object') {
return false
}
Object.defineProperties(obj, {
configurable: false
})
}
}
};
6、动态长列表性能优化
此类列表有两种优化方案:
- 时间分片
- 虚拟列表
时间分片是将动态列表切片,分段渲染。
虚拟列表是动态渲染视口展示的列表数据。这种相对是目前比较流行的做法。相关文档
我们可以采用插件来完成。
插件名:vue-virtual-scroller
插件官网:https://github.com/tangbc/vue-virtual-scroll-list
在组件中使用
<template>
<div>
// 这里是用法,这里的class要设置高度,且他包裹的元素高度要固定,不能是auto
<recycle-scroller
class="items"
:items="items" // items中传入数据
:item-size="24"
content-tag="div"
>
<template v-slot="{ item }">
// 下面是引入的自定义的滚动组件,和vue-virtual-scroller五官
<FetchItemView :item="item"/>
</template>
</recycle-scroller>
</div>
</template>
import Vue from 'vue'
import { RecycleScroller } from 'vue-virtual-scroller'
Vue.component('RecycleScroller', RecycleScroller)
export default {
data() {
return {
list: []
}
},
}
这里需要解释一下,组件直接传入了包含所有的数据数组,而不需要分批传入。首先,我们明确一点,接口请求返回数据(几千条上万条)速度都是很快的,几百毫秒。慢的是渲染过程。因此,这里是一次性的数据传入。当然,不会一次性的渲染,而是按需渲染。
7、内存泄漏问题-自定义监听事件及时销毁
1、自定义事件
vue组件上绑定的事件,在组件销毁时候是可以自动销毁的。但是一些自定义事件,比如定时器事件,监听的scroll事件等,是无法及时销毁的,这时候需要手动销毁,来防止内存泄漏。
created() {
this.timer = setInterval(this.refresh, 2000)
},
beforeDestroy() {
clearInterval(this.timer)
}
当然,我们也可以使用$once方法来清除定时器
const timer = setInterval(() =>{
// 某些定时器操作
}, 500)
this.$once('hook:beforeDestroy', () => {
clearInterval(timer);
})
这种方法将生命定时器和清除定时器写在了一块,代码看起来更清除一些。
当然,引起内存泄漏的情况有很多种。下面简单列举一些,都是开发中需要注意的。
- v-if移除的元素没有被真正释放掉
- 生命到window下的全局变量
- keep-alive的组件用deactivated来处理自定义的事件
- 闭包函数
- dom清空或删除时,事件未清除导致的内存泄漏
- 路由切换
8、图片懒加载
对于小图片,例如icon标志之类的,我们可以不做处理当然,可以的话,制作精灵图,将小图片放在一张图片中是最好的,然后通过背景图定位来展示。
那么大图片怎么优化呢,目前主流的就是懒加载。
(1)懒加载插件:vue-lazyload
<img v-lazy="/static/img/1.png">
(2)自定义图片懒加载
const imgList = document.querySelectorAll(".lazyload");
const observer = new IntersectionObserver(entries => {
entries.forEach(item => {
if (item.isIntersecting) {
// 判断在可视区了,把data-origin的值放到src
item.target.src = item.target.dataset.origin;
observer.unobserve(item.target); // 已经加载过的图片停止进行监听
}
});
});
imgList.forEach(item => observer.observe(item));
// 节流函数
function throttle(func, wait) {
let timeout
return function() {
let context = this
let args = arguments
if (!timeout) {
timeout = setTimeout(() => {
timeout = null
func.apply(context, args)
}, wait)
}
}
}
window.onscroll = throttle(loadImage, 500)
参考文档:https://mp.weixin.qq.com/s/z588pAlBa5PWGpeEQebKNQ
9、按需引入三方插件,以element为例
这里是我之前业务中的代码,直接复制过来了
import Vue from 'vue'
import './index.scss'
import {
// Basic
Row,
Col,
Button,
ButtonGroup,
// Form
Radio,
RadioGroup,
Checkbox,
CheckboxGroup,
Input,
Autocomplete,
InputNumber,
Select,
Option,
OptionGroup,
DatePicker,
Upload,
Form,
FormItem,
// Data
Table,
TableColumn,
Progress,
Tree,
Pagination,
Tag,
// Notice
Alert,
Loading,
Message,
MessageBox,
// Navigation
Menu,
Submenu,
MenuItem,
MenuItemGroup,
Tabs,
TabPane,
Dropdown,
DropdownMenu,
DropdownItem,
Steps,
Step,
// Others
Dialog,
Tooltip,
Popover,
Scrollbar,
Card
} from 'element-ui'
const components = [
// Base
Row,
Col,
Button,
ButtonGroup,
// Form
Radio,
RadioGroup,
Checkbox,
CheckboxGroup,
Input,
Autocomplete,
InputNumber,
Select,
Option,
OptionGroup,
DatePicker,
Upload,
Form,
FormItem,
// Data
Table,
TableColumn,
Progress,
Tree,
Pagination,
Tag,
// Notice
Alert,
// Navigation
Menu,
Submenu,
MenuItem,
MenuItemGroup,
Tabs,
TabPane,
Dropdown,
DropdownMenu,
DropdownItem,
Steps,
Step,
// Others
Dialog,
Tooltip,
Popover,
Scrollbar,
Card
]
// 循环注册全局组件
components.forEach(item => {
Vue.use(item)
})
Vue.use(Loading.directive)
Vue.prototype.$loading = Loading.service
Vue.prototype.$msgbox = MessageBox
Vue.prototype.$alert = MessageBox.alert
Vue.prototype.$confirm = MessageBox.confirm
Vue.prototype.$message = Message
10、无状态组件声明为函数式
使组件无状态 (没有 data
) 和无实例 (没有 this
上下文)。他们用一个简单的 render
函数返回虚拟节点使它们渲染的代价更小。
声明组件为functional
<template functional>
<div class="cell">
<div v-if="props.value" class="on"></div>
<section v-else class="off"></section>
</div>
</template>
<script>
export default {
props: ['value']
}
</script>
11、子组件的分割
对于功能状态等完全独立的两个模块可以且分为子组件,便于代码的维护。
12、变量本地化
对于一些反复使用,但是无需进行响应式的数据,我们可以放在computed计算属性中。
参考视频:
点击查看【bilibili】
参考文档:https://zhuanlan.zhihu.com/p/111310865
13、vue打包时候后的优化
happypack + dll
happypack插件负责开启多个node子进程,来达到加快编译速度的目的。
dll用来剥离第三方插件,例如vue、vue-router(一切非业务的组件)。来进行提前打包,以保证我们打包的时候只进行业务代码的打包。
14、首页加载优化
具体方法有骨架屏,ssr渲染首页等