zhaobinglong / myBlog

https://zhaobinglong.github.io/myBlog/
MIT License
7 stars 0 forks source link

vue项目性能优化实践 #85

Open zhaobinglong opened 3 years ago

zhaobinglong commented 3 years ago

数据冻结Object.freeze

vue 会通过 object.defineProperty 对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 vue 来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间。所以,我们可以通过 object.freeze 方法来冻结一个对象,这个对象一旦被冻结,vue就不会对数据进行劫持了。

demo

export default {
  data: () => ({
    list: []
  }),
  async created() {
    const list = await axios.get('xxxx')
    this.list = Object.freeze(list)
  },
  methods: {
    // 此处做的操作都不能改变list的值
  }
}
zhaobinglong commented 3 years ago

子组件分担渲染任务

长列表在数据量很大的情况下,每次操作完原数组后,原数组list发生变化,会重新走一遍v-for进行页面重绘渲染,效率低下。写一个单独的组件,将数组list中的每一个对象传给子组件(Operate.vue),组件用props接受到后,之后只操作传过来的这个对象,不改变原数组list中的任何值;备注:这里为将一个数组变为两个,一个为 listOriginal 仅做初次渲染使用,另外一个listOperate 在组件回调后操作,用来做渲染之外的业务处理。这样页面渲染和业务操作通过子组件达到互补干扰的效果,页面也会极其流畅了。

实例

<template>
    <div class="optimize">
        优化列表
        <ul>
            <Operate v-for="(item,index) in listOriginal" :item="item" :index="index" :key="index"></Operate>
        </ul>
    </div>
</template>

// 子组件
<template>
    <li class="operate">
        <img :src="item.img" />
        <div class="left_bottom">
            <p>{{item.name}}</p>
            <p><span v-if="item.num > 0">{{item.num}}整份</span><span v-if="item.num > 0">{{item.num}}半份</span></p>
            <span class="add btn" @click="add(index)">+</span>
            <span class="num">{{item.num}}</span>
            <span class="reduce btn" @click="reduce(index)">-</span>
        </div>
    </li>
</template>

<script>
    export default {
        name: 'operate',
        data() {
            return {}
        },
        props: ["item", "index"],
        methods: {
                        // 子组件承担数据的渲染和业务交互
            add(index) {
                let t = this;
                t.item.num++;
                                //t.$emit(...);//回调操作父组件listOperate,或者处理其他业务均可
            },
            reduce(index) {
                let t = this;
                t.item.num > 0 ? t.item.num-- : 0;
                                //t.$emit(...);//回调操作父组件listOperate,或者处理其他业务均可
            }
        }
    }
</script>

参考

https://blog.csdn.net/weixin_34178244/article/details/88028856

zhaobinglong commented 3 years ago

合理使用v-for、v-if、v-show、v-once

vFor 的优先级其实是比 vIF 高的,所以当两个指令出现来一个DOM中,那么 vFor 渲染的当前列表,每一次都需要进行一次 vIf 的判断。而相应的列表也会重新变化,这个看起来是非常不合理的。因此当你需要进行同步指令的时候。尽量使用计算属性,先将 vIf 不需要的值先过滤掉。看起像是下面这样的。

// 计算属性
computed: {
  filterList: function () {
    // 利用计算属性把需要渲染的数据先过滤出来,避免在for的时候判断
    return this.showData.filter(item => item.checked)
  }
}

// DOM 
<ul>
  <li v-for="item in filterList" :key="item.id">
  {{ item.name }}
  </li>
</ul>
// 针对只需要渲染一次的组件,一旦渲染之后内容不会再次变化的组件
<div v-once>{{ message }}</div>
zhaobinglong commented 3 years ago

组件异步加载

利用v-if,异步加载不需要直接初始化就显示的组件,v-if是惰性指令,只有第一次为true,才会显示

 <app>    
   <A></A>    
   <B v-if="showB"></B>    
   <C v-if="showC"></C>
</app>
zhaobinglong commented 3 years ago

异步路由

使用异步路由可以根据URL自动加载所需页面的资源,并且不会造成页面阻塞,较适用于移动端页面 建议主页面直接import,非主页面使用异步路由

{
  path: ‘/order’,
  component: () => import(’./views/order.vue’)
}
zhaobinglong commented 3 years ago

提前异步请求数据

它本质上,类似于Vue.js的生命周期钩子函数。它在新路由被确认前被调用,可以在里面进行数据处理,发起ajax获取数据,甚至是取消导航。当调用该守卫时,页面仍停留在原页面。该守卫执行完毕后(包括异步获取数据),才会跳转到新页面。

实例

import $axios from '@/libs/axios';
import api from '@/api';  

export default {
  name:'testComponent',
  data(){
    return {};
  },
  beforeRouteEnter(to, from, next) {
    $axios
      .get(api.GetQueueSettingDetail, {
        params: {
          waybillKey: to.params.pickCode,
          warehouseType: 11
        }
      })
      .then(resp => {
        if (resp.success) {
          next(vm => {
            vm.carInfo = resp.result;
          });
        } else {
          next(false);
        }
      })
      .catch(() => {
        next(false);
      });
  },
}

参考

https://www.cnblogs.com/twodog/p/12134730.html

zhaobinglong commented 3 years ago

全局引用第三方包减少import

// 应用NP处理浮点数运算
import NP from 'number-precision'
Vue.prototype.NP = NP

// 在templete中使用
{{ NP.times(data1, data2) }}

// 在methods中使用
this.NP.times(data1, data2)
zhaobinglong commented 3 years ago

beforeDestroy释放资源

什么是资源? 每创建出一个事物都需要消耗资源,资源不是凭空产生的,是分配出来的。所以说,当组件销毁后,尽量把我们开辟出来的资源块给销毁掉,比如 setInterval , addEventListener等,如果你不去手动给释放掉,那么它们依旧会占用一部分资源。这就导致了没有必要的资源浪费。多来几次后,可以想象下资源占用率肯定是上升的。 添加的事件

created() {
  addEventListener('click', Function, false)
},
beforeDestroy() {
  removeEventListener('click', Function false)
}
// 定时器
created() {
  this.currentInterVal = setInterval(code,millisec,lang)
},
beforeDestroy() {
  clearInterval(this.currentInterVal)
}
zhaobinglong commented 3 years ago

生产环境下关闭map文件的生成

zhaobinglong commented 3 years ago

利用keep-alive缓存组件避免重复渲染

Vue(keep-alive)指定单页返回到页面不刷新,即保存状态 遇到的问题 在开发过程中,从 form单页 跳转到品牌选择页,选择成功后返回到 form页,需要保持 form页 的状态不变,即内容不刷新 众所周知,我们可以用keep-alive来实现,keep-alive是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM

// 实现所有页面不刷新
<keep-alive>
    <router-view ></router-view>
</keep-alive>

但是这样并不是我所需要的效果,我只需要从品牌页跳转到form页实现不刷新 实现指定页面返回不刷新

// 1. 在路由router/index.js里面,在需要被缓存的组件路由设置keepAlive:true
{
  path: '/form',
  name: 'form',
  component: form,
  meta:{
    keepAlive:true,
    title: '表单应用'
  }
},
// 2. 在App.vue把代码修改成如下
<keep-alive>
  <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>

解释:路由的meta属性的keepAlive属性值为true时页面保存状态,其他情况不保存状态