Vue3 コンパイルプロセス - ソースコード分析

Vue3 コンパイルプロセス - ソースコード分析

序文:

Vue3 がリリースされてからかなり経ちますが、最近、会社のプロジェクトでVue3 + TypeScript + Vite技術スタックを使う機会があり、私も暇な時間に Vue3 のソースコードを読む時間を取っています。記憶力は悪いペンほど良くないという考えから、ソースコードを読みながらメモを取りました。また、ソースコードを読みたいが理解に苦労しているすべての学生の理解コストを削減するために、ソースコードの読み取りメモをいくつか書きたいと思っています。

Vue2.x のソースコードも簡単に読んでみました。Vue3 の再構築以降、Vue プロジェクトのディレクトリ構造も大きく変わりました。各機能モジュールはpackagesディレクトリに配置され、責任がより明確になり、ディレクトリ名から一目でわかるようになりました。今日は、Vue エントリ ファイルから始めて、単一の Vue ファイルがcompile-coreどのように宣言され、レンダリング関数にコンパイルされるかを見ていきます。

読者の便宜と記事の長さを抑えるため、ソースコードを読む際にあまり注意を払う必要のないロジックは折り返したり、/* ロジックを無視 */ などのコメントで無視したりします。

個人的には、ソース コード分析記事を読むときに、冒頭に長いコード セクションが表示されるのは好きではありません。これは、それを読んでいない学生を簡単に混乱させる可能性があるためです。そこで、このシリーズの記事では、キーコードのフローチャートを描いてみたいと思います。目的は同じで、全員が理解するためのコストを削減できるようにし、同時に生徒が次回自主的に読むときに参照できるフローチャートを提供することです。

1. Vueエントリファイルの解釈

Vue オブジェクトのエントリ ポイント、 packages/vue/index.tsからソース コードの読み取りを開始します。このエントリ ファイルのコードは比較的単純で、 compileToFunction関数が 1 つだけですが、関数本体の内容は非常に重要です。そのため、まずこの関数本体が何を実現するのかを正確に理解するために、画像を見てみましょう。

フローチャートを見た後は、一緒にコードを見てみましょう。この時点で、ほとんどの生徒は図のコードについて明確に理解していると思います。

すべてのコードをスキップして、ファイルの最後の 35 行を見てください。registerRuntimeCompiler 関数registerRuntiomCompiler呼び出され、 compileToFunction関数がパラメーターとして渡されます。このコード行は、フローチャートの先頭に対応しています。compile compileは、依存性注入を通じてruntimeに注入されます。依存性注入は、分離するための巧妙な方法です。この時点で、実行時にcompileコンパイル関数を呼び出すと、現在のcompileToFunction関数が呼び出されます。

コードの 17 行目を見ると、 compile-domライブラリによって提供されるcompile関数が呼び出され、戻り値からcode変数が分解されています。コンパイラ実行後に生成されるコンパイル結果です。コードはコンパイル結果のパラメータの 1 つであり、コード文字列です。例えば

<テンプレート>
  <div>
    こんにちは世界
  </div>
</テンプレート>

この単純なテンプレートをコンパイルすると、 codeによって返される文字列は次のようになります。

const _Vue = Vue return function render(_ctx, _cache) { with (_ctx) { const { openBlock: _openBlock, createBlock: _createBlock } = _Vue return (_openBlock(), _createBlock("div", null, "Hello World")) } }

この魔法のcompile関数の内部にある謎については、後ほど詳しく説明します。

このコード文字列の結果を取得した後、コードを見ていきます。25 行目ではrender変数を宣言し、生成されたコード文字列 code をnew Functionコンストラクターのパラメーターとして渡します。これはフローチャートの最後から 2 番目のステップで、 render関数を生成します。上記に入力したcode文字列をフォーマットすると、 render関数が、スコープ チェーンを拡張する関数を返すカリー化された関数であることがわかります。

最後に、エントリ ファイルはrender変数を返し、レンダリング関数をキャッシュします。

上記のソースコードの 1 行目を見ると、エントリ ファイルがcompileToFunction関数によって生成されたrender関数をキャッシュするためのcompileCacheオブジェクトを作成し、 templateパラメータをキャッシュ キーとして使用し、11 行目にキャッシュの判断を行う if 分岐があることがわかります。テンプレートが以前にキャッシュされていた場合、テンプレートはコンパイルされなくなり、キャッシュ内のrender関数が直接返されてパフォーマンスが向上します。

この時点で、 package/vue/index.tsのエントリ ファイルが解釈されています。最も興味深い部分は、コード文字列をコンパイルするために compile 関数を呼び出すことであることは皆さんもご存知だと思いますので、次にcompile関数について引き続き説明します。コンパイル機能には、 compile-domcompile-core 2 つのモジュールが含まれます。この記事では、主要なプロセスについてのみ説明します。詳細な分析は後続の記事に掲載されます。コンパイルの実行プロセスを見てみましょう。

2. コンパイル処理

compile関数は、 baseCompile関数の結果を直接返します。実行中、 baseCompile関数は AST 抽象構文木を生成し、 transformを呼び出して各 AST ノードを処理します。たとえば、vOn、v-if、v-for などの命令を変換します。最後に、処理された AST 抽象構文木は、generate 関数によって使用され、上記のコード文字列を生成し、コンパイル結果を返します。この時点で、 compile関数が実行されます。大まかなプロセスを理解したら、ソースコードを見てみましょう。

compile関数のソースコードパスはpackages/compiler-dom/src/index.tsです。compile compileの本体では、 baseCompileの処理結果が直接返されていることがわかります。 baseCompileのソースコード パスはpackages/compiler-core/src/compile.tsです。 baseCompile名前がなぜこのように付けられているのでしょうか? compile-coreコンパイルのコア モジュールであるため、外部パラメータを受け入れてルールに従ってコンパイルを完了します。compile-dom は、特にブラウザー シナリオでのコンパイルを処理するために使用されます。このモジュールでエクスポートされるコンパイル関数は、エントリ ファイルが実際に受け取るコンパイル関数です。 compile-domの compile 関数も、 baseCompileよりも高レベルのコンパイラです。たとえば、Vue が iOS または Android 上の weex などのネイティブ アプリで動作する場合、 compile-dom関連するモバイル コンパイル ライブラリに置き換えられることがあります。

以下の baseCompile 関数を見てみましょう。

まず、関数宣言から、 baseCompile template template と上位レベルの高レベルコンパイラによって処理されたコンパイルoptions options を受け取り、最後にCodegenResult型のコンパイル結果を返します。

エクスポートインターフェースCodegenResult {
  コード: 文字列
  プリアンブル: 文字列
  ast: ルートノード
  マップ?: RawSourceMap
}

CodegenResultのインターフェース宣言を通じて、返された結果にcode文字列、処理された AST 抽象構文ツリー、および sourceMap が含まれていることが明確にわかります。

上記のソース コードの 12 行目を見て、 templateが文字列かどうかを判断します。文字列の場合は文字列が解析され、文字列でない場合はtemplate直接 AST として使用されます。実際、私たちが通常記述する単一ファイルの Vue コードは文字列として渡されます。

次のソースコードは 16 行目です。これは、 transform関数を呼び出し、命令変換やノード変換などのツール関数を渡して、テンプレートによって生成された AST を変換します。

最後に、32 行目で、変換された AST を生成に渡し、 CodegenResult型の戻り結果を生成します。

compile-core モジュールでは、AST 解析、 transformcodegencompile 、およびparse関数はすべて独立した小さなモジュールです。内部実装は非常に洗練されており、コンパイラに関する今後の記事で 1 つずつ紹介されます。

この記事では、エントリ ファイルから始まるコンパイルの一般的なプロセスについて説明します。コンパイラのこのモジュールのコードを読むときに、誰もがプロセスを明確に理解できるようにし、フローチャートとともに読むとさらに興味深くなることを願っています。

これで、Vue3 コンパイルプロセス - ソースコード分析に関するこの記事は終了です。Vue3 コンパイルプロセスの関連コンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • アイデアコンパイラvueインデントエラー問題シナリオの分析
  • Vue でリッチテキストエディタ wangEditor3 を使用する方法
  • Vue3 テンプレートコンパイルの原理に関する深い理解
  • Vue-cliがes6をコンパイルできない問題を解決する
  • Vue プロジェクトのパッケージ化とコンパイルの最適化ソリューション
  • vue プロジェクトをコンパイルする webpack によって生成されるコード探索についての簡単な説明
  • Vue テンプレートのコンパイルの詳細

<<:  Docker インストール Nginx チュートリアル 実装図

>>:  MySQLのデッドロックチェック処理の通常の方法

推薦する

Linux 仮想メモリ設定のチュートリアルと実践

仮想メモリとは何ですか?まずはWikipediaからの紹介文をそのまま引用します。仮想メモリは、コン...

CocosCreator ソースコードの解釈: エンジンの起動とメインループ

目次序文準備行く!文章プロセスを開始するメインループまとめ要約する序文準備皆さんは、こんなことを考え...

WeChatミニプログラムビデオ集中砲火位置ランダム

この記事では、WeChatミニプログラムのビデオ弾幕の位置をランダム化するための具体的なコードを紹介...

CentOS サーバーの時間を北京時間に変更する方法

1. VPSとCentOSシステムを購入しましたが、サーバーの時間が北京時間と一致せず、時差があるこ...

Angularの動的コンポーネントの詳細な説明

目次使用シナリオ達成方法1. 動的コンポーネントを配置する場所2. コンポーネントのインスタンスを取...

MySQL はカスタムシーケンスを使用して row_number 関数を実装します (詳細な手順)

いくつかの記事を読んだ後、ようやく MySQL で row_number() ソートを実装する方法が...

Linux仮想メモリについての簡単な説明

目次起源仮想メモリページングとページテーブルメモリのアドレス指定と割り当て関数プロセスメモリ管理デー...

Vueは透かし効果を簡単に実現します

序文: Vueプロジェクトで透かし効果を使用するには、コンテナを指定できます効果画像: 1. コンテ...

JavaScript の for/of、for/in の詳細な紹介

目次JavaScriptでは、 forループを記述する一般的な方法がいくつかあります。最初の、そして...

HTML タグ dl dt dd 使用方法

基本構造:コードをコピーコードは次のとおりです。 <ダウンロード> <dt>...

WIN10 での JDK インストールと環境変数の設定手順 (詳細版)

目次1. JDKをダウンロードする(例としてjdk1.8.0を使用する) 2. JDK をインストー...

JavaScript の setTimeout と setTimeinterval の使用例の説明

どちらの方法も、一定時間後に JavaScript コードを実行するために使用できますが、それぞれに...

Reactは動的ポップアップウィンドウコンポーネントを実装します

UI コンポーネントを作成するときに、アニメーションを考慮しなければ、アニメーションを実現するのは非...

HTML でフォーム コントロールを無効にする 2 つの方法: readonly と disabled

Web ページを作成する過程では、フォームがよく使用されます。しかし、フォーム上のコントロールを変更...

航空機戦争ゲームを実装するためのネイティブJS

この記事の例では、参考のために航空機戦争ゲームを実装するためのJSの具体的なコードを共有しています。...