日が経過しています。情報の鮮度にご注意ください
はじめに
この記事は Vue 2.0公式ドキュメント の「インスタンス」セクションから始め、いくつかの Vue API の使用方法や Vue が特定の機能を実装する原理について考察します。さらに、私自身の使用感や浅はかな視点から Vue の設計意図を分析する不遜な試みも含まれています。不適切な点があればご容赦ください。Vue に触れて間もないため、誤りがありましたら指摘していただけると幸いです。あらかじめお礼申し上げます。
注意: 基礎知識は省略し、私が説明が必要と考える点のみを述べます。
Vue インスタンス
すべての Vue.js アプリケーションは、コンストラクタを使って Vue のルートインスタンスを作成することで起動します。つまり、各ページのデータはこの1つのインスタンスによって管理されるべきであり、元データの送受信はすべてルートインスタンスを通じて一元管理され、ルートインスタンスは props でデータを配布したり、events でデータを監視したりします。子コンポーネントは watch/computed でデータの変化を監視し、適時更新すればよいのです。
ドキュメントには「すべての Vue.js コンポーネントは実際には拡張された Vue インスタンスである」と書かれています。この文を正しく理解するならば、コンポーネントでもインスタンスと同じメソッドやライフサイクルフックが使える(ただし data を除く)ということです。
コンポーネントの data は関数でなければなりません。コンポーネントは再利用されるため、毎回コンポーネントを呼び出すたびに新しいデータを生成する必要があるからです。
データプロキシ(proxy)とは、Vue インスタンスがその data オブジェクト内のすべての属性を代理することを指します。一方、インスタンスプロパティ $data は data 属性そのものを表し、プロキシされた data と区別されます。
つまり、vm の data 属性が {a: 'xheldon'} の場合、vm.a は 'xheldon' となり、vm.$data は {a: 'xheldon'} となります。
コンポーネントも実際には(拡張された)Vue インスタンスです。以下は簡単な検証です:
list.vue コンポーネント(template と style は省略):
1 | |
props vs data
コンポーネントを初期化する際、prop の属性と data の属性、そして computed のメソッドはすべて Vue インスタンスにバインドされます。しかし、props の属性は data の同名属性よりも優先度が高くなります。以下はその検証です:
1 | |
結果として警告が表示されます(エラーではなく、レンダリングには影響しません):
1 | |

一方、computed で返される関数名と data の属性名は重複可能で、何の警告も出ません。しかし、同名で上書きされた後、初期化時にコンソールで確認すると、_data が _computedWatcher より後にこの重複属性の getter と setter を設定しているようです(これが原因かどうかは不明で、詳細に調査した後でこの記事を修正する予定です)。そのため、上書きが発生します。関連する原理は この紹介記事 で確認できます。
これらはすべて初期化時にインスタンスの属性にバインドされ、同名の computed 属性は上書きされますが、Vue devtool には正しく表示されます)
1 | |

Vue devtool には正しく表示されます:

もし computed のメソッド名と data の属性名が重複しない場合:
1 | |

さらに、methodsとcomputedメソッドの違いは、後者がキャッシュを持つ(つまり、依存するリアクティブデータが変更されない限り再計算されない)こと以外に、両方が補間値を返すことを目的としている場合、methodsのメソッドはfunctionName()として使用され、computedのメソッドはfunctionNameとして参照されます。つまり、前者は関数を実行する必要があり、後者は実行する必要がありません。
その理由は、computedプロパティに記述したメソッドがプロパティとしてVueインスタンスにマウントされ、提供した関数がこのメソッドのgetterとして扱われるためです。
一方、methodsは単なる関数であるため、補間参照であれイベントメソッドであれ、()を使用して呼び出す必要があります。
このため、本当の関数が必要な場合(ここで関数呼び出しを使用する場合、computedは単に値を返すだけでなく、パラメータを渡す必要がある関数を返す必要があります)、methodsが最適です。
以下は、公式サイトとは異なる別バージョンのtodo listです:
テンプレート:
1 | |
ロジック:
1 | |
この例では、このようなテンプレートを使用する場合、buttonをクリックしたときに現在のliを削除するためにパラメータを渡す必要があるため、この状況ではmethodsを使用する必要があります。
computedを使用する場合、パラメータを受け入れません(getterであるため)](http://whereswalden.com/2010/08/22/incompatible-es5-change-literal-getter-and-setter-functions-must-now-have-exactly-zero-or-one-arguments)、たとえ関数を返したとしても:
1 | |
注意: 両方に同名の関数が存在する場合、computedの関数がmethodsよりも優先されます(getterとsetterの処理順序の問題で、詳細はソースコードを参照してください)。この状況は補間参照とイベントバインディングの両方に適用されます。以下は検証です:
補間参照テスト:
テンプレート:
1 | |
1 | |
関数im from computedが出力されます。computedが関数を返さない場合、補間参照で{{a}}のみを使用すると、明らかにcomputedの値が出力されます。検証はここでは省略します。
イベントバインディングの場合:
呼び出し時にインラインハンドラメソッドを使用:
1 | |
ロジック:
1 | |
computedが出力されます。
メソッドイベントハンドラを使用しても結果は同じです:
1 | |
ロジック:
1 | |
computedが出力されます。
イベントバインディングの場合、computedは常に関数を返す必要があります。
したがって、優先順位は次のとおりです:
props > data > computed > methods
computedとmethodsのthis.xxx内の参照優先順位も同じであると推測されます。詳細を確認したい場合は検証してください。
また、どちらの場合もmethodは関数を返す必要がありませんが、computedは常に関数を返す必要があり、どちらの場合もパラメータを受け入れません(getterであるため)。以上のことから、イベント処理にはmethodsを使用し、データバインディング/補間処理にはcomputed(キャッシュがあるため)を使用するのが最適です。
さらに、methodでイベントをバインドする場合、()を付けるか付けないかは同じ効果を持ち、どちらも関数を実行します。違いは次のとおりです:
-
()付きのものをインラインステートメントと呼び、ネイティブイベントとカスタムイベントの2つのケースに分けられます。どちらの場合も引数を渡すことができ、引数リストが空の場合、デフォルト引数argumentsも空となり、デフォルト引数は存在しません。input/clickのようなネイティブイベントによってトリガーされる場合、特別な$eventパラメータをネイティブのeventイベントハンドラとして渡すことができます。一方、カスタムイベントによってトリガーされる場合、イベントハンドラ関数の引数は実際に渡される値に依存し、カスタム関数には特別な$eventオブジェクトは使用できません。$emitでカスタムイベントをトリガーする際に渡される引数は無視されます。 -
()なしのものをメソッドイベントと呼び、これも2つのケースに分けられます:ネイティブイベントの場合、ネイティブのeventイベントが唯一のデフォルト引数として渡されます。カスタムイベントの場合、$emitイベント時にイベント名以外の第2引数から最後の引数まで任意の数の引数が渡されます。
言葉だけでは不十分、コードを見せてください
上記は4つのケースを指しています:
()付きのネイティブイベント
1 | |
()付きのカスタムイベント
1 | |
()なしのネイティブイベント
1 | |
()なしのカスタムイベント
1 | |
ディレクティブとパラメータ(属性)
基本的な使い方:
1 | |
ここで、directive はディレクティブと呼ばれ、propName はディレクティブの「パラメータ」と呼ばれます。実際、パラメータの具体的な表現は html 上の属性です(Vue にはいくつかの組み込みパラメータ/属性があります。例えば v-bind:click="method" の click、v-bind:href="/img/in-post/x.png" の href など。前者はインラインに表示されませんが、後者は必須であるためインライン属性に表示されます。一方、自分で定義したパラメータ/属性は必ずインライン属性に表示されます)。value は変数(ダブルクォーテーション内に書かれていますが)で、ほとんどの場合、親テンプレートから来ます。
propName には修飾子を付けることができ、.prevent のようにデフォルトイベントを禁止するなどの動作を簡単に操作できます。
一部のディレクティブは v-if のように直接使用できますが、v-bind:href/v-on:click のようにパラメータを追加する必要があるものもあります。
注意点として、value にダブルクォーテーションを付けるかどうかは結果に影響しません。つまり、:propName="value" と :propName=value は同じです。特に断りのない限り、以下のすべてのケースに適用されます。
value がブール値に変換された後に false の場合、propName は削除されます。true の場合、propName が表示されます。実際、これは以下のルールに従います(カスタム属性に限ります):
null、undefined、falseのリテラルの場合、propName属性は削除されます。valueが未定義の変数の場合、例えば:propName="wxd"の場合、propNameは削除され、警告が表示されます:
1 | |
-
valueが配列の場合、配列はオブジェクトであるため、propNameは以下の2番目のケースを除いて常に存在します。valueは以下のケースに分けられます: -
:propName =[]、:propName ="[]"、:propName ="['']"の場合、valueは削除され、属性のみが残ります。 -
:propName =[""]の場合、構造が崩れます。 -
:propName =["",""]または:propName ='["",""]'の場合、valueの値は","となります。 -
valueがネストされた配列の場合、その値が一次元化された後、valueまたはその再帰的な子要素にundefinedまたはnullリテラル(文字列内に記述されたものではない)が含まれている場合、その値は空になります。valueまたはその再帰的な子要素にObjectが含まれている場合、その値は[object Object]になります。 -
valueがオブジェクトの場合、propNameは保持され、値は[object Object]になります。 -
valueが数値または文字列の場合(例::propName = "'fff'")、propNameは保持され、valueは文字列または数値の値になります。
ソースコードを確認したところ、実際にこのようなロジックになっています:
1 | |
これらのルールはカスタム属性に限定されます。組み込み属性の場合は異なります。例えば class 属性をバインドする場合:
1 | |
これは、isActive の値が false またはブール値 false に変換可能な他の値の場合、active というクラス名が適用されず、それ以外の場合はクラス名が適用されることを意味します(if文の真偽判定と同じです)。
フィルター
フィルターを連結する場合、最初のフィルターのパラメータは初期値であり、以降のフィルターの最初のパラメータは前のフィルターの戻り値になります。戻り値がない場合は undefined になります。
テンプレート:
1 | |
ロジック:
1 | |
最初の filter を除き、後続の filter は初期値を取得できません。ただし、パラメータを渡したい場合、最初の filter が配列を返すなど、方法はいくらでもあります。
リストレンダリング
v-for でパラメータが2つの場合、ネイティブ js の forEach パラメータと一致し、value, key の順になります。of 演算子と in 演算子の効果は完全に同じです(ネイティブ js では異なりますが)。
もう一つ注意が必要な点は、コンポーネントで v-for を使用する場合、親からコンポーネントにデータが自動的に渡されないことです。コンポーネントには独自のスコープがあるためです。そのため、子コンポーネントにデータを渡すには、props プロパティを使用して少し手間をかける必要があります:
1 | |
また、v-for をオブジェクトに使用する場合、反復値はオブジェクトの値であり、キーではありません。これはネイティブ js とは異なります。ネイティブ js の for in ループで値を出力するには、手動で obj['i'] を反復処理する必要があり、キーを出力するには2番目のパラメータ (value, key) を記述する必要があります:
1 | |
注意:ネイティブ js では、Symbol.iterator を手動で実装しない限り、for of ループを使用できませんが、Vue では可能です(ただし効果は for in と完全に同じです)。
リストレンダリングには「就地复用原则」(その場での再利用原則)という小さな tips があります。これはどういう意味でしょうか? 前述の tololist の例で説明すると、各要素に一意の key 値を指定しない場合(以下のように):
1 | |
この場合、×ボタンをクリックして現在の li を削除するたびに、Vue はその場で現在の要素を再利用し、データを正しい位置に直接移動します。remove でDOM要素を削除せず、reflow を回避します。以下は、×ボタンをクリックして要素を削除するときのページの render 状況を chrome の devtool で表示したものです:

reflow の部分は最下部のわずかな部分だけであることがわかります。
一方、key を追加すると:
1 | |
「×」をクリックした後のブラウザの render の状況を見てみましょう:

ここで疑問に思うかもしれません。なぜこの部分で Vue が提供する (value, key) の key を使わず、自分で value 上に value.key を実装する必要があるのでしょうか?:
1 | |
答えは、Vue が提供する key は一見 key のように見えますが、実際には現在のデータとは無関係なものです。そのため、li を削除する際に、key は単に再計算されるだけで、削除された要素や残された要素に伴って移動したり削除されたりしません。上記の書き方を使用すると、効果は最初の key なしの場合と同じで、依然として就地再利用戦略が使われ、変更されるのはデータだけで、dom 構造は変わりません。したがって、自分で key を実装する必要があります。おおよそ以下のようになります:
1 | |
イベントハンドラ
イベントハンドラはチェーンできますが、一部の要素はそもそもサポートしていないため、バインドしても意味がありません。例えば div に keyup イベントをバインドする場合:
1 | |
そのため、一般的には div でイベントをバブリング処理し、input に alt+ctrl イベントをバインドします:
1 | |
ここで私が懸念しているのは、input が @keyup.space のリスナーを使用している場合、中国語入力法ではスペースは通常単語を選択するために使われるため、実際に出力されるのはまだ選択されていないピンイン文字なのか、それともスペースを押した後の候補リストの最初の単語なのかということです(これは Vue の問題ではないかもしれませんが、ここで提起しておきます)。
答えは、ほとんどの場合、スペースを押した後の最初の単語が出力されます。ただし、一文が長い場合、2回スペースを押して文を出力する必要がある場合、最初に押した時には何も出力されず、空になり、2回目にスペースを押した時に初めてすべての語彙が出力されます。これはテストの極端なケースを想定しているため、基本的にはスペースを押した後の最初の単語が出力され、空白やピンイン文字ではないと考えてよいでしょう。私は搜狗 mac 入力法の単行候補モードを使用しており、上記の todolist でテストできます(省略)。
注: 公式ドキュメントで v-model について説明する際に IME について触れていますが、これがまさにこの問題です。入力法を使用している際に v-model も即座に反応させたい場合は、input イベントをバインドすることができます。
フォームコントロール
v-model は一般的に input に使用され、テンプレート内の input の value 値は無視されます。v-model は js 内の初期値のみを認識し、それにバインドします。そのため、v-model を記述し、さらに value 属性も記述した場合、後者は dom 構造には現れますが、js がその値を取得する際には無視され、v-model がバインドした値が取得されます:
1 | |
1 | |
両者の違いは、jQueryにおける.dataと.attrの違いに似ています------インラインで記述されたものはattr('data','xxx')の値であり、DOM構造を確認してもxxxの値が表示されますが、jsで実際に取得される値はjsでバインドされた.data('yyy')の値です------もちろん、attrを使用してDOM構造を読み取る場合を除きます。(注:インスタンス化後にattrの値を変更すると、jsで取得されるのはattrの値になります。ここでの初期値の無視は、あくまで初期値のみを無視するものです。例を挙げると、初期化後のv-modelがvalueにバインドされた後、手動でDOM構造のvalue値を変更すると、v-modelがその要素のvalue値を取得する際には、変更後のattr属性値が使用され、data上の値ではありません)。
もし要件が特殊で、v-modelを使用せずにinputの値を取得してリアルタイムで更新したくない場合、またはinputの値を処理してから更新したい場合、$refを試してみてください(e.target.valueも使用可能です):
テンプレート:
1 | |
ロジック:
1 | |
公式ドキュメントにも記載されているように、v-modelは双方向データバインディングを実現するためのシンタックスシュガーに過ぎません:
1 | |
ただし、inputイベントを監視すると、入力法を使用している際にスペースを押して単語を選択する前にも入力イベントがトリガーされるため、この要件がない場合は、素直にv-modelを使用するのが良いでしょう。
複数の要素に同じ値をバインドして出力する必要がある場合、一般的な要件は一連のcheckboxです。この場合、配列を使用する必要があります:
1 | |
1 | |
(私が発見した限りでは)このように簡潔な配列の使用方法は、複数のcheckboxタイプのinputに対してのみ有効です------つまり、複数の要素が同じv-modelにバインドされていても、これらの要素の状態は同期されず、対応するvalueが配列に格納されます。もちろん、methodsを使用して任意の入力タイプの要素で同様の効果を実現できると強く主張する場合は------それについては触れません。
単一のcheckboxの場合、v-modelはvalue値にバインドされ、trueまたはfalseになります。:true-valueと:false-valueを使用して、選択時の値と非選択時の値をカスタマイズできます。
一方、複数のラジオボタンradioの場合、v-modelはnameと同様の役割を果たします------つまりグループ化に使用されるため、radioタイプのinputでv-modelを使用する場合、name属性を記述する必要はありません。
selectタイプの場合、各optionにvalue属性が指定されていない場合、バインドされるのはoption内の値です。指定されている場合はvalue属性の値になります。selectタイプの複数選択ボックスでv-modelにバインドするdataは配列型でなければなりません。そうでない場合、警告が表示されます(エラーにはなりませんが、Vueが自動的に変換し、正常に動作します):
1 | |
注意:上記のすべてのタイプでv-modelとvalueをバインドする際に、value属性が動的(:value="xxx")にdata上の他の属性(xxx)にバインドされている場合、v-modelに対応する属性と:valueに対応する属性は同一(厳密に等しい)です。
コンポーネント
まず区別すべきは、DOMテンプレートと文字列テンプレートの違いです。
HTMLテンプレートとは、通常のhtml要素を指し、これらの要素はVueインスタンスのelオプションによってバインドされます:
文字列テンプレートの部分:
1 | |
文字列テンプレートとは:
js内でtemplateによって登録されるテンプレート、例えば:
1 | |
または:
1 | |
-
<script type="text/x-tempalge"></script>によって登録されるテンプレート(Handlebarと同じ) -
.vueコンポーネント内の<template>タグの内容。
Vueはブラウザが解析を完了した後にDOMテンプレートの解析を開始するため、DOMテンプレートは特定の子要素を必要とするタグではコンポーネントを使用できません。例えば、selectタグの子要素はoptionでなければならず、カスタムタグcom-optionは認識されません。そのため、is="component-name"属性を追加して、そのタグが使用するテンプレート名を指定します。
リテラル構文 VS 動的構文
ここで注意すべき問題は、ネイティブのjsではオブジェクトのプロパティに数字を使用できますが、それは文字列として扱われます(ES5のみ、ES6ではオブジェクトのプロパティは任意の値にできますが、これ以降の説明と矛盾しません)。しかし、Vueでは、dataプロパティに数字を属性として使用できません。リテラル構文の場合、数字を渡すと先にtoString処理されますが、動的構文の場合は直接数字として処理され、dataにバインドされたプロパティ値は検索されません:
子コンポーネントテンプレート:
1 | |
子コンポーネントロジック:
1 | |
親コンポーネント-リテラル構文:
1 | |
親コンポーネント-動的構文:
1 | |
上記の両方の構文における親コンポーネントのロジックは同じ:
1 | |
結果として、親コンポーネントのリテラル構文を使用した場合、buttonをクリックすると子コンポーネントに1が渡され、ドキュメントにもある通り、文字列の1であるため、alertで表示されるのはstringです。一方、親コンポーネントの動的構文でv-bindを使用して親コンポーネントのdataの1プロパティをバインドしても、子コンポーネントは1プロパティに対応する属性-動的構文の値を受け取らず、依然として1という値であるため、buttonをクリックしたときにalertで表示されるのはnumberです。
結論:dataオブジェクトの属性として数字を使用しない方が良いです。
注意:子コンポーネントに渡される属性が配列またはオブジェクトの場合、子コンポーネントでこの属性値を変更すると、親コンポーネントに反映されます------これは通常すべきではありません。なぜなら、よく言われるように:props down, events up(例は省略)、ベストプラクティスは親コンポーネントから渡される参照型のディープコピーを使用することです------ただし、子コンポーネントが親コンポーネントの状態に影響を与える必要がある場合は、幸運を祈ります。
events upの際、子コンポーネントが$emitするときにイベント名以外のパラメータを渡すと、これらのパラメータは親コンポーネントのイベントリスナー関数に渡されます:
子コンポーネントテンプレート:
1 | |
子コンポーネントロジック:
1 | |
親テンプレート:
1 | |
親ロジック:
1 | |
非同期更新キュー
Vueがあれば、もはやjQueryは必要ありません。フレームワークの最大の利点は、同じ操作に対して繰り返しコードを書く必要がなくなることです。双方向データバインディングは多くのDOM操作問題を解決してくれますが、場合によってはjQueryがより優れていることもあります。例えばDOMを操作する際、jQueryはコールバック関数として関数を受け取り、アニメーションの実行完了時にトリガーされます。一方、私たちが使用する双方向データバインディングでは、データを設定した後、DOMがいつ更新されたかをどうやって知るのでしょうか?答えはjQueryと同じで、非同期更新キューです。
1 | |
この書き方は個人的にはお勧めしません。なぜなら、最良のロジックはインスタンスのプロパティ/メソッド内に記述すべきで、インスタンスの外側に書くべきではないと考えるからです。幸いVueはこれを実現する方法を提供してくれています:
1 | |
アニメーション
アニメーションについては特に言うことはありませんが、JavaScriptフック関数の中の2つのフックについて説明が必要です。それはenterCanceledとleaveCancelledです。enterCancelledはv-ifとv-showの両方で使用され、どちらでもトリガーされる可能性があります。トリガーのタイミングは、enterイベントが発生した後、アニメーションがまだ完了していない過程で、他のアニメーションを実行する必要があるときです。一方、leaveCancelledはv-showでのみ使用され、v-ifで使用した場合は無効で、決してトリガーされません。そのトリガーのタイミングは、離脱アニメーション(つまりxxx-leave-activeアニメーション)が再生中でまだ完了していないときに、他のアニメーションを実行したときです。
テストコード:
1 | |
ロジック:
1 | |
スタイル:
1 | |
フックのパラメータは、enterとleaveがel要素自体(ネイティブのElement型要素)とdoneコールバック関数であるのに対し、他のフックのパラメータはすべてel要素自体です。
要素のトランジションでは、各要素にkeyを追加するのが最善です。なぜなら、前述の就地再利用戦略により、切り替え時にデータを直接置き換えてしまい、アニメーション効果が得られない可能性があるからです。
公式ドキュメントで全く説明されていない点の1つは、Vue transitionのアニメーションクラス名cssの書き方には順序制限があることです。v-enterとv-leaveはv-enter-activeとv-leave-activeの後ろに書かなければならず、そうでないと無効になります。例えば、ボタンをクリックしたときのフェードイン/フェードアウト効果を作りたい場合、ボタンをクリックすると左から右へボタンがフェードインし、同時に現在クリックしたボタンが左から右へフェードアウトするようにしたいとします:
ロジック:
1 | |
構造:
1 | |
もしあなたのスタイルがこうなっている場合:
1 | |
アニメーション効果が期待通りでないことに気付くでしょう:

しかし、enterをenter-activeの後ろに移動すると:
1 | |
完璧に動作します:

これら4つのcssクラス名の可能な順序を比較した後、v-enterをv-enter-activeの後ろに置くだけで効果が得られることがわかりました。他のクラス名は任意です。
transitionタグ内にはアニメーションが必要な要素だけを配置でき、他の要素を入れることはできません。もし上記の例の構造がこのようになっている場合:
1 | |
アニメーション効果は全く得られません。また、通常のcssクラス名でいくつかのcssプロパティを使用して要素のスタイルを規定し、v-enterのようなアニメーションクラス名で同じプロパティを使用した場合も効果がありません。ドキュメントは「それらの優先度は通常のクラス名よりも高い」と述べていますが、実際にはそうではありません(私の理解が間違っているのでしょうか?ご指摘をお待ちしています)。前の例で、スタイルの中でbuttonの通常のcssプロパティを設定すると:
1 | |
結果:

見ての通り、opacity だけがアニメーションし、transform はアニメーションしません!(皆さんも試してみてください。animate.css を使用する際に、Animate.css と同じプロパティを事前に要素に設定した場合、アニメーション効果がまだあるかどうか、ぜひ issue を提出してください。)
transition-group は transition と少し異なります。見た目上、transition 自体は単なるラッパーコンテナで、ページ構成には関与しませんが、transition-group は Vue によってタグ(デフォルトでは span タグ)に置き換えられます。また、置き換えるタグ名をカスタマイズすることも可能です。
render 関数
render 関数を使用すると、テンプレートを書く代わりになります。その引数 createElement は一般的に h と記述されます。コンポーネントやタグ内の各種バインディングや属性などは、createElement 内で対応する JavaScript の書き方を見つけることができます。もし見つからない場合は、ネイティブの書き方を使用できます。例えば、.stop や .prevent などは、直接 event.stopPropagation() や event.preventDefault() を使用すればよいです。
その他
ミックスイン(mixin)とは、通常のコンポーネント作成プロセス(つまりコンポーネントのライフサイクル内)で、追加の機能を修正または追加することを指します。
一部の プラグイン は上記の mixin に基づいて書かれています。それ以外のプラグインとしては、Vue.prototype にメソッドを追加したり、config を通じてグローバルなメソッドやプロパティを追加するものがあります。
ルーティング は、component の is 属性を使用して簡単に実装できます。また、render 関数を直接書いて、異なるパスに応じて異なるテンプレートをレンダリングすることも可能です。もちろん、より複雑な場合はサードパーティライブラリが必要になります。
状態管理 については、サンプルを見ると、各状態変更のプロセスを記録するために wrap を追加する必要があるようです。公式が推奨するベストプラクティスは、直接インスタンスプロパティに代入できる場合でも、関数を通じて状態を変更することです。これにより、状態の追跡が可能になります。
ユニットテスト は、通常のユニットテストであり、特に説明する必要はありません。
サーバーサイドレンダリング(Server Side Render)
考え方を確認すると、基本的に非常にシンプルです。まず、app.js で Vue インスタンスを exports し、次に新しいページテンプレートファイル index.html(Vue.js をインポートし、Vue インスタンスをマウントするメソッド $mount を含む。公式サイトでは app.js もインポートしていますが、必要かどうかは私が検証してから説明します)を作成します。このテンプレートにはインスタンスのマウントポイント(id 属性を持つ空でない要素)が含まれています。その後、サーバー側の server.js でこれらをすべて require し、vue-server-renderer を使用して、app.js から exports された Vue インスタンスをレンダリングし、クライアントに返す際にマウントポイント(id 属性を持つ空でない要素、app.js 内のテンプレートが既に存在するため)を置き換えます。
サーバーサイドレンダリングの結果、上記のマウントポイント(id 属性を持つ空でない要素)に server-rendered="true" 属性が追加されます(ページソースを右クリックして確認すると存在することがわかります。これは js で動的に追加されたものではないことを示しています)。
サーバーサイドもストリーミングレンダリングをサポートしています。まず、htmlをマウントポイント(id属性を持つ空でない要素、例えば<div id="app"></div>)で分割点としてsplitし、aとbの2つの部分に分けます。先ほど使用したvue-server-rendererはapp.jsをrenderToStringしていましたが、ストリーミングレンダリングをサポートするためには、renderToStreamという別のメソッドを使用する必要があります。その後、dataイベントを監視し、htmlのa部分の後ろに追加します。endイベントの後、htmlのb部分を連結し、最終的にres.sendで送信します。
後記
帝都所在、cn.vuejs.orgをping:

自社のFQ VPSをping:

digを実行:

cloudflareのサービスを使用していることが確認できます。国際的なウェブサイトは難しいですね、はは。
人生の重要な選択に直面したとき、最善の方法を誰かが教えてくれて、貴重な時間を無駄にせずに済めばと、私はよく願っています。だからこそ、自分の経験を踏まえて頻繁にブログを書き、広大なインターネットのこの小さな片隅に、私にとって一度きりの人生経験を記録し、助けを求める人々の力になれればと思っています。