Closed funfish closed 4 years ago
@funfish 最近也在看preact源码,不太理解那边的diffLevel++具体有什么作用,因为在diff方法结尾处还是会--diffLevel。这个diffLevel貌似没可能大于1呀(除非idiff方法内部报错,导致后面的--diffLevel不能执行)。diffLevel并不能代表递归的层级,同时diffLevel还是全局可访问的变量,这个diffLevel具体是什么作用呢。还请帮忙解惑。
@AnthonyYY diffLevel在每次调用 diff 函数的时候都会触发自增,导致深度会大于1的。diff 函数除了在首次渲染时候会进来外,还可能会在组件渲染的时候调用。diffLevel 只是起到其是不是 0 的作用,如果是 0 的话,其最大作用就是 flushMounts 了
@funfish 感谢你的解答,但是我还是不明白diffLevel会在什么时候大于1. diff
方法不管在哪里被调用,调用了多少次。diffLevel
这个值都没有机会大于1吧。简化下diff
方法如下。diff
方法自身没有被递归调用。
let diffLevel = 0;
function diff() {
if (!diffLevel++) {
console.log('init diff');
}
// ... do anything
if (!--diffLevel) {
console.log('flush mount')
}
console.log(diffLevel); // always 0;
}
diff();
diff();
我当前是觉得每次diff
被调用的时候,diff
方法体内的两个if statement
永远为true
,if block always executes.
感觉我大脑思路被卡在什么地方了。还望指点下。
理解了 idiff最终是会执行到diff的
理解了 idiff最终是会执行到diff的
最近preact更新了10.0.0版本,我最近看了这个版本的源码,有兴趣可以一起讨论下
preact 的diff 与 react 的diff 的最大区别是 preact 的diff 是会直接操作 dom嘛?而不是两个虚拟dom 比?
前言
每次看到有人谈起 React 的 diff 机制的时候,总觉得很厉害的样子,所以自然这里也是立马就想介绍 diff 机制。
diff 机制
以下面为例子来介绍:
render 方法的实现如下:
这里面
merge
是需要对比的VNode
节点,vnode
就是传入节点,parent
则是挂载的节点。可以发现传入到render
方法里面,最终还是会调用diff
方法。看看diff
的实现:render
传参merge
,也就是diff
方法传参dom
,是用来和vnode
做diff
的前节点。可以看到上面diff
方法主要作用是生成ret
,并将其挂载到parent
上面去,并在最顶部的递归层,一般componentRoot
是undefined/false
,可以执行所有已经加载的组件的componentDidMount
方法。idiff
的实现如下:这里的
idiff
方法,看着比较复杂,实际上还是对传参 vnode 进行分类判断,分为下面几种情况:这里面第一种情况是最基础的,
vnode
是文本,就要替换掉对应的dom
,第二种情况是组件的方式,这里先不谈。第三种比较麻烦,是多个子节点情况,如若dom
存在并且为文本节点,out
变量就是dom
这个文本节点,否则out
会是vnodeName
的元素空节点,随后将dom
子节点转移到out
下面。接着设置out
的__preactattr_
属性。在第三种情况时,对于前一种简答情况,如果
dom
是<div>123</div>
,而 vnode 的 children 属性为文本的话,例如:vnode = {nodeName: 'SPAN', Children: ['sb'].....}
,则生成的out
为<span>sb</span>
,这种是简单的情况。复杂情况下需要调用到innerDiffNode
方法。在介绍innerDiffNode
之前,先看看idiff
方法最下面的diffAttributes
方法:diffAttributes
方法就是将vnode
里面attribute
和props
属性添加到out
里面,最后返回的是out
元素而不是 vnode!setAccessor
基本就是些条件语句,根据出入的属性名,来分类处理,看看就好了。就这样将 vnode 里面的attribute
属性添加到out
里面。innerDiffNode
idiff
第三种情况的复杂情况下下会调用innerDiffNode
方法,实际上就是对vnode
的子元素和out
的子元素进行递归对比。先看看innerDiffNode
的实现:innerDiffNode
的目的就是要vnode
的每个vchild
和能与其对应上的out
下面的child
进行对比,也就是调用diff
方法,从而实现子节点之间的对比。在innerDiffNode
里面对比找出child
的过程,看上面代码中的解释就好了。在通过idiff
方法生成新的child
后,child
会被加入到out
里面。从而一步步将vnode
的children
移入作为out
的节点。在遍历了所有的vnode
的children
之后,还需要对下面两种out
的子节点移除:key
属性的节点,如果没有匹配上 vnode 的 children ,需要移除;总结
diff 机制基本就是不断的遍历子节点和 vnode,来实现对比不同。将 vnode 里面的内容添加到 dom 里面,而将 dom 里面不需要的多余的子节点移除掉。所以这里还需要理解整体的移除机制,以及组件生成对比的机制,将在下篇文章里面介绍到。