?实际场景中的一些细节优化
函数式组件来加快组件渲染
函数式组件(functional):利用组件传值的关系,加快template模板的渲染,效率最快是render,但修改比较麻烦,考虑这点建议还是使用函数式组件。
使用场景:静态页面不需要修改值变化的场景。
<template functional>
<ul>
<li v-for="item in props.data" :key="item.id">{{item.value}}</li>
</ul>
</template>
<script>
export default {
name: 'Functional',
props: {
data: {
require: true
}
}
}
</script>
<template>
<Functional :data="list"/>
</template>
<script>
import Functional from '@/components/Functional.vue'
export default {
name: 'App',
data() {
return {
list: [
{id: 1,value: '111'},
{id: 2,value: '333'},
{id: 3,value: '333'}
]
}
},
components: {Functional}
}
</script>关于v-for和DOM-DIFF的一些细节处理
v-for中key为啥最好不要设置index?设置key为啥能提高dom-diff查找效率?
<template>
<div class="dom">
<div>v-for 和 dom-diff之间的关系</div>
<div class="btn">
<button @click="delList">del(0)</button>
<button @click="addList">add</button>
</div>
<ul>
<li v-for="item in list" :key="item.id">
{{item.id}}:{{item.value}}
</li>
</ul>
<div>.....................................................</div>
<button @click="delList2">del2(0)</button>
<button @click="addList2">add2</button>
<ul>
<li v-for="(item,index) in list2" :key="index">{{index}}:{{item.value}}</li>
</ul>
</div>
</template>
<script>
export default {
name: 'ForAndDiff',
data() {
return {
list: [
{id: 1,value: '111'},
{id: 2,value: '333'},
],
list2: [
{id: 1,value: '111'},
{id: 2,value: '333'},
]
}
},
methods: {
delList() {
this.list.splice(0,1);
},
delList2() {
this.list2.splice(0,1);
},
addList() {
this.list.splice(2, 0, {
id: 545,
value: "互联网",
});
},
addList2() {
this.list2.splice(2, 0, {
value: "互联网",
});
},
}
}
</script>根据上面案例,可以发现key设置index,key值是动态变化,而绑定id,key是唯一固定,添加相同id,能添加成功,但vue会给报错信息。这样的好处:有利于diff算法查询,而key为index,移除之前后又有重新的值,这样容易导致diff算法出现问题,为了减少不必要的情况,所以最好用id。
computed计算属性的优化
<template>
<div>
{{ subtotal }}
{{ subtotal2 }}
</div>
</template>
<script>
export default {
name: 'Compoueduse',
data() {
return {
n: 10
}
},
computed: {
m() {
return 20;
},
subtotal() {
console.time("AAA");
let result = this.n;
for (let index = 0; index < 9999999; index++) {
result += this.m;
}
console.timeEnd("AAA"); //第一次217.31494140625 ms
return result;
},
subtotal2() {
console.time("BBB");
let {m, n}= this;
let result = n;
for (let index = 0; index < 9999999; index++) {
result += m;
}
console.timeEnd("BBB"); //BBB: 20.25390625 ms
return result;
}
},
}
</script>一下子优化10倍,这效率很明显吧。
v-show和v-if的合理使用技巧
区别:
- v-if控制元素的创建销毁,会导致重绘;v-show控制元素的display,导致回流。
- v-show 有更高的首次渲染开销,而 v-if 的首次渲染开销要小的多;
- v-if 有更高的切换开销,v-show 切换开销小;
- v-if 有配套的 v-else-if 和 v-else,而 v-show 没有
使用场景具体分析:
- 切换频繁考虑v-if,不频繁v-show
- 切换后是相同组件,直接v-show
关于vue-lozyload图片懒加载的研究
- 使用第三方插件
- 利用img.onload,结合下面长列表实现。
长列表和无限列表的性能优化
监听离可视窗口的距离: IntersectionObserver(不兼容Safari, 有polyfill版)
Object.freeze()方法可以冻结一个对象
一个被冻结的对象再也不能被修改
- 不能添加新属性
- 不能删除已有属性
- 不能修改已有属性的可枚举性、可配置性、可写性
- 不能修改已有属性的值
- 不能修改原型
<template>
<div class="news-list">
<template v-if="list.length">
<div class="item" v-for="(item, index) in list" :key="index">
<div class="con">
<h4 class="title">{{item.title}}</h4>
<p class="hint">{{item.public_abbr}}</p>
</div>
<div class="pic" ref="pics">
<img
v-if="item.list_image_url"
:data-src="item.list_image_url"
alt=""
/>
</div>
</div>
</template>
<div class="loading" ref="loading" v-show="visible">加载更多数据...</div>
</div>
</template>
<script>
export default {
name: 'newList',
data() {
return {
list: [],
visible: false
}
},
methods: {
async query() {
let result = await this.$api.trending();
result = result.map((item) => {
return Object.freeze(item.object.data)
// return item.object.data;
})
this.list.push(...result);
this.visible = true;
// 获取PIC并且进行监听
this.$nextTick(() => {
this.$refs["pics"].forEach((item) => {
this.ob.observe(item);
});
});
}
},
created() {
this.query();
},
mounted() {
this.ob = new IntersectionObserver(
(changes) => {
changes.forEach(({isIntersecting, target}) => {
if (!isIntersecting) return;
// 出现在视口中:加载真实图片
const img = target.querySelector("img");
if (!img) return;
img.src = img.getAttribute("data-src");
img.onload = () => (img.style.opacity = 1);
this.ob.unobserve(target);
});
},
{ threshold: [1] }
);
// 滚动到底部加载更多数据
this.obLoading = new IntersectionObserver(([item]) => {
if (item.isIntersecting) {
// 滚动到底部了
this.query();
}
});
this.obLoading.observe(this.$refs["loading"]);
}
}
</script>
<style lang="scss" scoped>
.news-list {
.item {
position: relative;
padding: 10px 0;
min-height: 120px;
border-bottom: 1px dashed #eee;
.con {
margin-right: 170px;
.title {
line-height: 30px;
font-size: 16px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.hint {
font-size: 14px;
line-height: 20px;
}
}
.pic {
position: absolute;
top: 10px;
right: 10px;
box-sizing: border-box;
width: 150px;
height: 120px;
overflow: hidden;
background: url("../assets/defaultbg.webp") no-repeat;
background-size: 100% 100%;
img {
display: block;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity 0.3s;
}
}
}
.loading {
height: 50px;
line-height: 50px;
text-align: center;
}
}
</style>使用自定义指令(directive)来优化项目代码

direactive.js
import Vue from 'vue';
import store from '@/store/index';
Vue.directive('permission', {
inserted(el, binding){
let permission = store.state.permission,permissionList = [];
if (!permission) permission = "";
permissionList = permission.split('|');
let passText = binding.value;
// 循环校验是否有权限
let flag = permissionList.includes(passText);
// 控制元素显示隐藏
if (!flag) el.parentNode && el.parentNode.removeChild(el);
}
})demo.veu
<template>
<div class="permission-demo">
<el-menu
default-active="1"
text-color="#fff"
active-text-color="#409eff"
background-color="#222832"
>
<el-menu-item index="1" v-permission="'ControlPanel'">
<i class="el-icon-menu"></i>
<span slot="title">控制面板</span>
</el-menu-item>
<el-submenu index="2">
<template #title>
<i class="el-icon-s-tools"></i>
<span>主页配置</span>
</template>
<el-menu-item index="2-1" v-permission="'BannerSetting'">
<i class="el-icon-picture-outline"></i>
<span slot="title">轮播图</span>
</el-menu-item>
<el-menu-item index="2-2" v-permission="'GoodsSetting'">
<i class="el-icon-wallet"></i>
<span slot="title">热销商品</span>
</el-menu-item>
<el-menu-item index="2-3" v-permission="'ProductSetting'">
<i class="el-icon-position"></i>
<span slot="title">新品上线</span>
</el-menu-item>
<el-menu-item index="2-4" v-permission="'RecommendSetting'">
<i class="el-icon-thumb"></i>
<span slot="title">为你推荐</span>
</el-menu-item>
</el-submenu>
<el-menu-item index="3" v-permission="'ClassiFication'">
<i class="el-icon-tickets"></i>
<span slot="title">分类管理</span>
</el-menu-item>
<el-menu-item index="4" v-permission="'GoodsManager'">
<i class="el-icon-shopping-cart-full"></i>
<span slot="title">商品管理</span>
</el-menu-item>
<el-menu-item index="5" v-permission="'MemberManager'">
<i class="el-icon-user"></i>
<span slot="title">会员管理</span>
</el-menu-item>
<el-menu-item index="6" v-permission="'OrderManager'">
<i class="el-icon-s-order"></i>
<span slot="title">订单管理</span>
</el-menu-item>
<el-menu-item index="7" v-permission="'SystemSetting'">
<i class="el-icon-setting"></i>
<span slot="title">系统设置</span>
</el-menu-item>
</el-menu>
</div>
</template>
<script>
export default {
name: "permission-demo",
};
</script>
<style lang="scss" scoped>
.el-menu {
border-right: none;
}
</style>store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import {createLogger} from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
permission: "ControlPanel"
},
plugins: [createLogger()]
});
/*
全部权限代码
控制面板 ControlPanel
首页设置
轮播图 BannerSetting
热销商品 GoodsSetting
新品上线 ProductSetting
为你推荐 RecommendSetting
分类管理 ClassiFication
商品管理 GoodsManager
会员管理 MemberManager
订单管理 OrderManager
系统设置 SystemSetting
*/版权声明:本文为qq_39189369原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。