>100 Views
October 20, 25
スライド概要
友人と勉強会をした際の資料です。
学生です。
vuejs/core compiler を探索してみた Vue Fes JAPAN 2025前勉強会@🍜 1
前置き • 「何か作ってみた」や「Vueの技術解説系」でも良かったが、良いネタが思いつかなかった • そもそも、普段あまりVuejsを触らない そうだ、Vuejsのコードリーディングを したら面白そう?🤔 2
前置き • スライドにコードリーディングの軌跡を全て載せられないので、 雰囲気を感じてもらえればOKです🙏 3
vuejs/core • Vuejsのコードベース • ほぼTypeScript製、 ESModuels対応済み • 最も古いコミットは2018年9月 18日(by Evan氏) 4
Vue Render Pipeline compiled into Template render function code returns Virtual DOM tree mount / patch Actual DOM trigger re-render component reactive state track dependencies 引用:https://vuejs.org/guide/extras/rendering-mechanism.html 5
compiler周りのpackages @vue/compiler-core Vueテンプレートの解析-> AST(Abstract syntax tree)の構築 ->JavaScript(render関数)の出力、といった一連の処理が 実装されている。 @vue/compiler-dom, -sfc, -ssr @vue/compiler-coreの処理をベースとし、ブラウザやサーバ といった各環境に沿うように、ASTを変換する処理などが ある。 6
@vue/compiler-coreの処理の流れ source codeの入力 render関数の出力 字句解析 AST構築 Tokenizer Parser transform codegen JS/TSへ 変換 ASTのNode を編集 7
@vue/compiler-core - Tokenizer/Parser TokenizerとParserが密に連携しており、 「字句解析をしながら、ASTを構築」している。 8
@vue/compiler-core - Tokenizer/Parser Tokenizer側 1. ソースコードを1文字読み進める(this.index++) 2. 現在のstateに応じて、処理関数を呼ぶ(switch文) a. 意味の区切りを判定し、区切りがあればParserのコールバッ ク関数を呼ぶ b. 次のstateに遷移する 3. 1に戻る(ソースコードを読み切るまで繰り返す) Parser側 コールバックが呼ばれたら 1. Tokenizerで読み進めている箇所に対応するNodeを作成・編集 2. 意味の区切りで、ASTにNodeを付け足す(addNode) 9
Tokenizerの処理 • parse関数がTokenizerのエントリーポイント • ソースコードの文字列を引数で受け取り、 stateに応じて処理関数を呼ぶ 例として、`<div>`という文字列ならど う処理が進むか?考えていこう 10
Tokenizer側の処理 現在のstateは`State.TEXT`である • 現在の文字は`<`なので、最初の条件 がtrueとなる • stateを`State.BeforeTagName`に更新 index++ 11
Tokenizer側の処理 現在のstateは`State.BeforeTagName`である • 現在の文字は`d`なので、3つ目の条件 の`isTagStartChar(c)`がtrueとなる • stateを`State.InTagName`に更新 • 加えて、現在のindexをsectionStartに保 存しておく index++ 12
Tokenizer側の処理 現在のstateは`State.InTagName`である • TagName該当部分を読み進め、`>`、 ` `、または空白ならタグの終了とする • handleTagNameで、Parser側のコールバッ ク関数を呼ぶ(ここで登場!) • stateを`State.BeforeAttrName`に更新 ここがひとつの意味の区切り では、this.cbs.onopentagnameの挙動 を確認しよう 13
Parser側の処理 • タグの名前(=`div`)を取得 • 現在のタグ(currentOpenTag)として、 Nodeのオブジェクトを作成 • 最後に、currentOpenTagをaddNodeで ASTに付け足す。また、スタックにも入れ ておく。 スタックとJSのオブジェクト参照を上手く 利用し、ASTにNodeを追加している 14
@vue/compiler-coreの処理の流れ source codeの入力 render関数の出力 字句解析 AST構築 Tokenizer Parser transform codegen JS/TSへ 変換 ASTのNode を編集 15
@vue/compiler-core - transform 構築したASTのそれぞれのNodeを、よりVueの機 能を意味するNodeへと変更する。 16
@vue/compiler-core - transform 例えば、 `<h1 v-if="...">...</h1>` と `<h1 v-else>...</h1>` の区別がこれまでの段階ではついていない。 タグを発見した時の Parserの処理(再掲) 17
v-if, v-else, v-else-if の例 ⚠️分かりやすさのためだいぶ端折ってます • createIfBranchで`NodeTypes.IF_BRANCH` というtypeのNodeを新たに作成。それを `NodeTypes.IfNode`のbranchesにつめ、既 存のNodeを置き換える • else以下はv-ifに近接する兄弟Node(v- else, v-else-if)のハンドリングをする 18
v-if, v-else, v-else-if の例 エラー処理も見てみよう ```vue <h1 v-if>...<h1> <h1 v-else>...<h1> <h1 v-else>...<h1> (or <h1 v-else-if>...<h1>) ``` • v-else, v-else-ifの一つ上のBranch Nodeが v-elseだったら構文としておかしいので、コ ンパイルエラーとする 19
@vue/compiler-coreの処理の流れ source codeの入力 render関数の出力 字句解析 AST構築 Tokenizer Parser transform codegen JS/TSへ 変換 ASTのNode を編集 20
@vue/compiler-core - codegen 出来上がったASTをもとに、JS/TSファイルを出 力する(render関数を出力する) 21
@vue/compiler-core - codegen 簡単に観察すると... • ESModules対応の環境なのか? • SSRか否か? といったことを考慮している。 22
@vue/compiler-core - codegen • NodeTypesに応じて、生成関数 を当てている 23
@vue/compiler-core - codegen 入力したコードから、どんなrender関数が出力されるか確認できるサイト (大感謝🙏) • 開発者ツールのコンソールからASTも見れるよ https://template- explorer.vuejs.org/#eyJzcmMiOiI4ZGI2PlxuICA8ZGI2PmNvbnRlbnQ8L2Rpdj5cbiAgPGRpdj5jb250ZW50PC9kaXY+XG48L2Rpdj4= 24
まとめ 25
今回のコードリーディングで得られたこと • Vuejsのソースコードがcompilerを通じ、どのようなパスを辿って render関数へ至るか少し知ることができた。 • よくある再帰関数やスタック、抽象構文木がどのように利用され ているか勉強になった。 • 見慣れないようなJSの構文も勉強になった。 等々... 26
終わり 27