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を再コンパイルしてモジュールを追加する方法

Nginx をコンパイルしてインストールするときに、http_ssl_module などの一部のモジ...

Docker に ElasticSearch 6.x をインストールする詳細なチュートリアル

まず、イメージをプルします(またはコンテナを作成するだけで、自然にプルされます)。 docker p...

ElementuiはデータをxlsxとExcelテーブルにエクスポートします

最近、Vue プロジェクトについて知り、ElementUI でデータを xlsx および Excel...

HTML コード内のスペースと空白行についての簡単な説明

HTML コード内の連続するスペースまたは空白行 (改行) はすべて 1 つのスペースとして表示され...

Linux での MySQL データベースのアンインストール

Linux で MySQL データベースをアンインストールするにはどうすればいいですか? 以下では、...

Linux exa コマンド (ls よりも優れたファイル表示エクスペリエンス)

インストールREADMEに従ってインストールしてくださいドキュメントには、exa は Rust で実...

HTML におけるスクリプトの配置に関する簡単な説明

以前は、スクリプトは HTML 内のどこにでも配置できると思っていましたが、今日、要件に取り組んでい...

Vue-CLI3.xはプロジェクトをサーバーに自動的にデプロイします

目次序文1. scp2をインストールする2. テスト/本番環境サーバーのSSHリモートログインアカウ...

Vue 3.0 カスタムディレクティブの使い方

目次1. カスタム指示1. グローバルカスタム指示を登録する2. グローバルカスタム指示を使用する3...

Web 標準アプリケーション: Tencent QQ ホームページの再設計

Tencent QQのホームページがリニューアルされ、Webフロントエンド開発がますます注目を集めて...

MySQL ログイン警告問題の解決策

1. はじめにMySQL にログインすると、次のような警告が表示されることがよくあります。警告: コ...

Nginx 静的ファイル サービスの構成と最適化の詳細な説明

ルートディレクトリとインデックスファイルroot ディレクティブは、ファイルの検索に使用するルート ...

CentOS 7にChromeブラウザをインストールする方法

この記事では、CentOS 7 に Chrome ブラウザをインストールする方法を紹介します。詳細は...

Vue3ルーティングVueRouter4を使用する簡単な例

ルーティングvue-router4 では API の大部分は変更されていないため、変更点のみに焦点を...

Docker Composeのデプロイと基本的な使い方の詳しい説明

1. Docker Composeの概要Compose は、マルチコンテナ Docker アプリケー...