mini-vueレンダリングのシンプルな実装

mini-vueレンダリングのシンプルな実装

序文

現在主流のフレームワークである Vue と React はどちらも Virtual Dom を通じて実装されており、Virtual Dom テクノロジーによってページのレンダリング効率が向上します。 Vue ではテンプレートに HTML コードを記述し、React では内部レンダリング関数に HTML コードを記述します。この関数が jsx を通じてコン​​パイルされると、実際には h 関数が出力されます。これが仮想 DOM です。以下は、実際の DOM をレンダリングする仮想 DOM と更新メソッドの簡単な実装です。

ターゲット

主に以下の3つの機能を実現します。

  • h 関数を通じて Vnode を返します。
  • マウント関数を使用して、仮想 DOM を実際のノードにマウントします。
  • パッチ関数は、newVnodes と oldVnodes を比較して DOM を更新するために使用されます。

最初のステップ:

bodyタグ内にid appを持つノードを作成します。このノードに後で仮想ノードをマウントし、renderer.jsを使用して上記3つの機能を実装します。

<本文>
  <div id="アプリ"></div>
  <script src="./renderer.js"></script>
</本文>

ステップ2:

h 関数を記述して、tag (タグ要素)、props (プロパティ オブジェクト)、children (子ノード) を返します。簡単に言えば、仮想 Dom は通常の JavaScript オブジェクトです。

// レンダラー.js
 
const h = (タグ、プロパティ、子) => {
  戻る {
    タグ、
    小道具、
    子供たち
  }
}

それでは、この h 関数を使用して、次のコードが何を出力するか簡単に見てみましょう。

const vdom = h("div", {クラス: "header"}, [
  h("h2", null, "Hello World"),
  h("h2", {id: 0}, h("span", null, "啦啦啦啦啦")) // props に値がない場合、null を渡す必要があり、省略することはできません]);
コンソールログ(vdom);

下の図からわかるように、h 関数を通じて、ツリー ノードを形成する仮想 Dom である JavaScript オブジェクトが返されます。

では、この vnode を実際のノードにマウントするにはどうすればよいでしょうか?それではステップ3を見てみましょう。

ステップ3:

まず、2 つのパラメータを渡す必要があるマウント関数を作成します。1 つ目は h 関数で返した vnode で、2 つ目のパラメータはこれらの vnode をマウントするノードです。コードを見てみましょう。

const マウント = (vnodes, アプリ) => {
  // vnodes のタグ値 ("div", "h2") を通じてノードを作成します。 // また、将来の更新や追加などを容易にするために、vnodes オブジェクトに実際の DOM を保存します。 const el = vnodes.el = document.createElement(vnodes.tag); 
  // このノードを取得した後、props 値を判断してプロパティを追加します if (vnodes.props) {
    for (let key in vnodes.props) {
      // ここでは、props 内のキー値を取得した後、判断を行います。let value = vnodes.props[key];
      キーが「on」で始まる場合
        // たとえば、ユーザーが onClick="changeData" と記述した場合、イベント リスナー関数として処理されます el.addEventListener(key.slice(2).toLowerCase(), value)
      } それ以外 {
        el.setAttribute(キー、値)
      }
      // 境界処理のための命令やv-ifなどの判断が下にあります}
  }
  // props を処理した後、最後のノードは Children です if (vnodes.children) {
    if (typeof vnodes.children === 'string') {
      // これが文字列型の場合、ノードに直接追加できます el.textContent = vnodes.children
    } それ以外 {
      // このケースは配列型で、子ノードを含み、トラバーサルを通じて子ノードを生成します vnodes.children.forEach(vnode => {
        // 子ノードを現在のノードに再帰的にマウントします mount(vnode, el)
      })
    }
  }
  //最後に、この実際のノードを、app.appendChild(el); で渡したアプリ ノードにマウントします。
}
const app = document.querySelector("#app")
マウント(vdom、アプリ)

mount 関数を使用してマウントした場合の実際の効果を見てみましょう。

これまで、仮想 DOM を通じて実際の DOM を作成してきました。これらの DOM を Vue で更新するにはどうすればよいでしょうか。次に、パッチ関数 (update) を作成します。

ステップ4:

パッチ関数を通じて、新しい仮想 DOM と古い仮想 DOM の 2 つのパラメータ (vnodes1、vnodes2) を渡す必要があります。新しい仮想 DOM と古い仮想 DOM を比較することで、更新するノードを指定できます。 (ここではキー値は考慮されません。キー値を参照する必要がある場合は、リンクを確認してください: https://www.jb51.net/article/219078.htm)

//patch 関数 n1: 古いノード、n2: 新しいノード // vue ソースコードでは、古い vnode と新しい vnode はそれぞれ n1 と n2 で表されます。const patch = (n1, n2) => {
  // 上記では、マウント関数を通じてノード属性 el を n2 に追加し、それを n2 にバインドしました。const el = n2.el = n1.el
  // まず、2つのタグから始めます。if (n1.tag == n2.tag) {
    // n1とn2は同じタグを持っているので、プロパティを比較します
    定数 n1Props = n1.props || {};
    定数 n2Props = n2.props || {};
    // n1 と n2 からそれぞれプロパティを取得して比較します for (let key in n2Props) {
      // n2 のすべてのキーを取り出し、n2 のキー値が n1 のキー値と同じかどうかを確認します。const n1Value = n1Props[key] || '';
      const n2Value = n2Props[キー] || '';
      (n1値 !== n2値)の場合{
        キーが「on」で始まる場合
          // たとえば、ユーザーが onClick="changeData" と記述した場合、イベント リスナー関数として処理されます el.addEventListener(key.slice(2).toLowerCase(), n2Value)
        } それ以外 {
          el.setAttribute(キー、n2値)
        }
      }
      // 同じ場合は処理は行われません}
    for (let key in n1Props) {
      const oldValue = n1Props[キー];
      if (!(n2Propsのキー)) {
        キーが「on」で始まる場合
          el.removeEventListener(キー.slice(2).toLowerCase(), 古い値)
        } それ以外 {
          el.removeAttribute(キー)
        }
      }
    }
  } それ以外 {
    // タグが異なる場合は、n1 の親ノードを取得します。const n1Parent = n1.el.parentElement;
    // removeChild によって親ノードから古いノードを削除し、n2 を親ノードにマウントします。n1Parent.removeChild(n1.el); //n1.el はマウント関数によってオブジェクトに追加された実際の DOM ノードです。mount(n2, n1Parent)
  }
  // 最後に、比較的複雑な子を処理します。 // 子は文字列または配列になる可能性があるため、最初に文字列を処理する方法を見てみましょう。 const n1Children = n1.children || [];
  定数 n2Children = n2.children || [];
  if (typeof n2Children === "文字列") {
    // 新しいノードのコンテンツが文字列の場合は、innerhtml を直接使用して置き換えます。el.innerHtml = n2Children;
  } それ以外 {
    // 次の状況は、n2.children が配列の場合です if (typeof n1.children === "string") {
      // n1.children は文字列、n2.children は配列 el.innerHtml = ''; // まずノードの内容を確認し、次に新しい内容を追加します mount(n2.children, el)
    } それ以外 {
      // 両方が配列型の場合、キー値はここでは考慮されません const minLength = Math.min(n1Children.length, n2Children.length);
      (i = 0 ; i < minLength ; i++ とします) {
        パッチ(n1Children[i], n2Children[i]);
      }
      (n2Children.length > n1Children.length) の場合 {
        n2Children.slice(minLength).forEach(item => {
          マウント(アイテム、エル)
        })
      }
      (n2Children.length < n1Children.length) の場合 {
        n1Children.slice(minLength).forEach(item => {
          el.removeChild(アイテム.el)
        })
      }
    }
  }
}

上記は patch 関数の単純な実装であり、実際には diff アルゴリズムと呼ばれるものです (もちろん、ここではキー値は考慮されておらず、順番に比較できるのは 2 つだけです)。比較は同じレベルで行われます。次に、更新が成功するかどうかをシミュレートして実証してみましょう。

const vdom = h("div", {クラス: "header"}, [
  h("h2", null, "Hello World"),
  h("h2", {id: 0}, [h("span", null, "啦啦啦啦")]) // props に値がない場合、null を渡す必要があり、省略することはできません]);
const app = document.querySelector("#app")
マウント(vdom、アプリ)
setTimeout(()=> { // 3秒後に新しいVnodeと古いVnodeをパッチに渡す
  const vdom1 = h("div", {クラス: "header"}, [
    h("h3", null, "Hello World"),
    h("span", null, "哈哈哈哈")
  ])
  パッチ(vdom, vdom1)
},3000)

下の図から、仮想 DOM 更新ノードがシンプルに実装されていることがわかります。

要約する

仮想 Dom を実装して実際のノードを生成し、パッチを通じて更新するだけです。ソースコードをもう一度見てみると、Vue のレンダラーがどのように実装されているかがよりよく理解できるようになります。

mini-vueレンダリングの簡単な実装に関するこの記事はこれで終わりです。より関連性の高いmini-vueレンダリングコンテンツについては、123WORDPRESS.COMの過去の記事を検索するか、以下の関連記事を引き続き閲覧してください。皆様、今後とも123WORDPRESS.COMを応援してください。

以下もご興味があるかもしれません:
  • Vue+ElementUIは、フォームの動的レンダリングと視覚的な構成の方法を実現します。
  • vue.js での v-for ループレンダリングに関する簡単な説明
  • Vueはページデータのレンダリングが完了した後にメソッドを呼び出します。
  • Vue はデータのレンダリング後にスクロールバーの位置制御を実装します (推奨)
  • Vueが次のテーブルを介して配列を変更し、ページがレンダリングされない問題を解決します

<<:  Ubuntu 上で WebRTC ベースの複数人ビデオチャット サービスを構築するための詳細なコード

>>:  MySQLにおけるテーブルインデックスの定義方法と導入

推薦する

HTML におけるメタの役割について (インターネットから収集および分類)

W3Cschoolではこのように説明しています<meta> 要素は、検索エンジン向けの説...

HTML要素によるFlashブロックの詳細な例

コードをコピーコードは次のとおりです。 wmode パラメータ:透過モード: z-indexを使用し...

Windows での MySQL 8.0.15 の詳細なインストールと使用のチュートリアル

この記事では、MySQL 8.0.15の詳細なインストールと使用方法のチュートリアルを参考までに紹介...

MySQL UPDATE ステートメントの「典型的な」落とし穴

目次1. 問題のあるSQL文たとえば、次の図のような質問をした人がいました。 問題は次のように要約で...

js のプロトタイプ、プロトタイプ オブジェクト、プロトタイプ チェーンの包括的な分析

目次プロトタイプを理解するプロトタイプオブジェクトを理解するインスタンスプロパティとプロトタイププロ...

Linuxグループの基礎知識ポイントまとめ

1. Linuxグループの基本紹介Linux では、すべてのユーザーはグループに所属する必要があり、...

CSS3 transition-delay属性のデフォルト値が単位なしの0であり無効である問題を解決します

今日は、CSS3 の transition-delay 属性のデフォルト値 0 に単位がないのは無効...

dockerログマウントの問題を解決する

重要なのは、ローカルサーバーに書き込み権限がないことですキーはここにあります(アクセス拒否)。私は肯...

シェルスクリプト nginx 自動化スクリプト

このスクリプトは、nginxの起動、停止、再起動の操作を満たすことができます。 #!/bin/bas...

動的な色切り替えの実装コードをサポートするために、CSS で SVG 画像を参照します。

表示する svg 画像を追加すると、React はファイルが見つからないというメッセージを表示します...

JSはモバイル端末の画面を1つずつ上下にスライドさせる機能を実装します

この記事では、モバイル端末を一度に1画面ずつ上下にスライドさせるためのJSの具体的なコードを参考まで...

CSS3 テキストシャドウ text-shadow プロパティの詳細な説明

テキストシャドウ text-shadow プロパティの効果: 1. 右下隅の影、左下隅の影、左上隅の...

ネイティブ JS で音楽プレーヤーを実装するためのサンプル コード

この記事では主に、次のように共有されるネイティブ JS 音楽プレーヤーのサンプル コードを紹介します...

Dockerモードで起動したTomcatのホームページにアクセスすると404エラーが発生する

シナリオ: docker で tomcat を起動すると (Alibaba Cloud からダウンロ...

webpackを使用してTypeScriptコードをパッケージ化およびコンパイルする方法を教えます

TypeScript バンドルwebpack 統合通常、実際の開発では、ビルド ツールを使用してコー...