まず最初に、上記の少し偏った点を確認する必要があります。JSX で最も単純な Div のみを記述するとします。
<div></div>
Babel は、JSX を次の DOM 式に自動的に変換します。 式:
{ type: ‘’, props: null, children: [] }
ここでの props はデフォルトでは null であることに注意してください。前の記事ではこの属性に注意を払いませんでした。この部分では、Virtual DOM での Props の使用法を説明します。一般的に言えば、どのようなプログラミング環境であっても、Null の発生を避けるように努めなければなりません。そのため、最初に h 関数を変換して、デフォルトで Null の代わりに空のオブジェクトを返せるようにします。 Props
function h(type, props, …children) { return { type, props: props || {}, children };}
そして、それは最終的に次の式に変換されます:
<ul className=”list” style=”list-style: none;”></ul>
各要素props オブジェクト キーは属性名、値は属性値です。一般に、必要なのは setAttribute メソッドを呼び出すだけで、Props のキーと値のペアを DOM 要素に設定します:
{ type: ‘ul’, props: { className: ‘list’, style: ’list-style: none;’ } children: []}
この関数。単一の Prop 値が DOM 要素に設定され、props オブジェクトについては、それを順番に走査するだけで済みます。
function setProp($target, name, value) { $target.setAttribute(name, value);}
要素の作成に使用される createElement メソッドを覚えておく必要があります。要素が正常に作成されるまで setProps メソッドを配置します。その後:
function setProps($target, props) { Object.keys(props).forEach(name => { setProp($target, name, props[name]); });}
焦る必要はありません。それだけでは十分ではありません。 React の初心者向けチュートリアルでは、常に className と class の違いを強調してきました。 setProps では、次のような JS 予約語も置き換える必要があります。
function createElement(node) { if (typeof node === ‘string’) { return document.createTextNode(node); } const $el = document.createElement(node.type); setProps($el, node.props); node.children .map(createElement) .forEach($el.appendChild.bind($el)); return $el;}
さらに、Processing of など、DOM のより一般的なブール属性もあります。チェック済み、無効化など:
<nav className=”navbar light”> <ul></ul></nav>
実際の DOM ノードでは、false が発生した場合、checked 属性が表示されることを望まないため、Props 関数はインテリジェントに判断できなければなりません:
<input type=”checkbox” checked={false} />
最後に、必要なことカスタムの非標準 HTML 属性をフィルタリングすることです。これらの属性は、実際の DOM オブジェクトではなく JS オブジェクトにのみ表示される必要があります:
function setBooleanProp($target, name, value) { if (value) { $target.setAttribute(name, value); $target[name] = true; } else { $target[name] = false; }}
function isCustomProp(name) { return false;}
要約すると、この部分の完全な JSX コードは次のとおりです:
function setProp($target, name, value) { if (isCustomProp(name)) { return; } else if (name === ‘className’) { $target.setAttribute(‘class’, value); } else if (typeof value === ‘boolean’) { setBooleanProp($target, name, value); } else { $target.setAttribute(name, value); }}
Props: Props の比較比較の変更
/** @jsx h */function h(type, props, ...children) { return { type, props: props || {}, children };}function setBooleanProp($target, name, value) { if (value) { $target.setAttribute(name, value); $target[name] = true; } else { $target[name] = false; }}function isCustomProp(name) { return false;}function setProp($target, name, value) { if (isCustomProp(name)) { return; } else if (name === 'className') { $target.setAttribute('class', value); } else if (typeof value === 'boolean') { setBooleanProp($target, name, value); } else { $target.setAttribute(name, value); }}function setProps($target, props) { Object.keys(props).forEach(name => { setProp($target, name, props[name]); });}function createElement(node) { if (typeof node === 'string') { return document.createTextNode(node); } const $el = document.createElement(node.type); setProps($el, node.props); node.children .map(createElement) .forEach($el.appendChild.bind($el)); return $el;}//--------------------------------------------------const f = ( <ul style="list-style: none;"> <li className="item">item 1</li> <li className="item"> <input type="checkbox" checked={true} /> <input type="text" disabled={false} /> </li> </ul>);const $root = document.getElementById('root');$root.appendChild(createElement(f));
次に、古いノードと新しいノードの Props の変更に従って、実際の DOM ノードに適切な変更を加える updateProp 関数を記述する必要があります。以下のタイプがあります。 状況:
上記のルールによれば、Prop を更新する関数は次のとおりであることがわかります。
function removeBooleanProp($target, name) { $target.removeAttribute(name); $target[name] = false;}function removeProp($target, name, value) { if (isCustomProp(name)) { return; } else if (name === ‘className’) { $target.removeAttribute(‘class’); } else if (typeof value === ‘boolean’) { removeBooleanProp($target, name); } else { $target.removeAttribute(name); }}
を更新する関数であることがわかります。単一の Prop は依然として非常に単純です。つまり、削除と設定を組み合わせると、Props に展開され、次の関数が得られます:
function updateProp($target, name, newVal, oldVal) { if (!newVal) { removeProp($target, name, oldVal); } else if (!oldVal || newVal !== oldVal) { setProp($target, name, newVal); }}
同様に、updateElement 関数に update 関数を追加する必要があります:
function updateProps($target, newProps, oldProps = {}) { const props = Object.assign({}, newProps, oldProps); Object.keys(props).forEach(name => { updateProp($target, name, newProps[name], oldProps[name]); });}
Events
function updateElement($parent, newNode, oldNode, index = 0) { ... } else if (newNode.type) { updateProps( $parent.childNodes[index], newNode.props, oldNode.props ); ... }}
イベント ハンドラーを設定することは Prop を追加することであることがわかりますが、 name は on で始まり、次の関数を使用して、Prop がイベントに関連しているかどうかを判断できます:
<button onClick={() => alert(‘hi!’)}></button>
イベント タイプを決定した後、イベント名を抽出できます:
function isEventProp(name) { return /^on/.test(name);}
これを見て、直接配置することを検討できます。 setProps 関数と updateProps 関数のイベント処理ですが、ここで問題が発生します。 diffProps の場合、2 つの関数を比較するのは困難です:
したがって、すべてのイベント タイプ Props をカスタム Props としてみなします。上で述べたように、それは機能します:
function extractEventName(name) { return name.slice(2).toLowerCase();}
そして、イベント応答関数を実際の DOM ノードにバインドすることも非常に簡単です:
function isCustomProp(name) { return isEventProp(name);}
同じ関数を createElement に追加する必要があります:
function addEventListeners($target, props) { Object.keys(props).forEach(name => { if (isEventProp(name)) { $target.addEventListener( extractEventName(name), props[name] ); } });}
イベントの再追加: イベントをリセットします応答
function createElement(node) { if (typeof node === ‘string’) { return document.createTextNode(node); } const $el = document.createElement(node.type); setProps($el, node.props); addEventListeners($el, node.props); node.children .map(createElement) .forEach($el.appendChild.bind($el)); return $el;}
function changed(node1, node2) { return typeof node1 !== typeof node2 || typeof node1 === ‘string’ && node1 !== node2 || node1.type !== node2.type || node.props.forceUpdate;}
最後に、この記事の完全な JSX は次のとおりです:
function isCustomProp(name) { return isEventProp(name) || name === ‘forceUpdate’;}
この時点で、最も単純な Virtual DOM アルゴリズムが完成しましたが、実際の戦闘に使用できる Virtual DOM アルゴリズムにはまだ程遠いです。 :
徹底した分析: 仮想 DOM アルゴリズムの実装方法
私のフロントエンド ストーリー ----React アルゴリズムとは一体何ですか? !
仮想 DOM と差分アルゴリズム: 比較的複雑な仮想 DOM アルゴリズムの実装
simple-virtual-dom: 仮想 DOM の単純な実装
how-to-write-your-own - virtual-dom
仮想 DOM ベンチマーク