vue项目的一些优化
以上记录了vue在各个方面的优化,对于webpackp的优化在学完webpackp后完善。
前言
主要记录vue项目的一些优化
一、渲染优化
v-for
避免v-if和v-for的同级使用,v-for的优先级比v-if高,会导致数据渲染错误
v-for设置key的值,尽量不适用index,使用数据中唯一的标识,有利于dom的定位与diff。
v-show和v-if的选择
经常复用的组件用v-show来渲染(v-show是隐藏不销毁)
相反则用v-if(直接判断是否创建)
长列表优化
纯粹做数据展示,不需要热更新
处于data中的数据会被监视,发生变化时数据就发生变化
所以采用object.freeze(数据)方法冻结数据。
长列表
采用虚拟滚动,只渲染少部分区域的内容
只渲染视口部分的数据,也就是说渲染的DOM节点个数是固定的
页面:
<template>
<div>
<div>
<span v-if="list.length===0">暂无数据!!!</span>
<div v-else class="box" :style="`height:${viewH}px;overflow-y:scroll;`" @scroll="handleScroll">
<ul>
<li :style="`transform:translateY(${offsetY}px); height:${itemH}px;`" v-for='i in clist' :key="i">{{ i }}</li>
</ul>
</div>
</div>
<button @click="gotoCSSLongList" class="but">CssLongList</button>
</div>
</template>
逻辑:
<script>
export default {
name: "LongListOne",
data() {
return {
list: [],//上万条总数据
clist: [],// 页面展示数据
viewH: 500, // 可视box高度
itemH: 60, // 单项高度
scrollH: '', // 整个滚动列表高度
showNum: '',//可视化高度一次能装几个列表
offsetY: 0// 动态偏移量
}
},
mounted() {
for (let index = 0; index < 100000; index++) {// 10万条数据
this.list.push(index)
}
this.scrollH = this.list.length * this.itemH;// 计算总高度
// 计算可视化高度一次能装几个列表, 多设置几个防止滚动时候直接替换
this.showNum = Math.floor(this.viewH / this.itemH) + 4
// 默认展示几个
this.clist = this.list.slice(0, this.showNum);
this.lastTime = new Date().getTime()
},
computed: {},
methods: {
// 滚动监视器
handleScroll(e) {
let {lastTime,itemH,list}=this
if (new Date().getTime() - lastTime > 10) {
let scrollTop = e.target.scrollTop // 滚去的高度
// 每一次滚动后根据scrollTop值获取一个可以整除itemH结果进行偏移
// 例如: 滚动的scrllTop = 1220 1220 % this.itemH = 20 offsetY = 1200
this.offsetY = scrollTop - (scrollTop % itemH)
//上面卷掉了多少,就要往下平移多少,不然showNum滚出可视区外了
this.clist = list.slice(
Math.floor(scrollTop / itemH), // 计算卷入了多少条
Math.floor(scrollTop / itemH) + this.showNum
)
this.lastTime = new Date().getTime()
}
},
// 跳转
gotoCSSLongList(){
this.$router.replace('/CssLongList')
}
}
}
</script>
样式:
<style scoped>
* {
padding: 0;
margin: 0;
list-style: none;
box-sizing: border-box;
}
li {
text-align: center;
line-height: 60px;
border-bottom: 1px solid red;
}
.but{
height: 40px;
width: 100px;
margin-top: 10%;
}
</style>
插件
vue-virtual-scroller或者vue-virtual-scroll-list
自己封装
1. 数据一次性给:
- 元素监听scroll事件(滚动事件)
- 计算可视化高度一次能装几个列表,然后从总数据中进行slice截取
- 每一次滚动后根据scrollTop值获取一个可以整除itemH结果进行偏移(scrollTop:滚动条移动的距离)
2. 数据通过请求:
代码实现存在点问题修改后再上传
事件销毁
Vue组件在销毁时,会自动解绑它的全部指令事件及监听器,但仅限于组件本身。
比如定时器等最好在销毁阶段手动销毁,避免内存泄漏
created(){
this.timer = setInterval(this.refresh,)
}
beforeDestroy(){
clearInterval(this.timer)
}
无状态的组件标记为函数式组件(静态组件)
只接收父组件传过来的值,自己不做处理,无状态,不创建实例。
函数组件没有this
<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>
子组件分割
将子组件中耗时的任务交给组件自己管理,不影响整体页面的加载
<template>
<div>
<HelloWorld/>
</div>
</template>
<script>
export default {
// 子组件自己管自己
HelloWorld:{
methods:{
heavy(){/* 耗时任务 */}
},
render(h) {
return h('div', this.heavy());
}
}
}
变量本地化
减少使用 this.数据 的形式获取数据,减少一些不必要的处理
用一个变量先获取数据,在这个变量上处理数据。
在用this.数据 时会有数据劫持,去调用get/set做处理,浪费了时间。
模块化、组件化
- 封装高度复用的模块
- 拆分高度复用的组件
- 组件可配置性
<template>
// 可配置
<button
:class="['mybtn',`btn-${btnStyle}`]"
@click="myBtnClick($event)"
v-show="btnShow"
>
<slot></slot>
</button>
</template>
<script>
export default {
name: 'MyBtn',
props: {
btnStyle: String,
btnShow: Boolean
},
setup (props,ctx){
const myBtnClick = (e)=>{
ctx.emit('my-btn-click', e);
}
return{
myBtnClick
}
}
}
</script>
<style lang="scss" scoped>
.mybtn {
border: none;
outline: none;
height:34px;
padding: 0 15px;
background-color: #fff;
border: 1px solid #fff;
}
&.btn-default{
color: #333;
background-color: #fff;
border-color: #ccc;
}
&.btn-primary{
color: #fff;
background-color: #317DEF;
border-color: #317DEF;
}
</style>
二、懒加载
路由懒加载
const router = new VueRouter({
routes:[
{path:'.foo', component: () =>import('./Foo.vue')}
]
})
图片懒加载
运用插件
vue-lazyload
<img v-lazy="图片地址">
自己封装
代码完善后上传
三、keep-alive缓存页面
原理
在created周期函数(init初始化后)调用时。将需要缓存VNode节点保存 在this.cache中,在渲染页面前调用render函数时。如果VNodedename符合缓存条件(用include和exclude控制),则会从this.cache中取出缓存的VNode实例进行渲染。
参数
- include:字符或者正则表达式。在名称匹配的组件会被渲染。
- exclude:字符或者正则表达式。在名称匹配的组件不会被渲染。
- max:数字。最多可以缓存多少个实例。
被keep-alive包裹时具有的周期函数
activated
- keep-alive组件激活时调用
- 服务器端渲染不被调用
deactivated
- keep-alive组件停用时调用
- 服务器渲染期间不调用
注意
- keep-alive会将数据保留在内存中,如果要获取最新数据,则要在activated钩子函数中获取。
- include需要的name是组件的name
- 在多级嵌套兄弟页面时,缓存其中一个页面时会将父级都缓存了,导致所有子页面都缓存了,这时需要手动销毁其它兄弟页面。
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view></keep-alive>
// 销毁不需要的兄弟组件
<router-view v-if="!$route.meta.keepAlive"></router-view>
使用场景
如需要在不同组件切换时某个组件需要不改变
如:搜索框在输入文字后,切换组件时,输入框文字不消失,返回也不消失。
使用方法
使用属性include和exclude
// 只缓存名字为home的组件
<keep-alive include="home">
<router-view />
</keep-alive>
// 缓存整个组件
<keep-alive>
<router-view />
</keep-alive>
路由中meta属性来控缓存
// meta属性中keepAlive值设为true
{
path: '/',
name: 'home',
meta:{
keepAlive:true
},
component: Home
}
// 配合 keep-alive进行控制缓存
<keep-alive>
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
存在的问题
组件被缓存后,不存在销毁阶段,在组件切换时,不会调用created等生命周期函数,导致数据不能及时更新。
解决方法:
使用提供的activated与deactivated函数来获取当前组件是否活跃。
在activated函数中获取数据,更新数据(被缓存的组件才会有这两个函数)
四、打包优化
1.引入库
按需引入,避免体积过大
import {Button , selet}from 'element-ui';
2.引入资源
插件cdn、图片cdn、使用CSS图标
减少项目体积
3.打包配置
位置:build/config/index.js
属性:productionGzip: true
作用:打包压缩
位置:truebuild/config/index.js
属性:productionSourceMap: false
作用:减小打包体积 定位源码的会生成map文件 可能造成源码泄露
五、首屏优化
数据加载使用loding骨架屏
在数据加载阶段擦用骨架屏可以给用户带来更好的体验
首屏采用SSR服务端渲染
- 客户端发送请求给服务器
- 服务器读取模板,解析成dom节点,返回一个完整的首屏html结构
- 客户端进行首屏激活(把用户写的交互的代码,在前端激活,重新变成一个spa应用)
- 这样后续,用户再点击超链接、跳转时,不会再向服务器发送请求了,而是使用前端路由跳转,只会发送一些ajax请求数据
使用web worker
作用
让JS实现多线程
给JS创造多线程运行环境,允许主线程创建worker线程,分配任务给后者,主线程运行的同时worker线程也在运行,相互不干扰,在worker线程运行结束后把结果返回给主线程。
这并不意味着JS语言本身支持了多线程能力,而是浏览器作为宿主环境提供了JS一个多线程运行的环境。
使用
Worker()构造函数,第一个参数是脚本的网址(必须遵守同源政策),该参数是必需的,且只能加载 JS 脚本,否则报错。第二个参数是配置对象,该对象可选。它的一个作用就是指定 Worker 的名称,用来区分多个 Worker 线程。
var myWorker = new Worker(jsUrl, options)
使用场景
-
加密数据
有些加解密的算法比较复杂,或者在加解密很多数据的时候,这会非常耗费计算资源,导致UI线程无响应,因此这是使用Web Worker的好时机,使用Worker线程可以让用户更加无缝的操作UI。 -
预取数据
有时候为了提升数据加载速度,可以提前使用Worker线程获取数据,因为Worker线程是可以是用 XMLHttpRequest 的。 -
预渲染
在某些渲染场景下,比如渲染复杂的canvas的时候需要计算的效果比如反射、折射、光影、材料等,这些计算的逻辑可以使用Worker线程来执行,也可以使用多个Worker线程,这里有个射线追踪的示例。 -
复杂数据处理场景
某些检索、排序、过滤、分析会非常耗费时间,这时可以使用Web Worker来进行,不占用主线程。 -
预加载图片
有时候一个页面有很多图片,或者有几个很大的图片的时候,如果业务限制不考虑懒加载,也可以使用Web Worker来加载图片,可以参考一下这篇文章的探索,这里简单提要一下。
优化webpack配置
- webpack的code-split结合vue-router做懒加载
- webpack的conthash模式针对文件级别更改做缓存
总结
以上记录了vue在各个方面的优化,对于webpackp的优化在学完webpackp后完善
更多推荐
所有评论(0)