1.はじめに
前回の記事で説明しVNode
、私たちが言った時に、VNode
最大の目的は、前と後のデータで真の変化を生成することであるDOM
仮想対応するDOM
ノード、その後、次の2つの新旧を比較することができますVNode
違いは、違いが、その後更新がある場所を見つけるために、DOM
最終的に到達するためには、ノードを最小動作し本当のDOM
ビューを更新する目的。2新旧のコントラストVNode
とプロセスの違いを見つけるが呼び出されるDOM-Diff
プロセス。DOM-Diff
全体の仮想アルゴリズムするとDOM
コアのは、次の、我々はソースコードを開始、での綿密な表情プロセスは次のようです。Vue
DOM-Diff
2.パッチ
ではVue
真ん中、DOM-Diff
プロセスと呼ばれるpatch
プロセス。パッチ、「パッチ」を意味古いを参照するVNode
パッチ、新しい結果としてパッチVNode
カザフスタンの、非常にイメージ。どんなにそれが呼ばれているもの、その本質は、新旧2つの比較するのではないVNode
のプロセスを。私たちは次のことを勉強patch
古いいわゆる:プロセスは、このような考えを把握しなければならない場合VNode
(であるoldVNode
)に対応するデータ変更ビューの前に仮想であるDOM
ノードを、新たなVNode
データの変更が新しいビューに対応する描画できるようにした後、仮想DOM
ノードを、我々は新しい生成する必要があるので、VNode
古いと比較して、基礎をoldVNode
新しい場合、VNode
一部のノードで、古いoldVNode
ではない上、古いoldVNode
へのアドオンで、新しい場合はVNode
無ノード上で、古いoldVNode
そこに、古い中oldVNode
で除去;新しいにおけるいくつかのノードた場合VNode
、古いoldVNode
そこに、新しいVNode
主題は、古い更新oldVNode
、とても新しく、そのVNode
異なります。
たぶん、あなたはそれが問題で、我々ははっきり話している、あなたはそれをこのように解釈することができていない、周りの少しを感じる:あなたは、古いコンピュータは、文書の電子版になりましたがあると、上司はあなたに紙の文書の新しいシートを与えた。この時、およびこれらの文書の両方を使って、文書の新鮮な紙のバージョンは、所有者に送信された文書の新しい電子版を行うには、ドキュメントの紙版を優先するものと、内容のほとんどが同じであることを伝えます。この時点で、この作業のために、次の2つのソリューションを持っている必要があります:1つのオプションは、それが古い文書の内容はすべて削除するもの、であるのかどうかで、その後、単語単位に紙文書の新しいバージョンに対してノック、このプログラムは、手数料の脳はポイントで問題を解決することができます疲れているんです。別のプログラムは、新しいドキュメントやマニュアルがない旧内の特定の部分がある場合、違いは何文書の紙のバージョンで文書の新しい電子版の古い外観に比べ、ベンチマークとしてドキュメントの新しい紙のバージョンに基づいていますが、その後、文書の内部の古い部分でそれらを一緒に入れて、文書の特定の部分がなかったし、そこに新たな文書の古い場合は、古い文書でこれらの部分を削除し、古いのいくつかの部分と新規場合ドキュメントは、更新が必要であるかどうかを確認し、古い文書の最終更新見て、紙の文書、完璧なソリューションと同じの手に最終版に到達するために比較し、持っています。
上記の二つの方式を対比し、明らかにあなたVue
賢いとして、2番目のオプションを選択します。対応するビューでレンダリングされた古い文書の電子版で、第2のプログラムはoldVNode
、紙に対応する文書の新しいバージョンは、新しい観点でレンダリングされますVNode
。言葉では:新しいVNODEベース、古いoldVNodeの変換は新しいVNODEのようにそれを作る、これは物事を行うには、パッチプロセスです。
それは真実ではない、のように、それは非常に複雑な気持ちのように聞こえる、と言った、我々はそれについて考える、全体がpatch
より多くの3つのことを行うよりも何もありません。
- ノードを作成します:新しい
VNode
古いはありながらoldVNode
、古いでは、ないoldVNode
創造。 - ノードを削除するには:新しい
VNode
いいえ古いものはoldVNode
古いからそれを持ってoldVNode
削除。 - 更新ノード:新しい
VNode
と古いoldVNode
の両方で、新しいに関してVNode
被験者は、古い更新しますoldVNode
。
OK、ここに、あなたは正しいVue
にpatch
理解半分のプロセス、次に、我々は、いずれかで見いずれかを分析するVue
方法を行うために、すべての上記の3つのもののために。
ノードを作成します3。
我々が分析前回の記事では、VNode
クラスは、ノードの6種類を記述することができますが、実際には、ノードの3種類のみを作成することができるとに挿入DOM
途中、彼らは以下のとおりです。要素ノード、テキストノード、コメントノード。だから、Vue
あなたが作成したときにノードが新しいを決定しますVNode
古いを持っているoldVNode
。このノードではないの作成とに挿入するために、異なるメソッドを呼び出すために、ノードのどのタイプの一部であるDOM
ミドル。
実際には、それはノードの3種類を備えているので、それは、また、決定することは困難であることは時にソースを判断する方法で非常に明白です。
// 源码位置: /src/core/vdom/patch.js
function createElm (vnode, parentElm, refElm) {
const data = vnode.data
const children = vnode.children
const tag = vnode.tag
if (isDef(tag)) {
vnode.elm = nodeOps.createElement(tag, vnode) // 创建元素节点
createChildren(vnode, children, insertedVnodeQueue) // 创建元素节点的子节点
insert(parentElm, vnode.elm, refElm) // 插入到DOM中
} else if (isTrue(vnode.isComment)) {
vnode.elm = nodeOps.createComment(vnode.text) // 创建注释节点
insert(parentElm, vnode.elm, refElm) // 插入到DOM中
} else {
vnode.elm = nodeOps.createTextNode(vnode.text) // 创建文本节点
insert(parentElm, vnode.elm, refElm) // 插入到DOM中
}
}
上記のコードから、我々は見ることができます:
- 要素ノードは、単純に決定するかどうかを決定する
VNode
ノードが持っているかどうかtag
のタブを。ある場合はtag
要素ノードであると考えられているプロパティを、それから呼び出すcreateElement
方法は要素ノードを作成し、通常はその後、再帰的に作成されたすべての子ノードを横断し、要素ノードの子ノードがあるでしょう、良い後に作成されたすべてのサブノードinsert
最後に、現在の要素ノードの内部に現在の要素ノードDOM
。 - コメントノード、単に裁判官かどうかを決定
VNode
するisComment
プロパティがあるかどうかtrue
、それがされている場合、するtrue
コメントノードに比べて、その後、呼び出しcreateComment
コメントノードを作成する方法を、その後に挿入DOM
真ん中。 - ノードが要素でもコメントノードでもない場合は、テキストノードは、その後、呼び出しと考えられている
createTextNode
テキストノードを作成する方法を、その後に挿入DOM
真ん中。
コードが
nodeOps
あるVue
、すべてのノードは、例えば、パッケージを操作するクロスプラットフォームの互換性にnodeOps.createTextNode()
するブラウザと同等でdocument.createTextNode()
これは次のように全体のフローチャートであり、ノードを作成する動作を終了します。
4. [ノードの削除
その後、新しいノードの一部とはならばVNode
、古いではないoldVNode
、古いからノードへのこれらの必要性そして、そこにoldVNode
削除が。削除ノードは非常に簡単です、単に削除されたノードのことを親要素に呼び出すremoveChild
方法をすることができます。ソースとして、次のとおりです。
function removeNode (el) {
const parent = nodeOps.parentNode(el) // 获取父节点
if (isDef(parent)) {
nodeOps.removeChild(parent, el) // 调用父节点的removeChild方法
}
}
5.更新したノード
ノードおよび削除のノードが比較的単純であり、ノードを更新作成することは比較的複雑ですが、実際にはロジックが理解できるようになるだけでソートから、あまり複雑ではありません。
アップデートのノードが新しい内のノードのいくつかのときでVNode
、古いoldVNode
そこでは、我々は慎重に比較する必要があり、別の場所を更新することがわかります。
我々はノードを更新する前に、まず小さな、静的ノードの概念を導入何ですか?私たちは例を見て:
<p>我是不会变化的文字</p>
任意の変数変数なしで、テキストのみが含まれ、このノード、手段以上のものに関係なく、データの、その後どのように変化し、限りその後、初めてのレンダリングのためのノード、および、後に、その上には決して変更、理由データの変更は、それとは何の関係も発生しないので、それは、任意の変数が含まれていません。私たちは、このノードが静的ノードと呼ばれる呼び出します。
OK、このコンセプトに、我々はノードを更新するために始めていました。我々は、ノードの判断に次の3つの場合を更新する必要があり、個別に対処する場合:
場合
VNode
とoldVNode
静的のノードがあります私たちは、その静的ノードは、その後、治療せずに直接スキップしているにかかわらず、すべての変更の静的なノードのデータはそれとは何の関係もない、と言います。
もし
VNode
テキストノード場合は
VNode
、このノードで表現されたテキストノードは、プレーンテキストのみが含まれている、あなたは簡単に見ることができoldVNode
置けば、それは、二つの異なるテキストを比較するために異なっているかどうか、それがテキストノードであるかどうか、もしそうであればoldVNode
とにテキストでVNode
同じテキスト。場合oldVNode
ではないテキストノード、そしていいえ、それが何であるかは重要で、ダイレクトコールのsetTextNode
テキストノードとのテキストコンテンツにそれを回す方法VNode
と同じ。場合は
VNode
要素ノードであります場合は
VNode
要素ノードがあり、その後、次の2つの状況を細分化:ノードが子を持ちます
新しいノード内の子ノードが含まれている場合、古いノードは、子ノードが含まれている場合は、古いノードを見て時間が、子ノードが含まれ、それは更新の子を再帰的にコントラストノード必要があり、古いノードが含まれていない場合古いノードが子ノードに配置する空のノード新しいノードが古いノードがテキストである場合、ノードは、その後、古いの内部に挿入されて作成している場合、子ノードは、古いノードは、空のノードまたはテキストノードがあるかもしれませんノード、空のテキストを入れて、その後、古い内部に挿入されたノードの新しい子ノードのノードを作成します。
ノードが子ノードを持ちません
それがテキストノードではありませんしながら、ノードは、子ノードが含まれていない場合、それは関係なく、古いノードの前に、直接空にすることができているものはありません、それは簡単にハンドルにある、ノードが空ノードであることを示しています。
OK、以上の3例を終え、ノード、その後、私たちは見ていても、更新の基本的な完成は、特定のソースを達成する方法で、以下のように、ソースコードは次のとおりです。
// 更新节点
function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) {
// vnode与oldVnode是否完全一样?若是,退出程序
if (oldVnode === vnode) {
return
}
const elm = vnode.elm = oldVnode.elm
// vnode与oldVnode是否都是静态节点?若是,退出程序
if (isTrue(vnode.isStatic) &&
isTrue(oldVnode.isStatic) &&
vnode.key === oldVnode.key &&
(isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
) {
return
}
const oldCh = oldVnode.children
const ch = vnode.children
// vnode有text属性?若没有:
if (isUndef(vnode.text)) {
// vnode的子节点与oldVnode的子节点是否都存在?
if (isDef(oldCh) && isDef(ch)) {
// 若都存在,判断子节点是否相同,不同则更新子节点
if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
}
// 若只有vnode的子节点存在
else if (isDef(ch)) {
/**
* 判断oldVnode是否有文本?
* 若没有,则把vnode的子节点添加到真实DOM中
* 若有,则清空Dom中的文本,再把vnode的子节点添加到真实DOM中
*/
if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
}
// 若只有oldnode的子节点存在
else if (isDef(oldCh)) {
// 清空DOM中的子节点
removeVnodes(elm, oldCh, 0, oldCh.length - 1)
}
// 若vnode和oldnode都没有子节点,但是oldnode中有文本
else if (isDef(oldVnode.text)) {
// 清空oldnode文本
nodeOps.setTextContent(elm, '')
}
// 上面两个判断一句话概括就是,如果vnode中既没有text,也没有子节点,那么对应的oldnode中有什么就清空什么
}
// 若有,vnode的text属性与oldVnode的text属性是否相同?
else if (oldVnode.text !== vnode.text) {
// 若相同:用vnode的text替换真实DOM的文本
nodeOps.setTextContent(elm, vnode.text)
}
}
上記のコードのコメントは非常に明確に書かれている、と我々は全体のプロセスのフロー・チャートを整理するフローチャートを描く次のとおりです。
フローチャートやコードを照射することによって、私は、論理ノード更新の一部は、あなたが簡単に理解できると信じています。
また、あなたは、古いものと新しいものならば、気づいたかもしれませんVNode
含まれている子ノードでは、コード内で子ノードを更新するため呼び出すupdateChildren
方法を、そして最後に、このアプローチの論理は、我々は次の記事で学ん拡大置くものです。
6.まとめ
この記事では、導入されたアルゴリズムを:パッチプロセスを。私たちはまず、アルゴリズムによってコーミング全体を理解するために、プロセス全体アルゴリズムの考え方を導入したノード、削除ノード、更新ノードを作成するには:プロセス、すなわち、3つのことをやりました。そして、すべてのコントロールソースは、論理フロー図を示す詳細な調査を開始しました。ノードを更新することに加えて、新旧の場合はノードが含まれている子で、私たちは次の記事で学び始めた子ノードの更新処理について詳細なサブノードを更新する必要があります。(終わり)Vue
DOM-Diff
patch
VNode