HTMLからReactを実装する方法を教えます

HTMLからReactを実装する方法を教えます

Reactとは

React は、効率的で高速なユーザー インターフェイスを構築するためのシンプルな JavaScript UI ライブラリです。軽量なライブラリなので人気があります。コンポーネント設計パターン、宣言型プログラミングパラダイム、関数型プログラミングの概念に従って、フロントエンドアプリケーションをより効率的にします。仮想 DOM を使用して DOM を効率的に操作します。これは、高位コンポーネントから低位コンポーネントへの一方向のデータ フローに従います。

序文📝

👉 私たちは、JavaScript で高速かつ応答性に優れた大規模な Web アプリケーションを構築するには、React が最適な方法だと考えています。 Facebook や Instagram でのパフォーマンスは良好です。公式ウェブサイトアドレス

React のコンセプトは、大規模なプロジェクトに快速響應です。React 16.8 の新バージョンでは、Web ページの高速応答に関連する問題、つまり CPU のボトルネックを解決するために、新しいコンセプトのfiberが導入されています。従来の Web ブラウジングは、ブラウザーの更新レートや js の実行時間が長いなどの要因によって制限され、ページのフレームがドロップしたり、フリーズしたりします。

Reactは独自の基礎設計によりこの問題を回避しているため、React16.8のリリースではフロントエンド分野で3つのことだけを行います。高速応答、高速応答、またはTMD高速応答です。この記事ではHTMLから始めて、Reactのファイバーコンセプトに従い、非常に基本的なReactを模倣します。

まずは準備作業🤖

html

ページ全体をサポートし、React の動作をサポートする HTML が必要です。ページに<div></div>を追加し、スクリプト タグを追加します。モジュール構築にはimportを使用する必要があるため、スクリプトに type moduleの属性を追加する必要があります。

<!DOCTYPE html>
<html lang="ja">

<ヘッド>
  <メタ文字セット="UTF-8">
  <meta name="viewport" content="width=デバイス幅、初期スケール=1.0">
  <title>ドキュメント</title>
</head>

<本文>
  <div id="ルート"></div>
  <script type="module" src="./index.js" ></script>
</本文>

</html>

コードのデバッグに役立ち、次の操作にも使用されるLive Serverプラグインをインストールすることをお勧めします。

JavaScript

基本的な操作を実装するために、次の反応を模倣し、イベントを<input/>にバインドし、入力値を<h2/>タグに挿入します。

...
関数App() {
  戻る (
    <div>
      <input onInput={updateValue} 値={value} />
      <h2>こんにちは {value}</h2>
      <時間 />
    </div>
  );
}
... 

ReactがBabelでコンパイルされると、 JSX構文はReact.createElement()に変換されます。上記のretuenコードは次のように変換されます。

...
React.createElement() は、
  "div",
  ヌル、
  React.createElement("入力", {
    onInput: 更新値、
    値: 値、
  })、
  React.createElement("h2", null, "Hello", 値),
  React.createElement("hr"、null) は、
);
...

オンラインアドレス

変換されたコードから、React.createElement が複数のパラメータをサポートしていることがわかります。

  • タイプ、ノードタイプ
  • config、idやhrefなどのノードの属性
  • children、子要素。子要素は複数存在できます。タイプは単純なテキストまたは React.createElement です。React.createElement の場合、実際には子ノードであり、子ノードの下に子ノードが存在する可能性があります。このように、React.createElement のネストされた関係を使用して、HTML ノードのツリー構造を実装します。

React.createElementの形式で同じ機能を実現できるcreateElement記述して、単純なデータ構造、つまり虛擬DOMこのようにして、更新時に、新しいノードと古いノードの比較を仮想 DOM の比較に変換することもできます。

{
  タイプ: 'ノードラベル',
  小道具:{
    props:'イベント、クラスなど、ノードのプロパティ',
    children:'ノードの子ノード'
  }
}

ここでは、次の要件を満たす関数を書くことができます。

  • 原則はすべてのパラメータをオブジェクトに返すことです
  • 子要素もpropsに入れる必要があります。そうすれば、コンポーネント内のprops.childrenを通じて子要素を取得できます。
  • 子コンポーネントがテキストノードの場合、 TEXT_ELEMENT型のノード型を構築することによって表されます。
/**
 * 仮想DOM構造を作成する * @param {type} タグ名 * @param {props} プロパティオブジェクト * @param {children} 子ノード * @return {element} 仮想DOM
 */
const createElement = (type, props, ...children) => ({
  タイプ、
  小道具: {
    ...小道具、
    子: children.map(child =>
      子の型 === "オブジェクト"
        ? 子供
        : {
            タイプ: "TEXT_ELEMENT",
            小道具: {
              ノード値: 子、
              子供たち: []、
            },
          }
    )、
  },
});

React ソースコードによる createElement の実装

createElement実装したら、仮想 DOM を取得できますが、コードをページにレンダリングするにはrenderが必要です。このとき、 index.jsを処理し、入力イベントを追加し、import を通じてcreateElementrender導入し、レンダリング時にコンパイルされた仮想 DOM とページのroot要素を渡し、最後にexecuteRender呼び出します。ページがレンダリングされます。ページが更新されたら、 executeRender再度呼び出して更新とレンダリングを行います。

「./mini/index.js」から {createElement,render} をインポートします。
定数 updateValue = e => executeRender(e.target.value);
const executeRender = (値 = "ワールド") => {
  定数要素 = createElement(
    "div",
    ヌル、
    要素を作成します("入力", {
      onInput: 更新値、
      値: 値、
    })、
    createElement("h2", null, "Hello", 値),
    要素を作成します("hr"、null)
  );
  レンダリング(要素、document.getElementById("root"));
};

レンダリングを実行します。

レンダリング時に何が行われますか?

以前のバージョン

render関数は、実際のノードに要素を追加するのに役立ちます。まず、2 つのパラメータを受け入れます。

ルート コンポーネントは実際には JSX コンポーネントであり、createElement によって返される仮想 DOM です。

親ノードは、この仮想DOMをレンダリングする場所です。

React 16.8 より前では、レンダリングは次の手順で行われていました。

  • element.type 型の DOM ノードを作成し、それをルート要素に追加します (テキストノードの特別な処理)
  • 要素のpropsを対応するDOMに追加し、イベントを特別に処理してドキュメントにマウントします(react17ではコンテナにマウントするように調整されています)
  • DOM ノードに element.children ループを追加します。

仮想DOMを取得し、上記の3つのステップを再帰的に実行します。レンダリングされたページは次のプロセスに似ています。

/**
 * 実際のDOMに仮想DOMを追加する
 * @param {要素} 仮想DOM
 * @param {container} 実際のDOM
 */
const render = (要素、コンテナ) => {
  支配させる;
  /*
      処理ノード(テキストノードを含む)
  */
  if (要素の型 !== "オブジェクト") {
    DOM = document.createTextNode(要素);
  } それ以外 {
    DOM 要素を作成します。
  }
  /*
      処理プロパティ(イベントプロパティを含む)
  */
  if (要素.props) {
    オブジェクト.キー(要素.プロパティ)
      .filter((キー) => キー != "子供")
      .forEach((アイテム) => {
        dom[item] = 要素.props[item];
      });
    オブジェクト.キー(要素.プロパティ)
      .filter((キー) => key.startsWith("on"))
      .forEach((名前) => {
        定数eventType = name.toLowerCase().substring(2);
        dom.addEventListener(イベントタイプ、nextProps[名前]);
      });
  }
  もし (
    要素.props &&
    要素.props.children &&
    要素.props.children.length
  ){
    /*
      DOMに追加するためのループ
  */
    element.props.children.forEach((child) => render(child, dom));
  }
  コンテナに子要素を追加します。
};

後バージョン(ファイバー)

上記のコードを書き終えると、この再帰呼び出しに問題があることがわかります。

上記の作業部分は、React では正式には renderer と呼ばれています。Renderer は、サードパーティが独自に実装できるモジュールです。また、reconsiler と呼ばれるコアモジュールもあります。reconsiler の主な機能の 1 つは、diff アルゴリズムです。どのページノードを更新する必要があるかを計算し、更新する必要があるノードの仮想 DOM を renderer に渡します。renderer はこれらのノードをページにレンダリングする役割を担っていますが、同期的です。レンダリングが開始されると、すべてのノードとその子ノードがレンダリングされ、完了するまでプロセスは終了しません。

React の公式プレゼンテーションには、この同期計算によって生じる遅延を明確に示す例があります。

DOM ツリーが大きい場合、JS スレッドの実行時間は比較的長くなる可能性があります。この間、JS スレッドと GUI スレッドは相互に排他的であるため、ブラウザは他のイベントに応答しません。JS の実行中はページが応答しません。この時間が長すぎると、ユーザーに遅延が発生する可能性があります。

この問題は 2 つのステップで解決できます。

  • レンダリング作業を中断できます。優先度の高いジョブが挿入されると、ブラウザのレンダリングが一時的に中断されます。ジョブが完了すると、ブラウザのレンダリングが再開されます。
  • レンダリング作業を小さな単位に分割します。

ソリューションIは新しいAPIを導入します

requestIdleCallback は、ブラウザがアイドル状態のときに呼び出されるコールバックを受け取ります。各呼び出しは、現在のアイドル時間を取得するために IdleDeadline を渡します。options は最大待機時間を渡すことができます。ブラウザがその時間までにアイドル状態でない場合は、強制的に実行されます。

window.requestIdleCallback は、ブラウザのアイドル期間中に呼び出される関数をキューに追加します。これにより、開発者は遅延されたキー イベントに影響を与えることなく、メイン イベント ループでバックグラウンドおよび低優先度の作業を実行できます。

ただし、この API はまだ実験段階であり互換性が低いため、React は独自のセットを正式に実装しました。この記事ではタスクのスケジュールにrequestIdleCallbackを引き続き使用します。

// 次の作業単位 let nextUnitOfWork = null
/**
 * workLoop 作業ループ関数* @param {deadline} 期限*/
関数 workLoop(期限) {
  // 作業ループ関数を停止する必要がありますか? let shouldYield = false

  // 次の作業単位があり、より優先度の高い作業がない場合は、while (nextUnitOfWork && !shouldYield) { をループします。
    次の作業単位 = 実行作業単位(
      次の作業単位
    )

    // 期限が近づいている場合は、作業ループを停止します。関数 shouldYield = deadline.timeRemaining() < 1
  }

  // アイドル時間中に workLoop を実行する必要があることをブラウザに通知します
  リクエストアイドルコールバック(ワークループ)
}
// アイドル時間中に workLoop を実行する必要があることをブラウザに通知します
リクエストアイドルコールバック(ワークループ)

// ユニットイベントを実行し、次のユニットイベントを返す function performUnitOfWork(nextUnitOfWork) {
  //やるべきこと
}

ソリューションII ファイバーデータ構造を作成する

Fiber の以前のデータ構造はツリーであり、親ノードの子ノードが子ノードを指しますが、このポインタだけでは中断と継続を実現できません。たとえば、現在親ノード A があり、A には 3 つの子ノード B、C、D があります。C に移動すると、中断されます。もう一度開始すると、C の下でどれを実行すればよいかわかりません。これは、C しか知らず、その親ノードへのポインターも、兄弟へのポインターもないためです。

Fiber はこのような構造を変換し、親ノードと兄弟ノードへのポインタを追加します。

  • child は子コンポーネントを指します
  • 兄弟は兄弟コンポーネントを指します
  • 親コンポーネントへのポイントを返す

各ファイバーには、最初の子、次の兄弟、および親へのリンクがあります。このデータ構造により、次の作業単位をより簡単に見つけることができます。A Aルートにぶら下がっているノードであると仮定すると、ファイバーのレンダリング順序も次のようになります。

  • ルートから始めて、最初の子ノード A を見つけます。
  • Aの最初の子ノードBを見つける
  • Bの最初の子ノードEを見つける
  • E の最初の子ノードを検索します。子ノードがない場合は、次の兄弟ノードを検索します。E の兄弟ノード F を検索します。
  • F の最初の子ノードを検索します。子ノードも兄弟ノードもない場合は、その親ノードの次の兄弟ノードを検索し、F の親ノードの兄弟ノード C を検索します。
  • Cの最初の子ノードを探すが見つからないので、兄弟ノードDを探す
  • D、Gの最初の子ノードを見つける
  • G の最初の子ノードを検索しましたが、見つかりません。その兄弟ノードを検索しましたが、見つかりません。その親ノード D の兄弟ノードを検索しましたが、これも見つかりません。D の親ノードの兄弟ノードの検索を続行し、最終的にルートを見つけます。
  • 前の手順でルート ノードが見つかり、レンダリングが完了しました。

このデータ構造を使用してファイバーを実装します

//初期ルートファイバーを作成する
 wipRoot = {
  dom: コンテナ、
  プロパティ: { 子: [要素] },
};
作業単位を実行します(wipRoot);

次に、 performUnitOfWorkを呼び出して、ファイバーツリー全体を上から下まで構築します。

/**
 * performUnitOfWork はタスクを実行するために使用されます * @param {fiber} 現在のファイバータスク * @return {fiber} 次のファイバータスク */
const performUnitOfWork = ファイバー => {
  if (!fiber.dom) fiber.dom = createDom(fiber); // マウントするDOMを作成する const elements = fiber.props.children; // 現在の要素の下にあるすべての兄弟ノード // 親ノードがある場合は、現在のノードを親ノードにマウントする if (fiber.return) {
    fiber.return.dom.appendChild(fiber.dom);
  }

  prevSibling を null にします。
  /*
      コードの後半で、ここでロジックを抽出します*/
  if (要素 && 要素の長さ) {
    elements.forEach((要素, インデックス) => {
      定数newFiber = {
        タイプ: 要素.type、
        プロパティ: 要素.props、
        戻り値: ファイバー、
        dom: null、
      };
      // 親の子は最初の子要素を指します if (index === 0) {
        ファイバーの子 = newFiber;
      } それ以外 {
        // 各子要素には次の子要素へのポインターがあります prevSibling.sibling = newFiber;
      }
      前兄弟 = 繊維;
    });
  }
  // 最初に子要素を検索し、子要素がない場合は兄弟要素を検索します // 兄弟要素がない場合は親要素に戻ります // 最後にルートノードで終了します // トラバーサルの順序は上から下、左から右です if (fiber.child) {
    fiber.child を返します。
  } それ以外 {
    nextFiber = fiber;とします。
    (nextFiber) の間 {
      if (nextFiber.sibling) {
        nextFiber.sibling を返します。
      }
      nextFiber = nextFiber.return;
    }
  }
}

バージョン後(調整)

現在のルート

調整は、実際には仮想 DOM ツリーの差分操作です。更新前と更新後のファイバー ツリーを比較します。比較結果を取得した後、変更されたファイバーに対応する DOM ノードのみが更新されます。

  • 不要なノードを削除する
  • 変更されたノードを更新する
  • 新しいノードの追加

ルート ノードが更新される前にファイバー ツリーを保存するための currentRoot 変数を追加し、ファイバーが更新される前にファイバー ツリーを保存するための alternate 属性をファイバーに追加しました。

currentRoot = null とする
関数レンダリング(要素、コンテナ){
    wipRoot = {
        // 代替を省略: currentRoot
    }
}
関数commitRoot(){
    commitWork(wipRoot.child)
    /*
        ファイバーツリーの方向を変更し、キャッシュ内のファイバーツリーをページ内のファイバーツリーに置き換えます。
    */
    現在のルート = wipRoot
    wipRoot = null
}
  • 新しいノードタイプと古いノードタイプが同じ場合は、古いノードDOMを再利用してプロパティを更新します。
  • タイプが異なり、新しいノードが存在する場合は、古いノードを置き換える新しいノードを作成します。
  • タイプが異なる場合、新しいノードはないが古いノードが存在する場合は、古いノードを削除します

和解する子供達

  • performUnitOfWork で新しいファイバーを作成するロジックを reconcileChildren 関数に抽出します。
  • reconcileChildren の新しいファイバーと古いファイバーを比較します。

ファイバーツリーを比較すると

  • 新しいファイバー タイプと古いファイバー タイプが同じ場合は、DOM を保持し、僅更新props,設置effectTag 為UPDATE
  • 新しいファイバータイプと古いファイバータイプが異なり、新しい要素がある場合は創建一個新的dom 節點,設置effectTag 為PLACEMENT
  • 新しいファイバータイプと古いファイバータイプが異なり、古いファイバーが存在する場合は刪除舊fiber,設置effectTag 為DELETION
/**
 * 調整子ノード * @param {fiber} ファイバー
 * @param {elements} ファイバーの子ノード */
関数 reconcileChildren(wipFiber, elements) {
  let index = 0; // 子ノードのインデックス値をカウントするために使用されます let oldFiber = wipFiber.alternate && wipFiber.alternate.child; // 更新時にのみ生成されます let prevSibling; // 前の兄弟ノード while (index < elements.length || oldFiber) {
    /**
     * 子ノードをトラバースします* oldFiber は、更新トリガーか最初のトリガーかを判断します。更新トリガーの場合は、要素の下のすべてのノードが対象となります*/
    新しいファイバーを作成します。
    const 要素 = 要素[インデックス];
    const sameType = oldFiber && element && element.type == oldFiber.type; // ファイバータイプが同じかどうか/**
     * 更新時* 属性が異なる同じタグの場合は、属性を更新します*/
    if (同じタイプ) {
      新しいファイバー = {
        タイプ: oldFiber.type、
        props: element.props, //プロパティのみ更新 dom: oldFiber.dom,
        親: wipFiber、
        代替: oldFiber、
        効果タグ: "UPDATE",
      };
    }
    /**
     * 異なるタグ、つまりタグを置き換えるか、新しいタグを作成します*/
    if (要素 && !sameType) {
      新しいファイバー = {
        タイプ: 要素.type、
        プロパティ: 要素.props、
        dom: null、
        親: wipFiber、
        代替: null、
        効果タグ: "PLACEMENT",
      };
    }
    /**
     * ノードが削除されました */
    if (oldFiber && !sameType) {
      oldFiber.effectTag = "削除";
      削除.push(oldFiber);
    }

    oldFiber の場合、 oldFiber = oldFiber.sibling;
    // 親の子は最初の子要素を指します if (index === 0) {
      // ファイバーの最初の子はその子です wipFiber.child = newFiber;
    } それ以外 {
      // fiber の他の子ノードは、最初の子ノードの兄弟ノードです。prevSibling.sibling = newFiber;
    }
    // 新しく作成された newFiber を prevSibling に割り当てて、newFiber の兄弟ノードを追加しやすくします。prevSibling = newFiber;
    // インデックス値 + 1
    インデックス++;
  }
}

コミット時に、ファイバーノードのeffectTagのプロパティに応じて異なるレンダリング操作が実行されます。

バージョン後(コミット)

commitWork では、ファイバーの effectTag が実際の DOM 操作を処理すると判断されます。

  • ファイバーの effectTag が PLACEMENT の場合、新しいファイバーが追加され、ノードが親ノードに追加されることを意味します。
  • ファイバーの effectTag が DELETION の場合、ファイバーを削除し、親ノードのノードを削除することを意味します。
  • ファイバーの effectTag が UPDATE の場合、ファイバーと props プロパティを更新することを意味します。
/**
 * @param {fiber} ファイバー構造の仮想DOM
 */
関数 commitWork(ファイバー) {
  if (!fiber) 戻り値:
  DOM 親クラスを fiber.parent.dom に変更します。
  if (fiber.effectTag === "PLACEMENT" && fiber.dom != null) {
    domParent.appendChild(fiber.dom);
  } そうでない場合 (fiber.effectTag === "UPDATE" && fiber.dom != null) {
    fiber.dom を更新します。
  } そうでない場合 (fiber.effectTag === "削除") {
    domParent の子を削除します。
  }

  // 子要素と兄弟要素を再帰的に操作する commitWork(fiber.child);
  コミットワーク(fiber.sibling);
}

ここで、 updateDom何が起こるかに注目してみましょう。DOM上で変更された新しい属性と古い属性を取得し、操作を実行します。

/*
    isEvent: イベント属性を取得します。isProperty: ノード以外の、イベント以外の属性を取得します。isNew: 前後で変更された属性を取得します*/
const isEvent = key => key.startsWith("on");
const isProperty = key => key !== "children" && !isEvent(key);
const isNew = (prev, next) => key => prev[key] !== next[key];


/**
 * DOMプロパティを更新 * @param {dom} ファイバーDOM
 * @param {prevProps} ファイバー DOM の古いプロパティ * @param {nextProps} ファイバー DOM の新しいプロパティ */
関数 updateDom(dom, prevProps, nextProps) {
  /**
   * 便利な古い属性* 1. on で始まるイベント属性を取得します* 2. 削除されたイベントを取得します* 3. 削除されたイベントの監視をキャンセルします*/
  オブジェクト.keys(前のプロパティ)
    .filter(イベント)
    .filter(key => !(nextProps 内のキー))
    .forEach(名前 => {
      定数eventType = name.toLowerCase().substring(2);
      dom.removeEventListener(eventType、prevProps[名前]);
    });

  /**
   * 便利な古い属性* 1. 非イベント属性と非子ノード属性を取得します* 2. 削除された属性を取得します* 3. 属性を削除します*/
  オブジェクト.keys(前のプロパティ)
    .filter(プロパティ)
    .filter(key => !(nextProps 内のキー))
    .forEach(key => dom[key]を削除します);

  /**
   * 便利な新しい属性 * 1. 非イベント属性と非子ノード属性を取得します * 2. 前後に変更された属性を取得します * 3. 属性を追加します */
  オブジェクト.keys(次のプロパティ)
    .filter(プロパティ)
    .filter(isNew(前のプロパティ、次のプロパティ))
    .forEach(名前 => {
      dom[名前] = nextProps[名前];
    });

  /**
   * 便利な新しい属性 * 1. on で始まるイベント属性を取得します * 2. 前後に変更されたイベント属性を取得します * 3. 新しく追加されたイベント属性のリスナーを追加します */
  オブジェクト.keys(次のプロパティ)
    .filter(イベント)
    .filter(isNew(前のプロパティ、次のプロパティ))
    .forEach(名前 => {
      定数eventType = name.toLowerCase().substring(2);
      dom.addEventListener(イベントタイプ、nextProps[名前]);
    });
}

DOM に対する一連の操作が完了したら、新しく変更された DOM をページにレンダリングします。input イベントが実行されると、ページは再度レンダリングされますが、このときにファイバー ツリーを更新するロジックに入ります。
図に示すように、代替ポイントは再利用のために前のファイバー ノードを指し、更新操作をより高速に実行します。

ミッション完了!

完全なコードは私の github にあります。

結論と要約💢

結論は

  1. 私たちが書いた JSX コードは、babel によって React.createElement に変換されました。
  2. React.createElement は実際には仮想 DOM 構造を返します。
  3. 仮想 DOM の調整とレンダリングは単純かつ大まかに再帰的に行うことができますが、このプロセスは同期的です。処理する必要のあるノードが多すぎると、ユーザー入力とアニメーションの再生がブロックされ、遅延が発生する可能性があります。
  4. Fiber は 16.x で導入された新しい機能であり、同期調整を非同期調整に変換するために使用されます。
  5. Fiber は、親 -> 最初の子、子 -> 兄弟、子 -> 親などのポインタを使用して、仮想 DOM の構造を変換します。これらのポインタを使用すると、任意の Fiber ノードから他のノードを見つけることができます。
  6. ファイバーは、ツリー全体の同期タスクを、各ノードが独立して実行できる非同期実行構造に分割します。
  7. ファイバーはどのノードからでもトラバーサルを開始できます。トラバーサルは、親->子->兄弟->親の順序、つまり上から下、左から右への深さ優先のトラバーサルです。
  8. ファイバーの調整フェーズは小さな非同期タスクにすることができますが、コミット フェーズは同期する必要があります。非同期コミットでは、ノードが次々に表示されるのをユーザーに見せる可能性があり、これは悪いエクスペリエンスになります。

要約する

  • Reactフックの実装✖
  • React合成イベント✖
  • まだまだ実現できていないことがたくさんあります😤...

ここまで、この記事を読んでくださりありがとうございます。この記事がお役に立てば幸いです。ご質問があれば、遠慮なくご指摘ください。仕事の都合で、この記事を約1か月間断続的に書いてきました。騰訊云TRTC + websocketをベースにした小さなプログラムの電話機能で忙しいです。 時間ができたら記事にして共有します。 もちろん、 react の実装は続きます。

👋: github へジャンプします。スターを付けていただけると嬉しいです。皆さんありがとうございます。

参考文献

🍑: 手書きシリーズ - プラチナレベルのReactを実現する

🍑: 自分で React を構築する (強く推奨)

🍑: React の Fiber アーキテクチャを手書きし、その原理を深く理解する

🍑: シンプルなReactを手書きする

🍑: 妙威教室のDasheng先生がReactのファイバーとフックのアーキテクチャを書きました

🍑: React Fiber アーキテクチャ

🍑: シンプルなReactを手書きする

これで、HTML から React を実装する方法を説明するこの記事は終わりです。より関連性の高い HTML 実装 React コンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後も 123WORDPRESS.COM を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • React HTML で react を使用する 2 つの方法
  • Vue、React、WeChat アプレットのリッチテキストとフィルターの HTML タグを削除する方法
  • HTML ページで React を使用する方法をご存知ですか?

<<:  Nginx 500 内部サーバーエラーの解決方法

>>:  MySQL クラスタの詳細な説明: 1 つのマスターと複数のスレーブのアーキテクチャ実装

推薦する

nginxでgzip圧縮を有効にする手順を完了する

目次序文1. gzip圧縮を設定する2. 詳細設定3. nginxサービスを再起動する要約する序文ウ...

mysql5.6.8 ソースコードのインストールプロセス

カーネル: [root@opop ~]# cat /etc/centos-release CentO...

マウスの尾行効果を実現する JavaScript

マウス効果では、setTimeout を使用して固定時間にノードを生成し、ノードを削除し、生成された...

Tencent インタビュー: SQL ステートメントの実行が非常に遅くなる理由は何ですか? ---後悔シリーズは見ないで(推奨)

正直に言うと、この質問には MySQL のコア知識がかなり必要で、コンピュータ ネットワークの知識を...

Ubuntu環境でxdebugをコンパイルしてインストールする方法

この記事では、Ubuntu 環境で xdebug をコンパイルしてインストールする方法について説明し...

vmware16 仮想マシンに共有フォルダを設定する方法

1. 仮想マシンに共有フォルダを設定します。 1. 処理する仮想マシンを選択し、右クリックして設定...

MySQL操作テーブルでよく使われるSQLのまとめ

1. テーブル内のフィールドの種類を表示する テーブル名を記述する desc テーブル名 2. テー...

Linux で大きなファイルの指定された内容を見つける方法

大きなことも小さなことも考えて、方向転換しましょう。 Linux では非常に大きなファイルに遭遇する...

Vue3 の動的コンポーネントはどのように機能しますか?

目次1. コンポーネントの登録1.1 グローバル登録1.2 グローバルコンポーネントの登録プロセス1...

MySQLテーブル名の大文字と小文字を区別しない設定方法の詳細な説明

デフォルトでは、Linux の MySQL はテーブル名の大文字と小文字を区別します。 MySQL ...

シャドウソックスを使用してLAN透過ゲートウェイを構築する

目次dnsmasq をインストールして設定するChinaDNS をインストールして設定するshado...

MySQLのストレージエンジンの詳細な説明

MySQL ストレージ エンジンの概要ストレージ エンジンとは何ですか? MySQL のデータは、さ...

React+Typescriptはカウントダウンフックメソッドを実装します

まず、setIntervalはフックとしてカプセル化されます👇 'react' から...

CSS 完全な視差スクロール効果

1. 何ですか視差スクロールとは、複数の背景レイヤーを異なる速度で動かすことで、3次元のモーション...

Vueコンポーネントドキュメント生成ツールライブラリのメソッド

目次.vue ファイルの解析文書情報を抽出するコンポーネント名、説明、プロパティ、メソッド、モデルを...