1.Vue 组件 data 为什么必须是函数
因为js本身的特性带来的,Vue组件可能会有多个实例,如果 data
是一个对象,那么由于对象本身属于引用类型,当我们修改其中的一个属性时,会影响到当前组件所有实例的数据。如果将 data
作为一个工厂函数,那么每一个组件实例的 data
属性都是全新独立的,不会相互影响了,有效规避了很多组件状态污染问题。
通过查看源码发现,在state.js的initData方法中,对data的类型做了判断,如果是函数类型,则执行getData(data.call()直接执行)方法获取其值,如果是普通对象则返回原对象。
在组件中使用对象作为data的值,在编译时会直接报错。
原理:
- data是被挂到组件类的原型中,如果不是函数而直接是一个对象,会被其他任何组件继承,任何组件都可以对其进行修改。
- 如果data是一个工厂对象,每个组件进行调用时,执行函数都会返回一个全新的对象,就可以避免组件之间的数据互相影响。
- Vue实例化根组件时data是一个对象,是因为Vue根实例只会实例化一次,是单例的,实例不会被公用。不用考虑组件之间干扰问题。
2.计算属性computed 和 methods 有什么区别
我们可以将同一函数定义为一个 method
或者一个计算属性。对于最终的结果,两种方式是相同的。
区别:
computed
计算属性是一个watcher
有缓存,计算属性是基于依赖(依赖就是data中的数据)进行缓存的。只要依赖的数据不发生改变,计算属性中的方法是不需要执行的,一直使用缓存中的数据。当它的相关依赖发生改变时会重新赋值。当多次调用的时候只要依赖项不改变,就不会重新执行,从而节省了内存。(缓存机制在源码中通过dirty
状态去判定)method
就是普通的函数。不依赖data中的数据,也没有缓存,每次调用都重新执行一遍。- 函数的调用时需要加()的,而计算属性调用不需要加括号。
- 计算属性是必须有返回值的。
3.侦听器 watch和计算属性computed区别
两者的原理相同,在内部都是watcher
,主要区别就是computed
是有缓存的。如下:
- 侦听器
watch
(数据一旦发生变化就通知侦听器所绑定的方法)。watch 监听的是已经在 data 中定义的变量,当该变量变化时,会触发 watch 中的方法; - 计算属性
computed
是根据一个现有数据去生成一个新数据,并且这两个数据会永久的建立关系,还会建立缓存,当数据没有改变的时候,不会重新计算而是直接使用缓存中的值。 - 初始化
computed
计算属性时会实例化一个watcher
,这个watcher
有一个lazy:true
标识,代表计算属性。还有一个默认dirty:true
的标识,为true时才会让计算属性执行。当第一次取值时会执行,执行完毕后会将dirty
改为false
,也就是说下次再取值不会执行,会直接使用缓存的值。只有当计算属性所依赖的属性发生变化时,会将dirty
改为true computed
的底层还是使用Object.defindProperty
来实现视图更新。watch
一般用于异步、定时任务、或者开销较大(耗时)的操作。watch
中的属性 一定是data 中 已经存在的数据。- 当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,此时就需要
deep
属性对对象进行深度监听(深度遍历和递归)。 - 使用 watch 选项允许我们执行异步操作,并在我们得到最终结果前,进行中间操作。也可以在数据频繁变化时进行自定义的操作如过滤操作。 而计算属性无法做到的。
- 计算属性是必须有返回值的。
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
时才会显示节点,无法进行精准的判断。而且两者连用会在渲染函数中做两次判断,影响了性能。
底层原理:
v-if
在内部使用with
语法,使用三元运算符判断v-if的值的真假。若为真,则渲染节点。若为假,则不渲染。v-show
在内部使用创建指令方式,判断值为假,则改变节点样式display:none
,若为真,则使用原有样式。
5.为什么v-if和v-for不能连用?
因为v-for
比v-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值会发生变化,那么新旧节点在进行比对时容易错位。最终导致渲染出错。
- key主要作用是为了高效得更新虚拟DOM。原理是VUE在patch过程中会通过key精准判断两个节点是否是同一个。从而避免频繁更新不同元素,使得整个patch过程更加高效,减少dom操作量,提高性能。使用
key
,它会基于key
的变化重新排列元素顺序,并且会移除key
不存在的元素。 - 若不设置key,会在列表的渲染过程中发生一些隐蔽的bug
- 重复的
key
会造成渲染错误。
没有key情况还会导致节点的删除和重建,影响性能。若有key,在进行更新时会直接保留原有节点,只插入新节点,无需删除重建。
- v-for循环的时候,key属性只能使用number/string
- key属性值必须是唯一标识符。
- key属性不要使用索引作为标识。
用引索index作为key可能会引发的问题:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
- 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
- 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
7.vue的生命周期介绍
Vue实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载DOM->渲染、更新->渲染、卸载等一系列过程,我们称这是Vue的生命周期。
1.vue生命周期的作用是什么?
它的生命周期中有多个事件钩子,让我们在控制整个vue实例的过程时更容易形成好的逻辑。
在于Vue渲染过程的每个节点给一个通知(事件、回调),便于我们在某一个节点进行一些相关的操作。
2.Vue生命周期总共有几个阶段?
它可以总共分为8个阶段:创建前/后,载入前/后,更新前/后,销毁前/销毁后
3.第一次页面加载会触发那几个钩子?
第一次页面加载时会触发beforeCreate,created,beforeMount,mounted
4.DOM渲染在哪个周期中就已经完成?
DOM渲染在mounted中就已经完成了
5.生命周期的调用流程:
beforeCreate
:实例初始化完成,数据观测(data observer)之前。在这里拿不到实例中的数据。created
:实例创建完成(数据观测完成、计算属性运算完成、method方法绑定、watch和event事件回调绑定),但dom还未挂载,数据还未渲染到页面,拿不到$el(拿不到dom元素)。<create 和 mount 的区别>beforeMounted
:实例挂载之前执行。会调用render函数(调用template)。这个钩子基本用不到。mounted
:实例挂载完成。创建新的vm.$el,把老的el对象删掉。这里可以拿到dom元素。beforeUpdate
:数据更新时调用,发生在虚拟DOM重新渲染和打补丁之前。updated
:当前生命周期更新之后调用。由于数据更改,虚拟DOM渲染完成之后。beforeDestroy
: 实例销毁之前调用。在这时,实例仍然可用。destroyed
:实例销毁之后调用。调用后,实例所有数据监听都会解绑,所有事件监听都会移除,所有子实例也会被销毁。6.生命周期钩子的一些使用方法(重点):
beforeCreate
:可以加个loading事件,在加载实例时候触发。created
:在这里可以结束loading事件,执行异步请求将数据存如data。mounted
:获取到dom节点,可以进行一些dom操作。由于这里能够取到dom,也可以在这里执行异步请求。beforeUpdate
:在这里可以进一步更新状态,不会导致DOM重新渲染。updated
:这里尽量不要再更新状态,否则可能会导致死循环。这里一般用不到。就算用到也是执行一些基于dom的操作,如动画效果。beforeDestroy
:可以执行一些优化操作。如清空定时器,移除绑定事件等。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。
- 有两个属性props:include和exclude,想缓存的内容和不想缓存的内容。max属性:最多缓存多少个。
keep-alive
内插入多个组件只会缓存第一个。
作用:①提升性能,避免每次重新渲染。(但也不宜太多,太多的话占用内存太大得不偿失)②保留状态(数据)
本文固定连接:https://code.zuifengyun.com/2018/10/3335.html,转载须征得作者授权。