vue的底层原理面试题
1. diff的比较方式? ok
在采取diff算法比较新旧节点的时候,比较只会在同层级进行, 不会跨层级比较。
如果两个节点都是一样的,那么就深入检查他们的子节点。
果两个节点不一样那就说明 Vnode 完全被改变了(ul和div节点不一样),
就可以直接使用新节点替换老节点。【他们的子代不会进行比较了】
<div>
<p>123</p>
</div>
<div>
<span>456</span>
</div>
上面的代码会分别比较同一层的两个div以及第二层的p和span,但是不会拿div和span作比较。
2. virtual DOM和真实DOM的区别? ok
virtual DOM是将[真实的DOM的数据]抽取出来,以对象的形式[模拟]树形结构。比如dom是这样的:
或者说
<div>
<p>123</p>
</div>
对应的virtual DOM(伪代码):
var Vnode = {
tag: 'div',
children: [
{ tag: 'p', text: '123' }
]
};
(温馨提示: VNode 和 oldVNode 都是对象,一定要记住)
3. patch 函数的分析 ok
function patch (oldVnode, vnode) {
if (sameVnode(oldVnode, vnode)) {
patchVnode(oldVnode, vnode)
} else {
const oEl = oldVnode.el // 当前oldVnode对应的真实元素节点
let parentEle = api.parentNode(oEl) // 父元素
createEle(vnode) // 根据Vnode生成新元素
if (parentEle !== null) {
api.insertBefore(parentEle, vnode.el, api.nextSibling(oEl)) // 将新元素添加进父元素
api.removeChild(parentEle, oldVnode.el) // 移除以前的旧元素节点
oldVnode = null
}
}
// some code
return vnode
}
patch函数接收两个参数 oldVnode 和 Vnode 分别代表旧节点和新节点
如果两个节点都是一样的,那么就深入检查他们的子节点。
如果两个节点不一样那就说明 Vnode 完全被改变了,就可以直接替换 oldVnode 。
4. 如何节点是真实的节点path函数如何处理?
//是虚拟节点还是dom节点,如果是dom节点包装成虚拟节点
if (!isVnode(oldVnode)) {
oldVnode = emptyNodeAt(oldVnode);
}
处理方式:
1.是虚拟节点还是dom节点,如果是dom节点包装成虚拟节点
5. 如何知道是虚拟节点还是DOM节点呢 ?
export default function (oldVnode,newVnode) {
// 通过sel属性或者tag属性是否为空或者undefined说明是DOM节点
if (oldVnode.sel=='' || oldVnode.sel==undefined) {
// 说明第一个参数是DOM节点,
// 通过vnode 将DOM节点包装成虚拟dom节点
}
}
因为真实DOM节点sel属性肯定不是false
再更新节点的时候是:先插入然后再删除
6. 对diff算法的命中查找的解释
1、新前与旧前
命中结果:新前与旧前下标同时下移 ↓
2、新后与旧后
命中结果:新前与旧前下标同时上移 ↑
巧计:1-2相同,只变前面那一个字。
3、新后与旧前
命中结果:移动新前指向的这个节点到老节点的旧后之后 。
4、新前与旧后
命中结果:移动新前指向的这个节点到老节点的旧前前面。
3-4相反,
共同点:都是前开头
注意的点:
1、命中一种就不在往下判断了。如果都命中不了,就创建一个map映射,移动到旧节点最初的的位置
2、如果旧节点先循环完毕==》说明新节点有要插入的节点
7. diff 了解
diff 算法时发生在虚拟Dom之间的。
是新的虚拟dom和老的虚拟dom之间的比较。【注意一下】
然后算出最小量之间的跟新,最后渲染到真正的Dom上
https://www.jb51.net/article/140471.htm
=明天继续======
1. nextTick 的原理
nextTick的原理:将回调延迟到下次 DOM 更新循环之后执行。
2. nextTick是如何实现的
Vue 在更新 DOM 是异步的,当监听数据变化后,vue会开启一个队列。
并缓冲同一个事件在循环中发生【所有数据变化后】,
如果同一个 watcher 被多次触发,只会被推入到队列中一次。
这种缓冲的意义在于,去除重复的数据,可以避免不必要计算和DOM操作。
Vue内部对异步队列尝试使用 Promise.then、
然后时是 MutationObserver 和 setImmediate
如果前两者都不支持使用 setTimeout(fn, 0) 代替。
对当前环境进行不断的降级处理
【mjuːˈteɪ ʃn]】 Mutation 突变;变异;基因突变
【ɪn mi di ət 】
也就是说我们在设置数据的时候,( this.msg = 'some thing' )
Vue并没有马上去更新DOM数据,而是将这个操作放进一个队列中
如果我们重复执行的话,队列还会进行去重操作。
等待同一事件【循环中】的所有数据变化完成之后。
会将事件从队列拿出来。
这样做主要是为了提升性能,
如果循环100次就要更新100次DOM,是非常消耗性能的。
但是如果等【事件循环完成】之后更新DOM,只需要更新1次。
3. nextTick 的流程就是:
1.把回调函数放入callbacks等待执行
2.将执行函数放到微任务或者宏任务中
3.事件循环到了微任务或者宏任务,依次执行callbacks中的回调
=明天继续==============
1. 当数据发生变化时,vue是怎么更新节点的?
在跟新的时候,调用patch函数。
函数会判断是虚拟节点还是DOM节点。
如果是DOM节点,将它包装成虚拟节点。
如果是虚拟节点:
如果两个节点都是一样的,那么就深入检查他们的子节点。
如果两个节点不一样那就说明 Vnode 完全被改变了,就可以直接替换 oldVnode
1. Vue中的双向数据绑定是如何实现的
Vue的双向数据绑定是通过【数据劫持】结合【发布者订阅者】模式来实现的
条件:
1、实现一个数据监听器Observer,能够对数象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
(我的理解:Observer->监听->变化->通知 )
2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图