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におけるテーブルインデックスの定義方法と導入

推薦する

MySQL のメモリ使用量と CPU 使用率が高い場合のテストと解決策

変更後: innodb_buffer_pool_size=576M ->256M InnoDB...

MySQL 学習ノート ヘルプ ドキュメント

システムヘルプを表示help contents mysql> ヘルプコンテンツ; ヘルプ カテ...

3つの簡単な調整でMySQLを最適化する

私は熟練した DBA になるつもりはありませんが、MySQL を最適化するときは、いくつかの構成を調...

react+antd.3x は IP 入力ボックスを実装します

この記事では、IP入力ボックスを実装するための react+antd.3x の具体的なコードを参考ま...

mysql8.0.19 の基本データ型の詳細な説明

MySQL 基本データ型一般的な MySQL データ型の概要 ![1036857-201708011...

タイムライン効果を実現するCSS3

最近、コンピューターの電源を入れたところ、Geek Academy が新規ユーザーに 1 か月の無料...

JS でオブジェクトが空オブジェクトかどうかを判断する 5 つの方法

1. jsonオブジェクトをjson文字列に変換し、文字列が「{}」であるかどうかを判断します。 v...

HTML の長いテキストは、タグの幅を超えると自動的に切り捨てられます。

長いテキストを表示する場合、C# 側で文字をインターセプトする必要があることがよくありますが、長いテ...

Docker で Let's Encrypt から永久無料 SSL 証明書を取得する方法

1. 原因公式の cerbot は面倒すぎます。野生の成長よりもさらに悪い acme.sh の使用は...

MySQL の効率的なクエリの左結合とグループ化 (プラス インデックス)

mysql 効率的なクエリMySQL は、左結合の速度を上げるために group by を犠牲にし...

サーバー同時実行数の推定式と計算方法

最近、サーバーのストレステストを再度行う必要が出てきました。ここでは、最近学んだ見積もりスキームと見...

Vue はチャット ボックスで絵文字を送信する機能を実装します

vueチャットボックスで絵文字を送信し、vueインターフェースで絵文字を送信するための具体的なコード...

Docker を使用して ELK 環境を迅速にデプロイする方法の詳細な説明 (最新バージョン 5.5.1)

Linux サーバーに Docker をインストールした後、関連する公式 Docker イメージを...

VUE+CanvasはシンプルなGobangゲームの全プロセスを実現します

序文レイアウトの点では、Gobang はランダムな動きを目的とするゲームよりも実装がはるかに簡単で、...