Vue重点知识总结—基础篇(二)

1.Vue 组件 data 为什么必须是函数

因为js本身的特性带来的,Vue组件可能会有多个实例,如果 data 是一个对象,那么由于对象本身属于引用类型,当我们修改其中的一个属性时,会影响到当前组件所有实例的数据。如果将 data 作为一个工厂函数,那么每一个组件实例的 data 属性都是全新独立的,不会相互影响了,有效规避了很多组件状态污染问题。

通过查看源码发现,在state.js的initData方法中,对data的类型做了判断,如果是函数类型,则执行getData(data.call()直接执行)方法获取其值,如果是普通对象则返回原对象。

在组件中使用对象作为data的值,在编译时会直接报错。

原理:

  1. data是被挂到组件类的原型中,如果不是函数而直接是一个对象,会被其他任何组件继承,任何组件都可以对其进行修改。
  2. 如果data是一个工厂对象,每个组件进行调用时,执行函数都会返回一个全新的对象,就可以避免组件之间的数据互相影响。
  3. Vue实例化根组件时data是一个对象,是因为Vue根实例只会实例化一次,是单例的,实例不会被公用。不用考虑组件之间干扰问题。

2.计算属性computed 和 methods 有什么区别

我们可以将同一函数定义为一个 method 或者一个计算属性。对于最终的结果,两种方式是相同的。

区别:

  1. computed计算属性是一个watcher有缓存,计算属性是基于依赖(依赖就是data中的数据)进行缓存的。只要依赖的数据不发生改变,计算属性中的方法是不需要执行的,一直使用缓存中的数据。当它的相关依赖发生改变时会重新赋值。当多次调用的时候只要依赖项不改变,就不会重新执行,从而节省了内存。(缓存机制在源码中通过dirty状态去判定)
  2. method 就是普通的函数。不依赖data中的数据,也没有缓存,每次调用都重新执行一遍。
  3. 函数的调用时需要加()的,而计算属性调用不需要加括号。
  4. 计算属性是必须有返回值的。

3.侦听器 watch和计算属性computed区别

两者的原理相同,在内部都是watcher,主要区别就是computed是有缓存的。如下:

  1. 侦听器 watch(数据一旦发生变化就通知侦听器所绑定的方法)。watch 监听的是已经在 data 中定义的变量,当该变量变化时,会触发 watch 中的方法;
  2. 计算属性computed是根据一个现有数据去生成一个新数据,并且这两个数据会永久的建立关系,还会建立缓存,当数据没有改变的时候,不会重新计算而是直接使用缓存中的值。
  3. 初始化computed计算属性时会实例化一个watcher,这个watcher有一个lazy:true标识,代表计算属性。还有一个默认dirty:true的标识,为true时才会让计算属性执行。当第一次取值时会执行,执行完毕后会将dirty改为false,也就是说下次再取值不会执行,会直接使用缓存的值。只有当计算属性所依赖的属性发生变化时,会将dirty改为true
  4. computed的底层还是使用Object.defindProperty来实现视图更新。
  5. watch一般用于异步、定时任务、或者开销较大(耗时)的操作。
  6. watch 中的属性 一定是data 中 已经存在的数据。
  7. 当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,此时就需要deep属性对对象进行深度监听(深度遍历和递归)。
  8. 使用 watch 选项允许我们执行异步操作,并在我们得到最终结果前,进行中间操作。也可以在数据频繁变化时进行自定义的操作如过滤操作。 而计算属性无法做到的。
  9. 计算属性是必须有返回值的。
watch:{
    obj:{ //监听的对象
        deep:true, //深度监听设置为 true
        handler:function(newV,oldV){
            console.log('watch中:',newV)
        }
    }
}
// deep:true的实现就是深度遍历和递归,很耗性能。
// 如果监听的是数组,deep:true会将数组中的每一项进行遍历取值,会将当前的watcher存到对应属性或数组项的依赖中,实现深层数据变化也会通知。

4.v-if 和 v-show 区别

使用了 v-if 控制页面元素节点是否渲染。v-show 改变 CSS 中的 display 显隐。故想要频繁控制显隐,最好使用v-show

两者不能连用,虽然连用不会报错,但是只有两者同时为true时才会显示节点,无法进行精准的判断。而且两者连用会在渲染函数中做两次判断,影响了性能。

底层原理:

  1. v-if在内部使用with语法,使用三元运算符判断v-if的值的真假。若为真,则渲染节点。若为假,则不渲染。
  2. v-show在内部使用创建指令方式,判断值为假,则改变节点样式display:none,若为真,则使用原有样式。

5.为什么v-if和v-for不能连用?

因为v-forv-if优先级高(vue2.x)。

通过测试得出,如果连用,在渲染函数($options.render)中v-for遍历出的每个元素中都添加v-if指令,性能很差。

所以一般在外层template标签添加v-if,这样如果为false,则不会执行v-for遍历,节省了性能开销。

优化:若非整体显隐,而是在列表中某些字内容有v-if的判断,这时也可以做一个计算属性,通过计算将不需要显示字内容进行过滤,节省了性能。

6.v-for中为什么要有key属性?

在Vue中使用v-for进行列表渲染的时候,它会默认使用“就地更新”的策略。直接对已有的标签就地更新,并不会将所有的标签全部重新删除和重建,只会重新渲染数据,然后再创建新的元素直到数据渲染完为止。

v-for渲染的列表更新时,diff算法会比较新旧节点的标签名key。若进行增加删除列表内节点后,key值会发生变化,那么新旧节点在进行比对时容易错位。最终导致渲染出错。

  1. key主要作用是为了高效得更新虚拟DOM。原理是VUE在patch过程中会通过key精准判断两个节点是否是同一个。从而避免频繁更新不同元素,使得整个patch过程更加高效,减少dom操作量,提高性能。使用 key,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
  2. 若不设置key,会在列表的渲染过程中发生一些隐蔽的bug
  3. 重复的 key 会造成渲染错误。

没有key情况还会导致节点的删除和重建,影响性能。若有key,在进行更新时会直接保留原有节点,只插入新节点,无需删除重建。

  1. v-for循环的时候,key属性只能使用number/string
  2. key属性值必须是唯一标识符。
  3. key属性不要使用索引作为标识。

用引索index作为key可能会引发的问题:

  1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
  2. 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
  3. 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

7.vue的生命周期介绍

Vue实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载DOM->渲染、更新->渲染、卸载等一系列过程,我们称这是Vue的生命周期。

1.vue生命周期的作用是什么?

它的生命周期中有多个事件钩子,让我们在控制整个vue实例的过程时更容易形成好的逻辑。

在于Vue渲染过程的每个节点给一个通知(事件、回调),便于我们在某一个节点进行一些相关的操作。

2.Vue生命周期总共有几个阶段?

它可以总共分为8个阶段:创建前/后,载入前/后,更新前/后,销毁前/销毁后

3.第一次页面加载会触发那几个钩子?

第一次页面加载时会触发beforeCreate,created,beforeMount,mounted

4.DOM渲染在哪个周期中就已经完成?

DOM渲染在mounted中就已经完成了

5.生命周期的调用流程:

  1. beforeCreate:实例初始化完成,数据观测(data observer)之前。在这里拿不到实例中的数据。
  2. created:实例创建完成(数据观测完成、计算属性运算完成、method方法绑定、watch和event事件回调绑定),但dom还未挂载,数据还未渲染到页面,拿不到$el(拿不到dom元素)。<create 和 mount 的区别>
  3. beforeMounted:实例挂载之前执行。会调用render函数(调用template)。这个钩子基本用不到。
  4. mounted:实例挂载完成。创建新的vm.$el,把老的el对象删掉。这里可以拿到dom元素。
  5. beforeUpdate:数据更新时调用,发生在虚拟DOM重新渲染和打补丁之前。
  6. updated:当前生命周期更新之后调用。由于数据更改,虚拟DOM渲染完成之后。
  7. beforeDestroy: 实例销毁之前调用。在这时,实例仍然可用。
  8. destroyed:实例销毁之后调用。调用后,实例所有数据监听都会解绑,所有事件监听都会移除,所有子实例也会被销毁。

6.生命周期钩子的一些使用方法(重点):

  1. beforeCreate:可以加个loading事件,在加载实例时候触发。
  2. created:在这里可以结束loading事件,执行异步请求将数据存如data。
  3. mounted:获取到dom节点,可以进行一些dom操作。由于这里能够取到dom,也可以在这里执行异步请求。
  4. beforeUpdate:在这里可以进一步更新状态,不会导致DOM重新渲染。
  5. updated:这里尽量不要再更新状态,否则可能会导致死循环。这里一般用不到。就算用到也是执行一些基于dom的操作,如动画效果。
  6. beforeDestroy:可以执行一些优化操作。如清空定时器,移除绑定事件等。
  7. nextTick:更新数据后立即操作dom,回调可以做一些在dom渲染完成后做的事。

8.父子组件执行顺序

在组件开始生成到结束生成的过程中,如果该组件还包含子组件,则自己开始生成后,要让所有的子组件也开始生成,然后自己就等着,直到所有的子组件生成完毕,自己再结束。所以上图中。“父亲”先开始自己的created,然后“儿子”开始自己的created和mounted,最后“父亲”再执行自己的mounted。

9.v-html指令会造成哪些问题?

1.可能会导致xss攻击。

2.会将标签内的子元素全部替换。

10.对 keep-alive 的了解

keep-alive 是 Vue 内置的一个组件,可以实现组件缓存。缓存组件数据。组件切换时不会对其进行卸载。可以使被包含的组件保留状态,避免重新渲染。

keep-alive会改变组件的生命周期。当引入keep-alive的时候,页面第一次进入,钩子的触发顺序created-> mounted-> activated,退出时触发deactivated。当再次进入(前进或者后退)时,只触发activated。

  1. 有两个属性props:include和exclude,想缓存的内容和不想缓存的内容。max属性:最多缓存多少个。
  2. keep-alive内插入多个组件只会缓存第一个。

作用:①提升性能,避免每次重新渲染。(但也不宜太多,太多的话占用内存太大得不偿失)②保留状态(数据)

加载中...
加载中...