一、vue响应式数据的原理
1、 对于对象,在2.X版本中使用Object.defineProperty方法,在get方法中,收集依赖watcher;在set方法中触发数据对应的依赖更新。
二、vue如何监测数组中的变化
- 首先改写了'push','pop','shift','unshift','splice','sort','reverse'七种方法,使得在调用原生方法基础上通知视图更新
- 在调用'push',‘unshift’,'splice'插入数据时,对插入的数据进行观测
- 对于数组的每一项调用this.observeArray(value)进行拦截,如果数组项是对象则进行深度数据观测
说明: arr = [{a: 1}, 2] 当修改arr[0].a = 100, 视图是会更新的;若修改arr[1] = 100,则视图不会更新
三、vue为什么采用异步渲染
- 理解: 如果不采用异步更新,那么每次更新数据就会对当前组件重新渲染。所以为了性能考虑,vue会在本轮数据更新后再去更新视图。
- 原理:dep.notify() 通知watcher进行更新操作 -> subs[i].update() 依次调用watcher的update -> queuewatcher 将watcher去重放入队列中 -> nextTick() 异步晴空watcher队列
四、nextTick的实现原理
-
理解:Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。
-
如果同一个 watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。
-
然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。 Vue 在内部对异步队列尝试使用原生的Promise.then、MutationObserver(h5原生方法) 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。
-
在 vue2.5 的源码中,macrotask降级的方案依次是:setImmediate、MessageChannel、setTimeout
-
vue 的 nextTick 方法的实现原理:
vue 用异步队列的方式来控制 DOM 更新和 nextTick 回调先后执行 microtask 因为其高优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕 考虑兼容问题,vue 做了 microtask 向 macrotask 的降级方案
五、vue中computed的特点
- 默认computed也是一个watcher,具备缓存属性,只有当依赖的属性发生变化时才会更新视图
- watch和computed的内部原理是一样的,computed具有缓存属性,并且会立即执行;配置项
options:{lazy: true}表明是计算属性watcher - watch,不具备缓存属性,如果immedate为false时,初始化不会立即执行,配置项
options:{user: true}表明是用户自定义的watcher
六、watch中的deep:true是如何实现的
- 使用的递归的方法,对对象中的每一项进行求值,会将当前的watcher存入对应的属性依赖中,这样当对象发生变化时也会通知数据更新。所以性能不高
七、v-if 和 v-show 的区别
- v-if:在编译的时候,如果为true则生成ast树;false时创建空节点
- v-show:在编译时生成指令,在指令中
el.style.display= value ?originalDisplay: 'none' - v-for比v-if的优先级高,所以连用的时候会对 循环的每一项进行判断,这样存在性能问题
八、template先转成ast树,然后ast树 通过codegen 生成render函数,render函数内部通过调用_c即createElement方法生成虚拟dom
九、vue中的diff算法
- 首先,对于新旧vNode都有两个指针,newStartIndex,newEndIndex,oldStartIndex,oldEndIndex
- 第一步:头指针指向的节点相同时
sameVnode(oldStartVnode, newStartVnode),则指针同时向后移动 - 第二步:
sameVnode(oldEndVnode, newEndVnode)尾指针指向节点相同时,同时向前移动 - 第三步:
sameVnode(oldStartVnode, newEndVnode)oldVNode指针向右移动,新的指针向左移动 - 第四步:
sameVnode(oldEndVnode, newStartVnode)oldVNode指针向左移动,新的指针向右移动 - 第五步:根据key来查找,newStartIndex从前往后移动,通过key判断当前节点是否存在oldVNode中,如果存在,就将其移动oldStartIndex前一个位置,oldStartIndex还是指向原来的节点,newStartIndex依次向右移动,最后oldVNode删除没有被查找到的节点
- 如果没有key,vue会重新创建dom,存在key只是移动dom,性能得到提高
十、组件中的data为什么是一个函数
*vue.extend是通过一个对象创建了一个构造函数vueComponent, vue组件都是构造函数的实例,如果data是对象,那么所有实例恶斗共享同一个对象;是一个函数通过闭包,避免组件之间的数据互相影响,保证组件数据的独立性,必须使用一个函数返回一个对象作为组件状态
- new Vue({data: {}})这里面的data可以是对象,因为不会被共用
十一、v-model
我们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
- text 和 textarea 元素使用 value 属性和 input 事件;
- checkbox 和 radio 使用 checked 属性和 change 事件;
- select 字段将 value 作为 prop 并将 change 作为事件。
十二、作用域插槽和普通插槽的区别
- 普通插槽渲染到父组件中
- 作用域插槽渲染到组件中