代码性能优化

减少Object.defineProperty的执行次数

* vue2使用他来订阅data中的所有属性

* vue3使用proxy,解决了如下问题

  * vue2的defineProperty不能察觉属性的添加与删除

  * vue2的defineProperty在数组的所有方法都是自己实现的

  * proxy可以做到

* __只有需要在页面中响应式显示的属性,才放到data中__

只显示一次的变量处理

* 也可以用v-once

 data(){ 
        return {
        // 应针对里面属性不修改
             val:Object.freeze({a:1})
        }
    }

 一次都不显示的变量处理

 // 直接给this.挂载属性使用,如 this.timer

   this.timer = setInterval();  

keep-alive

* 避免频繁的创建与销毁组件

- `include` - 字符串或正则表达式。只有名称匹配的组件会被缓存。

- `exclude` - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。

- `max` - 数字。最多可以缓存多少组件实例。

keep-alive实例

* 处理方案

<keep-alive :include="cacheComponents" max="20">  
    <router-view/>
</keep-alive>  

<script>
    import cacheComponents from 'config'; //组件名 ['01_defineProperty','02_freeze']
    export default {
        name:'main',
        data(){
    return {
    cacheComponents
            }
        }
    }
</script>

* max 会根据队列,后进后出,先取消缓存前面的

v-for和v-if为什么不能在一起

* 为什么要在一起?

* v-for执行权重较高

<div v-if="data">
    {{ data.xxx }}
</div>
<!--业务需要-->
<div v-for="item in data" v-if="item.show">
    
</div>
<!--不好的示例:循环次数 * v-if的判断-->
<div v-for="item in data" v-if="data">
    
</div>

* v-for 第一层含义是判断元素是否存在

* v-for执行权重较高, 循环次数 * v-if的判断, 影响性能

*  跟for原理一致,会先判断被循环的元素是否存在,因此不用单独v-if判断元素

<!--不好的方式,n次判断v-if-->
 <div v-for="u in users" :key="u.name" v-if="u.active">
      {{ u.name }}
    </div> 
  <!--中等方式,n次js判断-->

<div v-for="u in getActiveUser(users)" :key="u.name">
      {{ u.name }}
 </div> 
  <!--高等方式compunted,n次js判断,this.xxx相关属性原值不变,可以缓存-->
 <div v-for="u in activeUsers" :key="u.name">
      {{ u.name }}
 </div> 

* 总结:computed方式

  * 1. 解决元素存在与否的判断

    2. 执行元素次数的js判断

    3. 原值相同,不会执行,走缓存

减少diff时不必要的DOM操作

* 索引作为key时,key会随着元素增加和移除而改变

* 在diff时,如果告诉了框架 老DOM中的张三  === 新DOM中的李四

* 就会造成不必要的DOM操作

* 尽可能给与准确的key作为diff对比的依据,否则会造成多余的操作(修改),

  * vue会尽可能的就地复用元素,对比出差异,产生修改

减少渲染层级

减少DOM层级

* vue-fragment

import Fragment from 'vue-fragment'
Vue.use(Fragment.Plugin)

// or
import { Plugin } from 'vue-fragment'
Vue.use(Plugin)

// …

export const MyComponent {
  template: '
  <fragment>
    <input type="text" v-model="message">
    <span>{{ message }}</span>
  </fragment>
  ',
  data() { return { message: 'hello world }}
}

减少css层级

* 远离scoped

* 组件的style会将内容作为style标签,动态插入

* 大型项目 使用普通css,特定个例使用scoped css

* 对于template周期以外生成的结构,需要使用/deep/ 来查找

  * 比如第三方的UI库的某些样式

  * 比如v-html中的元素

分析消耗

* 1. 动态创建style标签 : 性能消耗

  2. 浏览器解析多个style标签: DOM阻塞渲染

  3. 给元素添加属性:性能消耗

  4. 浏览器css选择器根据属性查找:阻塞css渲染

* 解决:尽可能少些scoped样式,而直接使用传统方式-全局

函数式组件

* 函数式: pure纯函数,只接收参数,返回数据,没有自身的状态

* function (n1,n2) { let xx = 1; return   n1 + n2  }

* 默认组件有状态:自身数据data,生命周期(生命周期初始化)

* 没有生命周期 -> 没有初始化生命周期的js开销

<!--方式1:也可以使用模板的方式-->
<template functional>
    <div>
      <!-- 当前作用域就是context作用域`props` `children` `slots`:
 `scopedSlots` `data``parent`:`listeners` -->
        {{data.attrs.text}}
        <slot></slot>
    </div>
</template>
<!--方式2:-->
export default {
    functional:true,
    render(h,context){
        return h('div',context.props.text,context.children)
    }
}

参数列表

组件需要的一切都是通过 `context` 参数传递,它是一个包括如下字段的对象:

- `props`:提供所有 prop 的对象

- `children`:VNode 子节点的数组

- `slots`:一个函数,返回了包含所有插槽的对象

- `scopedSlots`:(2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。

- `data`:传递给组件的整个[数据对象](渲染函数 & JSX — Vue.js),作为 `createElement` 的第二个参数传入组件

- `parent`:对父组件的引用

- `listeners`:(2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 `data.on` 的一个别名。

- `injections`:(2.3.0+) 如果使用了 [`inject`](API — Vue.js) 选项,则该对象包含了应当被注入的 property。

总结

1. data作为Vue框架中组件的运行基础,其底层Object.defineProperty承载了很重要的职责,尽可能减少不必要的该方法执行。

2. DOM层级目前受框架约定影响,可以尽可能借助第三方减少DOM的层级,优化渲染速度。

3. 尽量减少CSS选择器查找的效率,也可以提升渲染效率。

4. 计算属性是Vue中提供的一个响应式输出数据的方式, 内部已经实现缓存。 

更多推荐

Vue代码优化