vue面试真题及答案:绍棠Vue面试整理
vue面试真题及答案:绍棠Vue面试整理computed 内部实现了一个惰性的 watcher 也就是 computed watcher computed watcher 不会立刻求值 同时持有一个 dep 实例。computed 本质是一个惰性求值的观察者。Vue.js的数据驱动是通过MVVM这种框架来实现的,MVVM 框架主要包含三部分:Model View ViewMode数据驱动视图 - Vue MVVMMVVM是Model-View-ViewModel缩写,也就是把MVC中的Controller演变成ViewModel。Model代表数据模型,View代表UI组件,ViewModel是View和Model层的桥梁,数据会绑定到ViewModel层并自动将数据渲染到页面中,视图变化的时候通知viewModel层更新数据。
组件化基础=>(MVVM模型)传统组件,知识静态渲染,更新依赖于操作dom。
Vue的核心理念是数据驱动的理念,所谓的数据驱动的理念:当数据发生变化的时候,用户界面也会发生相应的变化,开发者并不需要手动的去修改dom。
优点:
不需要在代码中去频繁的操作dom了,这样提高了开发的效率,同时也避免了在操作Dom的时候出现的错误。
Vue.js的数据驱动是通过MVVM这种框架来实现的,MVVM 框架主要包含三部分:Model View ViewMode
数据驱动视图 - Vue MVVM
MVVM是Model-View-ViewModel缩写,也就是把MVC中的Controller演变成ViewModel。Model代表数据模型,View代表UI组件,ViewModel是View和Model层的桥梁,数据会绑定到ViewModel层并自动将数据渲染到页面中,视图变化的时候通知viewModel层更新数据。
computed 的实现原理computed 本质是一个惰性求值的观察者。
computed 内部实现了一个惰性的 watcher 也就是 computed watcher computed watcher 不会立刻求值 同时持有一个 dep 实例。
其内部通过 this.dirty 属性标记计算属性是否需要重新求值。
当 computed 的依赖状态发生改变时 就会通知这个惰性的 watcher
computed watcher 通过 this.dep.subs.length 判断有没有订阅者
有的话 会重新计算 然后对比新旧值 如果变化了 会重新渲染。 (Vue 想确保不仅仅是计算属性依赖的值发生变化,而是当计算属性最终计算的值发生变化时才会触发渲染 watcher 重新渲染,本质上是一种优化。)
没有的话 仅仅把 this.dirty = true。 (当计算属性依赖于其他数据时,属性并不会立即重新计算,只有之后其他地方需要读取属性的时候,它才会真正计算,即具备 lazy(懒计算)特性。)
computed 和 watch 有什么区别及运用场景?区别:
计算属性computed :
1. 支持缓存,只有依赖数据发生改变,才会重新进行计算
2. 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
3.computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
4. 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
5.如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。
侦听属性watch:
1. 不支持缓存,数据变,直接会触发相应的操作;
2.watch支持异步;
3.监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
4. 当一个属性发生变化时,需要执行对应的操作;一对多;
5. 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数,
immediate:组件加载立即触发回调函数执行,
deep: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变,注意监听数组的变动不需要这么做。注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异 只有以响应式的方式触发才会被监听到。
运用场景
当我们需要进行数值计算 并且依赖于其它数据时 应该使用 computed 因为可以利用 computed 的缓存特性 避免每次获取值时 都要重新计算。
当我们需要在数据变化时执行异步或开销较大的操作时 应该使用 watch 使用 watch 选项允许我们执行异步操作 ( 访问一个 API ) 限制我们执行该操作的频率 并在我们得到最终结果前 设置中间状态。这些都是计算属性无法做到的。
Vue 中的 key 到底有什么用?key的作用主要是为了高效的更新虚拟DOM,虚拟DOM中的最小量更新使用
key 是给每一个 vnode 的唯一 id 依靠 key 我们的 diff 操作可以更准确、更快速 (对于简单列表页渲染来说 diff 节点也更快 但会产生一些隐藏的副作用 比如可能不会产生过渡效果 或者在某些节点有绑定数据(表单)状态,会出现状态错位。)
diff 算法的过程中 先会进行新旧节点的首尾交叉对比 当无法匹配的时候会用新节点的 key 与旧节点进行比对 从而找到相应旧节点.
更准确 : 因为带 key 就不是就地复用了 在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确 如果不加 key 会导致之前节点的状态被保留下来 会产生一系列的 bug。
更快速 : key 的唯一性可以被 Map 数据结构充分利用 相比于遍历查找的时间复杂度 O(n) Map 的时间复杂度仅仅为 O(1) 源码如下:
function createKeyToOldIdx(children beginIdx endIdx) {
let i key;
const map = {};
for (i = beginIdx; i <= endIdx; i) {
key = children[i].key;
if (isDef(key)) map[key] = i;
}
return map;
}
谈一谈 nextTick 的原理
JS 运行机制
JS 执行是单线程的,它是基于事件循环的。事件循环大致分为以下几个步骤:
所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
主线程不断重复上面的第三步。
主线程的执行过程就是一个 tick,而所有的异步结果都是通过 “任务队列” 来调度。 消息队列中存放的是一个个的任务(task)。 规范中规定 task 分为两大类,分别是 macro task 和 micro task,并且每个 macro task 结束后,都要清空所有的 micro task。
for (macroTask of macroTaskQueue) {
// 1. Handle current MACRO-TASK
handleMacroTask();
// 2. Handle all MICRO-TASK
for (microTask of microTaskQueue) {
handleMicroTask(microTask);
}
}
在浏览器环境中 :
常见的 macro task 有 setTimeout、MessageChannel、postMessage、setImmediate
常见的 micro task 有 MutationObsever 和 Promise.then
异步更新队列
可能你还没有注意到,Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。
如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。
然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。
Vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn 0) 代替。
在 vue2.5 的源码中,macrotask 降级的方案依次是:setImmediate、MessageChannel、setTimeout
vue 的 nextTick 方法的实现原理:
vue 用异步队列的方式来控制 DOM 更新和 nextTick 回调先后执行 microtask 因为其高优先级特性,能确保队列中的微任务在一次事件循环前被执行完毕考虑兼容问题 vue 做了 microtask 向 macrotask 的降级方案
Vue 组件 data 为什么必须是函数 ?因为组件是可以复用的 JS 里对象是引用关系 如果组件 data 是一个对象 那么子组件中的 data 属性值会互相污染 产生副作用。
所以一个组件的 data 选项必须是一个函数 因此每个实例可以维护一份被返回对象的独立的拷贝。new Vue 的实例是不会被复用的 因此不存在以上问题。