Reactのソースコードは何度か読んでいますが、毎回忠実に読んでいるわけではありません。PReactの部分だけを学習しました。ソースコードの説明はネット上にたくさんありますが、基本的に古いものばかりなので、自分で整理していきます。 render.js 部分'./constants' から { EMPTY_OBJ, EMPTY_ARR } をインポートします。 './diff/index' から commitRoot、diff をインポートします。 './create-element' から createElement, Fragment をインポートします。 './options' からオプションをインポートします。 /** * Preact仮想ノードをDOM要素にレンダリングする * @param {import('./internal').ComponentChild} vnode レンダリングする仮想ノード * @param {import('./internal').PreactElement} parentDom インポートするDOM要素 * レンダリングする * @param {import('./internal').PreactElement | object} [replaceNode] オプション: 再利用を試みる * `replaceNode` をルートとする既存の DOM ツリー */ エクスポート関数render(vnode, parentDom, replaceNode) { if (options._root) options._root(vnode, parentDom); // `hydrate()` の `replaceNode` パラメータを悪用して、 // DOMの代わりに`hydrate`関数を渡すことでハイドレーションモードかどうかを指定します // 要素.. isHydrating = typeof replaceNode === 'function' とします。 // 同じページに対して `render()` を複数回呼び出すことをサポートできるようにするには // DOMノードを取得するには、前のツリーへの参照を取得する必要があります。 // これはDOMノードに新しい`_children`プロパティを割り当てることで実現されます。 // 最後にレンダリングされたツリーに追加します。デフォルトではこのプロパティは存在しないため、 // は、初めて新しいツリーをマウントすることを意味します。 // DOM ノード上のレンダリング関数への複数の呼び出しをサポートするには、DOM ノードに drinking を追加して、最後のレンダリングを指す仮想 DOM ツリーを取得する必要があります。 // このプロパティはデフォルトで null を指しています。つまり、初めて新しいツリーを装備することになります // したがって、最初は oldVNode は null です (isHydrating の値に関係なく)。ただし、このノードで render を繰り返し呼び出すと、oldVNode に値が割り当てられます let oldVNode = isHydrating ? ヌル : (replaceNode && replaceNode._children) || parentDom._children; // vnode を Fragment でラップし、replaceNode に vnode を割り当て、parentDom の _children = ( (!isHydrating && replaceNode) || 親ドメイン )._children = createElement(フラグメント、null、[vnode]); // 比較後に呼び出す必要があるエフェクトのリスト。 // cdm や cdu など、diff 後にさまざまなライフサイクル処理を実行する必要があるコンポーネントを配置するために使用します。componentWillUnmount は diffChildren の unmount 関数内で実行され、commitRoot が使用されている場合は実行されません。let commitQueue = []; 差分( parentDom, // これは、parentDomの_childrenプロパティを使用して[vnode]を指します。 // 新しいvnodeツリーを決定し、DOM要素に格納します。 // カスタム `_children` プロパティ。 vノード、 oldVNode || EMPTY_OBJ, // 古いツリー EMPTY_OBJ, parentDom.ownerSVGElement !== 未定義、 // extraDomChildren、このパラメータはDOMの再利用に使用されます! isHydrating && replaceNode ? [ノードを置換] : 古いVNode ? ヌル : parentDom.firstChild // parentDom に子ノードがある場合、子ノード全体が再利用されるノードとして使用されますか? EMPTY_ARR.slice.call(parentDom.childNodes) : ヌル、 コミットキュー、 // oldDom、後続のメソッドで挿入位置をマークするために使用されます!isHydrating && replaceNode ? ノードを置換 : 古いVNode ? 古いVNode._dom : 親Dom.firstChild、 水分補給 ); // キューに入れられたエフェクトをすべてフラッシュする // commitQueue 内のすべての nodes_renderCallbacks でメソッド commitRoot(commitQueue, vnode) を呼び出します。 } /** * Preact仮想ノードのデータを使用して既存のDOM要素を更新する * @param {import('./internal').ComponentChild} vnode レンダリングする仮想ノード * @param {import('./internal').PreactElement} parentDom インポートするDOM要素 * アップデート */ エクスポート関数hydrate(vnode, parentDom) { レンダリング(vnode、parentDom、hydrate); } create-context.js 部分コンテキストの使用: プロバイダのpropsに値属性がある Consumerで直接価値を得る 'preact' から {createContext、h、render} をインポートします。 定数FontContext = createContext(20); 関数Child() { <FontContext.Consumer> を返す {fontSize=><div style={{fontSize:fontSize}}>子</div>} </FontContext.Consumer> } 関数App(){ <Child/> を返す } 与える( <FontContext.Provider 値 = {26}> <アプリ/> </FontContext.Provider>, ドキュメント.getElementById('アプリ') ); ソースコードを見てみましょう: './component' から enqueueRender をインポートします。 エクスポートlet i = 0; エクスポート関数createContext(defaultValue, contextId) { contextId = '__cC' + i++; // 一意のIDを生成する 定数コンテキスト = { _id: コンテキストID、 _defaultValue: デフォルト値、 /** @type {import('./internal').FunctionComponent} */ コンシューマー(props, contextValue) { // props.children( を返す // context[contextId] ? context[contextId].props.value : defaultValue // ); props.children(contextValue) を返します。 }, /** @type {import('./internal').FunctionComponent} */ プロバイダー(props) { if (!this.getChildContext) { // 初めて呼び出されたときにいくつかの初期化操作を実行します let subs = []; ctx = {} とします。 ctx[コンテキストID] = this; // diff 操作では、コンポーネントが Consumer 内にあると判断された場合、sub が呼び出されて subscribe されます。 // 同時に、このノードの後続のすべての差分はこのコンテキストを保持し、サブメソッドを呼び出して // コンテキストには階層的な優先順位があり、コンポーネントは最も近いコンテキストに最初に追加されます this.getChildContext = () => ctx; this.shouldComponentUpdate = function(_props) { if (this.props.value !== _props.value) { // ここでの強制的な値の伝播は、`options.debounceRendering` がバイパスされている場合にのみ必要だったと思います: // https://github.com/preactjs/preact/commit/4d339fb803bea09e9f198abf38ca1bf8ea4b7771#diff-54682ce380935a717e41b8bfc54737f6R358 // ただし、これらのケースでは、値を修正しても、すべてのノードが二重にレンダリングされます。 // 強制同期モードを使用しないように伝える方がよいかもしれません。 // 現在、クラス コンポーネントで `useContext()` を使用すると、その `this.context` 値が上書きされます。 // subs.some(c => { // c.context = _props.value; // レンダリングをキューに追加します(c); // }); // subs.some(c => { // c.context[contextId] = _props.value; // レンダリングをキューに追加します(c); // }); // enqueueRender は最終的に renderComponent 関数に入り、diff、commitRoot、updateParentDomPointers などの操作を実行します。subs.some(enqueueRender); } }; this.sub = c => { subs.push(c);// サブスクリプション配列を入力します。 古いものを c.componentWillUnmount にします。 c.componentWillUnmount = () => { // componentWillUnmountをオーバーライドする subs.splice(subs.indexOf(c), 1); (古い) の場合、old.call(c); }; }; } props.children を返します。 } }; // Devtoolsはコンテキストオブジェクトにアクセスする必要がある。 // プロバイダに遭遇します。これはサポートするために必要です // 代わりにコンテキストオブジェクトに `displayName` を設定します // コンポーネント自体に。参照: // https://reactjs.org/docs/context.html#コンテキスト表示名 // createContext は最終的に、Provider と Consumer の 2 つの関数を持つコンテキスト オブジェクトを返します。// 同時に、Consumer 関数の contextType プロパティと Provider 関数の _contextRef プロパティは両方ともコンテキストを指します。 戻り値 (context.Provider._contextRef = context.Consumer.contextType = context); } したがって、プロバイダー コンポーネントの場合、レンダリング時に getChildContext メソッドがあるかどうかが判断されます。ある場合は、globalContext を取得して渡します。 c.getChildContext が null の場合 グローバルコンテキスト = 割り当て(割り当て({}、グローバルコンテキスト)、c.getChildContext()); } if (!isNew && c.getSnapshotBeforeUpdate != null) { スナップショット = c.getSnapshotBeforeUpdate(oldProps, oldState); } isTopLevelFragment = とします tmp != null && tmp.type === Fragment && tmp.key == null; renderResult を isTopLevelFragment とします。tmp.props.children: tmp; diffChildren( 親ドメイン、 Array.isArray(renderResult) ? renderResult : [renderResult], 新しいVNode、 古いVNode、 グローバルコンテキスト、 、 過剰DomChildren、 コミットキュー、 オールドドム、 水分補給 ); レンダリングが Consumer、つまり contextType 属性に遭遇すると、まず Context からプロバイダーを取得し、次にコンポーネントが取得するコンテキスト情報としてプロバイダーの props の値を取得します。同時に、プロバイダーのサブメソッドが呼び出され、サブスクライブします。プロバイダーの shouldComponentUpdate で値が変更されると、すべてのサブスクライバーが enqueueRender 関数に入ります。 したがって、ソース コードでは、globalContext オブジェクトの各キーは Context.Provider を指します。componentContext は、コンポーネントが配置されている Consumer によって渡されたコンテキスト情報、つまり、ペアになっている Provider の props の値を表します。 同時に、Provider の shouldComponentUpdate メソッドは ·this.props.value !== _props.value· を使用します。では、this.props はどこから来るのでしょうか?プロバイダーには関連するプロパティがありません。 重要なのは次の場所です。renderメソッドがないと判断された場合、Compoentを使用してオブジェクトをインスタンス化し、renderメソッドをdoRenderに設定し、コンストラクタはnewType(現在の関数)を指し、doRenderでthis.constructorメソッドが呼び出されます。 // 新しいコンポーネントをインスタンス化する newType に 'prototype' が含まれる場合、newType.prototype.render が返されます。 // @ts-ignore 上記のチェックはnewTypeが構築されることを検証します newVNode._component = c = new newType(newProps, componentContext); // eslint-disable-line new-cap } それ以外 { // @ts-ignore 信じてください、コンポーネントは私たちが望むインターフェースを実装します newVNode._component = c = 新しいコンポーネント(newProps、componentContext); c.コンストラクタ = newType; c.render = doRender; } /** PFC バッキング インスタンスの `.render()` メソッド。 */ 関数doRender(props, state, context) { this.constructor(props, context) を返します。 } 差分部分差分部分はより複雑なので、全体像を整理しました Blog Garden のエディターには、特に Mac で使用する場合にバグが多すぎると本当に不満に思います。たとえば、2 回目のアップロードではコードを送信できず、割り当てと貼り付けは使用できません。 。 。 感情だけが私をここに更新させている PReact10.5.13 ソースコードの理解を助けるこの記事はこれで終わりです。より関連性の高い PReact10.5.13 ソースコードの内容については、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM を応援していただければ幸いです。 以下もご興味があるかもしれません:
|
<<: Windows での Apache+Tomcat7 負荷分散構成方法の詳細な説明
>>: MacにMySQLをインストールするときに忘れたパスワードを変更する方法
この記事では、例を使用して、MySQL ビューの原理と基本操作を説明します。ご参考までに、詳細は以下...
目次物語の始まりvimをインストールし、hadoop-hive.envを編集します。不注意で回避しま...
目次1. 技術概要2. 技術的な詳細1. インターフェースからバックエンドデータを取得する2. フロ...
1. 問題の原因友人の @水米田 から、POSITION に基づくマスタースレーブについて質問があり...
目次シーン紹介プラグインの実装問題1: 重複したヘッダーコンポーネント質問2: 別の実装アイデア質問...
まず、provide/inject を使用する理由について説明しましょう。祖父コンポーネントと孫コン...
ユーザー テーブルを設計するときに、各人の ID 番号が一意であり、検索する必要があるシナリオを想像...
上はシステム時間、下はハードウェア時間です。ここでは変更を加えているので、同じくらいの速さになってい...
ポート マッピングは、Docker を別のコンテナーに接続する唯一の方法ではありません。 Docke...
学習目標: Windowsシステムを使用してMySQLデータベースをインストールする方法を学びます。...
1. Nexusの設定1. Dockerプロキシを作成する外部ネットワーク ウェアハウスからローカル...
検索ミラー docker 検索 rocketmq画像バージョンを表示他の画像を表示したい場合は、画像...
序文ページの HTML 構造にネストされたボックスが多数含まれている場合、ページに複数の垂直スクロー...
この記事では、MySQL のデータベース テーブルの容量を確認するためのコマンド ステートメントを紹...
<br />このセクションでは、XHTML でテキストの書式設定と特殊文字を実装する方法...