深入理解Vue之技巧篇
如何理解MVVM
响应式数据的原理是什么
在Vue2.x中
对象是在getter中收集依赖,在setter中通知依赖更新。
数组是在getter中收集依赖,在拦截器中通知依赖更新。
setter和getter通过Object.defineProperty()
方法定义。
Vue中是如何检测数组变化
数组是在getter中收集依赖,拦截器里触发依赖更新。
所谓的拦截器本质上是在数组和Array之间增加一层prototype,这个原型对象具有splice
、push
等能够改变自身数值的7个方法,当数组调用这些方法时就会使用拦截器的方法而不是原本Array的方法,然后我们就可以在拦截器中通知依赖更新。
nextTick实现原理
nextTick的回调将会在下一个 DOM 更新周期之后执行。
因为promise.then
是微任务,因此将回调函数放到promise.then
中,就能在下次事件循环时触发。
如果不支持promise,则降级用setTimeout
,这是宏任务,缺点是触发延迟时间比微任务长。
这一块设计到事件循环的知识点。
Vue组件的生命周期
Vue2.x
- beforecreate
- created
- beforemount
- mounted
- beforeUpdate
- updated
- beforeDestroy
- destroyed
Vue3.x
- beforecreate
- created
- beforemount
- mounted
- beforeUpdate
- updated
- beforeUnmount
- unmounted
Ajax请求放在哪个生命周期中
最好放到created
里,理由有两点:
- 此时组件还未挂载,可以尽快请求数据。
- created此时data对象已经创建好了,可以将请求数据存到data中。
何时需要使用beforeDestroy
beforeDestroy是组件被销毁前的回调,在此节点可以进行关闭定时器、websocket连接等操作
Vue中computed特点
computed主要是用于计算某些值,它的优点是能够对结果进行缓存,只有当依赖的响应式数据更新时,它才会重新计算。
computed这个特点是通过Watcher实现的。
computed适用于计算结果,而watch是执行回调,computed会对结果进行缓存,而watch不会缓存结果。
watch中的deep:true如何实现
watch的本质其实就是调用this.$watch
监听数据。
如果deep:true,watch会对数组或对象的每一个成员都取值,收集依赖,并进行递归。
Vue事件绑定原理
Vue为组件创建一个事件中心vm._events
对象,当调用vm.$on
监听事件时,将事件名作为键,值是存放回调函数的数组(为了能够注册多个同事件名的回调),当调用vm.$emit
触发事件时,取出对应事件的函数列表,依次遍历调用里面的函数。
Vue中的v-html会导致哪些问题
v-html容易引起XSS 跨站脚本攻击。必须对用户的输入的数据进行转义,否则很容易被攻击。
比如用户输入一段恶意代码<script>恶意代码</script>
。
v-if和v-show的区别
v-if和v-show都是用来控制元素的显示和隐藏,但是两者的实现方式不同。
v-if是通过控制节点的渲染来实现,而v-show是通过控制节点的css属性display
来实现的。
v-if会在渲染时就进行判断,而v-show只有当条件变化时才会进行判断。
v-if和v-show都会改变元素的几何属性,因此都会触发回流。
为什么v-for和v-if不能连用
因为v-for和v-if优先级不同,v-for的优先级高于v-if,也就是说即使只渲染一小部分元素,Vue也会遍历整个列表,这样造成了性能的浪费,并且这样写也容易因为忽视优先级的问题而导致出错。
所以建议使用computed
替代,这样有两点好处好处
- 没有优先级的问题,代码容易理解,可读性强
- computed只有当依赖的响应式数据变化时才会重新计算,提高性能
- 解耦逻辑层,提高代码维护性。
v-model的实现原理及如何自定义v-model
v-model实质上是一个语法糖,它通过value
等属性更新数据,通过input
、change
等事件获取用户的输入。
实现v-model的方式很简单,就是监听input
等事件来更新data状态,通过v-bind绑定value
等html属性
组件中的data为什么是一个函数
如果data是一个函数,那么每次复用组件,都会得到一个新的data,并且这个data对象是私有的,独立的。
如果data直接是一个对象,那么这个组件被其他组件使用时,就会共用同一个data对象。
其实不仅是data,props中default属性如果是引用类型,也要求是一个函数,理由也是如此。
什么是vnode
vnode就是虚拟节点,本质上就是一个描述节点的对象,它具有节点对应的属性。
Vue有三种vnode: 元素节点、注释节点、文本节点。
diff算法的时间复杂度
diff算法是一种通过同层的树节点进行比较的高效算法,避免了对树进行逐层搜索遍历,所以时间复杂度只有 O(n)。diff算法的在很多场景下都有应用,例如在 vue 虚拟 dom 渲染成真实 dom 的新旧 VNode 节点比较更新时,就用到了该算法。diff算法有两个比较显著的特点:
- 比较只会在同层级进行, 不会跨层级比较。
- 在diff比较的过程中,循环从两边向中间收拢
简述diff算法原理
见Patch一章
v-for为什么要用key,为什么不能用index作为key
v-for为什么有key
在使用v-for的时候Vue会建立key和节点下标的对应关系,也就是说Vue通过key找到对应的节点,这样在比较节点时就可以通过key找到对应的旧节点。
为什么key不能是index
如果key是index,假如删除了某个节点或增加了某个节点,那么前面说的那张key与节点下标的对应关系就会出现错误,那么比对节点的时候就会找到错误的旧节点。
事实上,key应该是一个唯一的、不变的值。
为什么需要虚拟节点
虚拟节点是使用是为了更好的比对节点,找出需要更新的节点,从而避免更新整个DOM树,减少DOM操作所带来的性能消耗。
事实上,虚拟节点并不一定能提高性能,如果要更改的节点很少,那么直接调用DOM操作比使用虚拟节点比对对性能的消耗更低。
使用虚拟节点并不完全是为了提高性能,还有一个重要的原因是虚拟节点可以实现跨端。例如vue不仅要实现web页面的开发,还要实现weex app的开发,平台只要根据虚拟DOm调用渲染程序就能绘制出界面。
描述组件渲染和更新过程
模板编译原理
Vue中常用的性能优化
为什么要用异步组件
为何Vue采用异步渲染
Vue组件如何通信
props/$emit
事件总线(Vue3不支持)
vuex
$attrs/$listeners(Vue3$listeners被整合到$attrs)
provide/inject(祖先传后代)
直接操作$parent/$children和$ref
什么是作用域插槽
Vue父子组件生命周期调用顺序
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted ->
父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated ->
父beforeDestroy -> 子beforeDestroy -> 子destroy -> 父destroy