この記事はPReact10.5.13のソースコードを理解するのに役立ちます

この記事はPReact10.5.13のソースコードを理解するのに役立ちます

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 を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • Reactコンポーネントが子コンポーネントを強化する方法
  • React Hooksの使用例
  • React Native の基本原則の深い理解 (Bridge of React Native)
  • React+Koa によるファイルアップロードの実装例
  • Reactフックとzarmコンポーネントライブラリ構成に基づいてh5フォームページを開発するためのサンプルコード
  • React antd タブの切り替えによりサブコンポーネントが繰り返し更新される
  • ReactJs 基礎チュートリアル - 基本編
  • 反応ルーティングでパラメータを渡すいくつかの方法についての簡単な説明
  • React.Childrenの詳しい使い方

<<:  Windows での Apache+Tomcat7 負荷分散構成方法の詳細な説明

>>:  MacにMySQLをインストールするときに忘れたパスワードを変更する方法

推薦する

CSS3で蓮の花が咲くアニメーション効果を実現

まずは効果を見てみましょう:この効果は非常に華やかに見えますが、原理は複雑ではありません。1 枚の花...

nginxリバースプロキシによるセッション障害の問題の解決策

同僚から助けを求められました。バックエンド システムへのログインは成功したものの、システムには正常に...

CSSリストのスライドにより、下部に隠れるのを防ぎ、長い画面モデルの処理に適応します。

1. モバイル端末がリストスライドを処理するとき、WeChat には下部にページに戻るボタンが組み...

mysql 8.0.20 winx64.zip 圧縮版のインストールと設定方法のグラフィックチュートリアル

mysql 8.0.20 winx64.zip圧縮版のインストールチュートリアルは以下のように記録さ...

Dockerでローカルマシン(ホストマシン)にアクセスする方法

質問Docker でローカル データベースにアクセスするにはどうすればよいでしょうか? 127.0....

XHTML でのハイパーリンク タグの使用に関するチュートリアル

ハイパーリンク。「リンク」とも呼ばれます。ハイパーリンクは、私たちが閲覧する Web ページのいたる...

Node.js ファイルのコピー、フォルダの作成、その他の関連操作

NodeJS は次のファイルをコピーします:通常、小さなファイルのコピー操作では、ストリーム パイプ...

mysql5.7.20 のインストールと設定方法のグラフィック チュートリアル (mac)

MySQL 5.7.20のインストールと設定方法のグラフィックチュートリアルをあなたと共有します1...

CSS 前景と背景の自動カラーマッチング技術の紹介 (デモ)

1. カラーマッチング効果のプレビュー下の GIF に示すように、ボタンの背景色が徐々に薄くなると...

MySQL でトランザクションのコミットとロールバックを実装する方法の詳細な例

最近、データベース データのスケジュールされた移行を実行する必要があります。実行プロセス中に何らかの...

Node.jsを理解するのはとても簡単です

目次Node.js の公式紹介Node.jsのコア開発言語ウェブ上の JavaScript と No...

CentOS7 は rpm パッケージを使用して mysql 5.7.18 をインストールします

例示するこの記事は、2017 年 5 月 20 日に MySQL-5.7.18 を使用して作成されま...

JS の配列トラバーサルについて、一般的なループをいくつ知っていますか?

序文基本的なデータ構造として、配列とオブジェクトはさまざまなプログラミング言語で重要な役割を果たしま...

docker を使用して minio と java sdk を構築するプロセスの詳細な説明

目次1minioはシンプル2 Dockerビルド minio 2.1 単一ノード2.2 マルチノード...

MySQL の昇順および降順データソートの実装

データの昇順、降順ソート1. フィールド名による単一フィールドのソート順機能:どのフィールドを基準に...