文章

深入理解Vue之技巧篇

如何理解MVVM

响应式数据的原理是什么

在Vue2.x中

对象是在getter中收集依赖,在setter中通知依赖更新。

数组是在getter中收集依赖,在拦截器中通知依赖更新。

setter和getter通过Object.defineProperty()方法定义。

Vue中是如何检测数组变化

数组是在getter中收集依赖,拦截器里触发依赖更新。

所谓的拦截器本质上是在数组和Array之间增加一层prototype,这个原型对象具有splicepush等能够改变自身数值的7个方法,当数组调用这些方法时就会使用拦截器的方法而不是原本Array的方法,然后我们就可以在拦截器中通知依赖更新。

nextTick实现原理

nextTick的回调将会在下一个 DOM 更新周期之后执行。

因为promise.then是微任务,因此将回调函数放到promise.then中,就能在下次事件循环时触发。

如果不支持promise,则降级用setTimeout,这是宏任务,缺点是触发延迟时间比微任务长。

这一块设计到事件循环的知识点。

Vue组件的生命周期

Vue2.x

  1. beforecreate
  2. created
  3. beforemount
  4. mounted
  5. beforeUpdate
  6. updated
  7. beforeDestroy
  8. destroyed

Vue3.x

  1. beforecreate
  2. created
  3. beforemount
  4. mounted
  5. beforeUpdate
  6. updated
  7. beforeUnmount
  8. 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等属性更新数据,通过inputchange等事件获取用户的输入。

实现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比较的过程中,循环从两边向中间收拢

Vue中的diff算法 - SegmentFault 思否

简述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组件如何通信

  1. props/$emit

  2. 事件总线(Vue3不支持)

  3. vuex

  4. $attrs/$listeners(Vue3$listeners被整合到$attrs)

  5. provide/inject(祖先传后代)

  6. 直接操作$parent/$children和$ref

什么是作用域插槽

Vue父子组件生命周期调用顺序

父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted ->

父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated ->

父beforeDestroy -> 子beforeDestroy -> 子destroy -> 父destroy