目录
6.3、vue2判断数据是否变化是通过hasChanged函数实现的
①为什么在x === y成立的时候,还要做一个return x === 0 && 1 / x !== 1 / (y as number)的判断?
②为什么在 x === y 不成立的时候,还要做一个x === x || y === y的判断?
Vue优化路径
一、使用key
也就是使用v-for这样通过循环生成的列表,应给每个列表项一个稳定且唯一的key,这样有利于在列表变动时,尽量少的删除、新增、改动元素。
二、使用冻结对象
在不需要改动的数据时(比如只读的数据:面向顾客的商品列表等),将对象冻结。例如:
- var obj = { a : 1, b : 2}
-
- Object.freeze(obj)
-
- obj.a = 3 // 无效,obg.a依然为1
-
- // 此时obj的属性无法访问,自然也无法通过Object.defineProperty来实现响应式
-
- Object.isFrozen(obj) // 返回true,检查是否冻结
冻结的对象不会被响应化,节约了observe该对象每个属性的资源(添加getter和setter)
举一个例子,可以做一个demo实际体验一下效率的区别,做个页面放两个按钮,分别绑定loadFrozenDatas和loadNormalDatas方法,就能明显感受到生成同样的数据,冻结的对象生成速度显著快于不冻结的对象生成。
- data() {
- return{
- normalDatas: [],
- freezeDatas: [],
- };
- },
- methods: {
- loadNormalDatas(){
- this.normalDatas = this.getDatas(); console.log("normalDatas", this.normalDatas);
- },
- loadFrozenDatas() {
- this.freezeDatas = Object.freeze(this.getDatas()); console.log("freezeDatas", this.freezeDatas);
- },
- getDatas(){
- const result = [];
- for (var i = 0; i < 1000000; i++) {
- result.push({
- id: i,
- name:`name${i}`,
- address:{
- city:`city${i}`,
- province:`province${i}`,
- },
- });
- }
- return result;
- },
- }
observe中会调用Object.defineProperty(),通过属性描述符为对象的每个属性实现响应式
属性描述符详情请看:属性描述符初探——Vue实现数据劫持的基础
三、使用函数式组件
在Vue.js中,函数式组件是一种没有状态和实例的概念的组件。函数式组件主要用于声明性地描述UI,它们接受 props 作为输入,并返回一个Vue元素树作为输出。
函数式组件不会通过new VueComponent生成新的vue实例,不会加入到vue的组件树中,只做页面渲染,节省性能。
- // Vue 2中的函数式组件:
-
- Vue.component('my-functional-component', {
- functional: true,
- render: function (createElement, context) {
- // 使用createElement创建元素
- return createElement('div', context.props.text);
- }
- });
-
-
- // Vue 3中的函数式组件:
-
- import { h, FunctionalComponent } from 'vue';
-
- const MyFunctionalComponent: FunctionalComponent = (props, { slots }) => {
- return h('div', props.text, slots().default);
- };
-
- export default MyFunctionalComponent;
四、使用计算属性
计算属性可以缓存(只有所依赖的数据变化了才会重新计算),如果模版中数据会使用多次,就可以使用计算属性。
五、使用非实时绑定的表单项
双向绑定会导致任意一端修改数据均会导致重渲染rerender,在不需要双向绑定的位置(比如只开放只读数据)或者不需要保持实时数据双向绑定的情况下(比如输入框内容和页面某元素绑定,输入过程中每按一次键盘都会导致一次重新渲染)不使用v-model。
也可以通过v-model.lazy来允许某一时间内数据与表单内容不一致,也就是从监听@input变成了监听@change。
六、保持对象引用稳定
6.1、保持对象引用稳定定义
大多数情况下,vue触发重渲染的时机是依赖数据发生变化的时机,若数据没有变化,哪怕重新给数据赋值,vue也不会做出反应。
因此,哪怕读取属性所属的对象值没变,但是引用变了,也会导致页面重新渲染。
6.2、保持对象引用稳定与不稳定的例子
现在页面上渲染了一个表格,由一系列对象数据生成。如果要在数据库增加一行。那么读取数据库增加的数据,并将其添加到现有的表格数据中,效率会比直接从数据库读取全部数据,然后赋值给表格绑定的数据上要高。
因为读取增加的数据,然后修改表格绑定的数据,只有变化的数据会重新渲染,原先有的表格行不会重渲染,可如果直接把增加后的全部数据(一个引用不同的新对象)赋值给表格绑定的数据,就会导致所有行全部重新渲染,哪怕大多数行数据并没有变化。
6.3、vue2判断数据是否变化是通过hasChanged函数实现的
- function hasChanged(x : unknown, y : unknown) :boolean{
- if (x === y){
- return x === 0 && 1 / x !== 1 / (y as number)
- } else {
- return x === x || y === y
- }
- }
这里x与y分别是新值和旧值。
正常情况下,如果x===y,代表没有改变,返回false,反之则返回true。
①为什么在x === y成立的时候,还要做一个return x === 0 && 1 / x !== 1 / (y as number)的判断?
+0 === -0的判断恒为true,但二者实际不相等。所以先判断x是否为0+或0-,如果不是,则直接触发短路返回false,如果是,就通过求倒数,比较倒数是否相等,如果均为0+或均为0-,则依然返回false,若倒数为Infinity和-Infinity,这样就会返回true,从而排除从0+变为0-,但是却新旧值却相等的情况。
②为什么在 x === y 不成立的时候,还要做一个x === x || y === y的判断?
因为如果NaN === NaN的判断恒为false,所以哪怕不相等,还要做一下自判断,若新旧值都是NaN,则返回false。排除x与y都为NaN,但是新旧值却不相等的情况。
七、使用v-show替代v-if
对于频繁切换显示状态的元素,使用v-show可以保证虚拟dom树的稳定,尤其是对于那些内部包含大量dom元素的节点,这一点极其重要。
DOM树只与布局有关,与显示与否无关。使用v-show渲染的元素,不管返回值是什么,都会添加到dom树中,但是使用v-if渲染的元素,只有为true的时候才会添加到DOM树中。
八、使用延迟装载(defer)
HTML中的