1. 基本的な使い方公式サイトの場合: <div id='アプリ'> <入力タイプ="テキスト" v-model="入力値" v-focus> </div> <スクリプト> Vue.directive('focus', { // 要素を初めてバインドするときにbind()を呼び出す { コンソールログ('バインド') }, // バインドされた要素が DOM に挿入されると... 挿入: 関数 (el) { console.log('挿入されました') el.フォーカス() }, // コンポーネント VNode が更新されたら update() を呼び出す { コンソールログ('更新') }, // 命令が配置されているコンポーネントのすべての VNode とその子 VNode が更新された後に、componentUpdated() を呼び出します { console.log('コンポーネントが更新されました') }, // 一度だけ呼び出される unbind() は、命令が要素からアンバインドされたときに呼び出されます { console.log('アンバインド') } }) 新しいVue({ データ: { 入力値: '' } }).$mount('#app') </スクリプト> 2. 指示の動作原理2.1. 初期化グローバル API を初期化するときに、platforms/web の下で createPatchFunction を呼び出して、VNode を実際の DOM に変換するパッチ メソッドを生成します。初期化の重要なステップは、DOM ノードに対応するフック メソッドを定義することです。DOM の作成 (create)、アクティブ化 (avtivate)、更新 (update)、削除 (remove)、および破棄 (destroy) 中に、対応するフック メソッドがそれぞれポーリングされ、呼び出されます。これらのフックの一部は、命令宣言サイクルへの入り口です。 // src/core/vdom/patch.js const フック = ['create'、'activate'、'update'、'remove'、'destroy'] エクスポート関数createPatchFunction(バックエンド){ i,jとします 定数 cbs = {} const { モジュール、 nodeOps } = バックエンド (i = 0; i < hooks.length; ++i) の場合 { cbs[フック[i]] = [] // モジュールは、class、style、domListener、domProps、attrs、directive、ref、transition などの Vue のモジュールに対応します。 (j = 0; j < モジュールの長さ; ++j) { if (isDef(モジュール[j][フック[i]])) { // 最後にフックを{hookEvent: [cb1, cb2 ...], ...}形式に変換します。cbs[hooks[i]].push(modules[j][hooks[i]]) } } } // .... 関数 patch (oldVnode, vnode, hydrating, removeOnly) を返す { // ... } } 2.2 テンプレートのコンパイルテンプレートのコンパイルは、命令パラメータを解析することです。具体的に分解された ASTElement は次のとおりです。 { タグ: '入力', 親: ASTElement、 ディレクティブ: [ { arg: null、// パラメータ終了: 56、// 命令の終了文字位置 isDynamicArg: false、// 動的パラメータ、v-xxx[dynamicParams]='xxx' フォーム呼び出し修飾子: undefined、// 命令修飾子名: "model"、 rawName: "v-model", // 命令名開始: 36, // 命令開始文字位置値: "inputValue" // テンプレート }, { 引数: null、 終了: 67, isDynamicArg: false、 修飾子: 未定義、 名前: "フォーカス"、 生の名前: "v-focus", 開始: 57, 価値: "" } ]、 // ... } 2.3. レンダリング方法の生成Vue では、DOM を操作するために命令を使用することを推奨しています。カスタム命令は DOM または属性を変更する可能性があるため、テンプレート解析に対する命令の影響を避けてください。レンダリング メソッドを生成するときに最初に処理するのは、v-model などの命令です。これは基本的に構文糖です。レンダリング関数をスプライシングすると、値属性と入力イベントが要素に追加されます (入力を例にとると、これもユーザーがカスタマイズできます)。 (これ){ _c('div', { を返す 属性: { 「id」: 「アプリ」 } }, [_c('入力', { ディレクティブ: [{ 名前: "モデル", 生の名前: "v-model", 値: (入力値) 式: "inputValue" }, { 名前: "フォーカス"、 生の名前: "v-focus" }], 属性: { "タイプ": "テキスト" }, domProps: { "value": (inputValue) // v-model命令を処理するときに追加される属性}, の上: { "input": function($event) { // v-model ディレクティブを処理するときにカスタムイベントが追加されます if ($event.target.composing) 戻る; 入力値 = $event.target.value } } })]) } 2.4. VNodeを生成するvue の命令の設計は、DOM の操作を容易にすることを目的としています。VNode を生成するとき、命令は追加の処理を実行しません。 2.5. 実際のDOMを生成するvue の初期化プロセスでは、次の 2 つの点を覚えておく必要があります。
パッチ処理中、実際の DOM を生成するために createElm が呼び出されるたびに、現在の VNode にデータ属性があるかどうかを検出します。データ属性がある場合は、invokeCreateHooks が呼び出され、最初に作成されたフック関数が実行されます。コア コードは次のとおりです。 // src/core/vdom/patch.js 関数createElm( vノード、 挿入されたVnodeQueue、 親エルム、 refElm、 ネストされた、 所有者配列、 索引 ){ // ... // createComponent には戻り値があり、これはコンポーネントを作成するメソッドです。戻り値がない場合は、次のメソッドに進みます if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) { 戻る } 定数データ = vnode.data // .... if (isDef(データ)) { // 実際のノードが作成された後、命令を含むノード属性を更新します // 命令は最初に bind メソッドを呼び出し、次に命令の以降のフック メソッドを初期化します。invokeCreateHooks(vnode, insertedVnodeQueue) } // 下から上へ、insert(parentElm, vnode.elm, refElm) // ... } 上記は、ディレクティブフックメソッドの最初のエントリです。directive.js // src/core/vdom/modules/directives.js // デフォルトでは、スローされるすべてのメソッドは updateDirectives メソッドです export default { 作成: updateDirectives、 更新: updateDirectives、 破棄: 関数 unbindDirectives (vnode: VNodeWithData) { // 破棄されると、vnode === emptyNode 更新ディレクティブ(vnode、空ノード) } } 関数 updateDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) { if (oldVnode.data.directives || vnode.data.directives) { _update(古いVnode、vnode) } } 関数_update(oldVnode, vnode) { const isCreate = oldVnode === 空のノード const isDestroy = vnode === 空のノード 定数 oldDirs = normalizeDirectives(oldVnode.data.directives、oldVnode.context) 定数 newDirs = normalizeDirectives(vnode.data.directives, vnode.context) // 挿入後のコールバック const dirsWithInsert = [ // 更新が完了した後のコールバック const dirsWithPostpatch = [] キー、oldDir、dir を入力します。 for (newDirsのキー) { oldDir = oldDirs[キー] dir = newDirs[キー] // 新しい要素の命令。挿入されたフックメソッドを 1 回実行します。if (!oldDir) { // 新しいディレクティブ、バインド callHook(dir, 'bind', vnode, oldVnode) dir.def が挿入されている場合 dirsWithInsert.push(dir) } } それ以外 { // 既存のディレクティブを更新 // 要素がすでに存在する場合、componentUpdatedフックメソッド dir.oldValue = oldDir.value が1回実行されます。 dir.oldArg = 古いDir.arg callHook(dir, 'update', vnode, oldVnode) dir.def がコンポーネント更新された場合 dirsWithPostpatch.push(dir) } } } (dirsWithInsert.length)の場合{ // 実際のDOMがページに挿入され、このコールバックメソッドが呼び出されます const callInsert = () => { (i = 0 とします; i < dirsWithInsert.length; i++) { callHook(dirsWithInsert[i], '挿入済み', vnode, oldVnode) } } // VNode マージ挿入フック if (isCreated) { mergeVNodeHook(vnode、'挿入'、callInsert) } それ以外 { 挿入()を呼び出す } } (dirsWithPostpatch.length)の場合{ mergeVNodeHook(vnode, 'postpatch', () => { (i = 0 とします; i < dirsWithPostpatch.length; i++) { callHook(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode) } }) } 作成する場合 for (キー in oldDirs) { if (!newDirs[キー]) { // 存在しないので、バインドを解除します callHook(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy) } } } } 初めて作成する場合の実行プロセスは次のとおりです。 1.oldVnode === emptyNode、isCreate は true であり、現在の要素内のすべてのバインド フック メソッドが呼び出されます。 2. 命令に挿入フックがあるかどうかを確認します。ある場合は、挿入フックを VNode.data.hooks プロパティにマージします。 3. DOM のマウントが完了すると、VNode.data.hooks に挿入フックがある場合は、マウントされたすべてのノードに対してinvokeInsertHook が実行されます。それが呼び出され、命令にバインドされた挿入されたメソッドがトリガーされます。 通常、最初の作成には bind メソッドと insert メソッドのみが使用され、update と componentUpdated は bind と insert に対応します。コンポーネントの依存関係ステータスが変化すると、VNode diff アルゴリズムを使用してノードにパッチが適用されます。呼び出しプロセスは次のとおりです。 1. 応答データが変更された場合は、dep.notify を呼び出してデータの更新を通知します。 2. patchVNode を呼び出して、新しい VNode と古い VNode の差分更新を実行し、現在の VNode 属性 (updateDirectives メソッドに入る命令を含む) を完全に更新します。 3. 命令に更新フック メソッドがある場合は、更新フック メソッドを呼び出し、componentUpdated コールバックを初期化し、postpatch フックを VNode.data.hooks にマウントします。 4. 現在のノードとその子ノードが更新されると、ポストパッチフックがトリガーされます。つまり、命令のcomponentUpdatedメソッドです。 コアコードは次のとおりです。 // src/core/vdom/patch.js 関数 patchVnode ( 古いVノード、 vノード、 挿入されたVnodeQueue、 所有者配列、 索引、 削除のみ ){ // ... 定数 oldCh = oldVnode.children 定数ch = vnode.children // ノード属性を完全に更新します if (isDef(data) && isPatchable(vnode)) { (i = 0; i < cbs.update.length; ++i) の場合、cbs.update[i](oldVnode, vnode) if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode) } // ... if (isDef(データ)) { // postpatchフックを呼び出す if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode) } } unbind メソッドは、ノードが破棄されるときにinvokeDestroyHook を呼び出しますが、ここでは詳細には説明しません。 3. 注意事項カスタム命令を使用する場合、v-model は通常のテンプレート データ バインディングとはいくつかの点で異なります。たとえば、渡すパラメーター (v-xxx='param') は参照型ですが、データが変更されたときに命令の bind または inserted をトリガーすることはできません。これは、命令の宣言サイクルで、bind と inserted は初期化時に 1 回だけ呼び出され、その後は update と componentUpdated のみが使用されるためです。 ディレクティブの宣言ライフサイクルの実行順序は、bind -> inserted -> update -> componentUpdated です。ディレクティブが子コンポーネントのコンテンツに依存する必要がある場合は、対応するビジネス ロジックを componentUpdated に記述することをお勧めします。 Vue では、フック メソッド、イベント コールバックなど、多くのメソッドがループで呼び出されます。通常、呼び出しは try catch で囲まれます。これは、処理メソッドがエラーを報告してプログラム全体がクラッシュするのを防ぐためです。これは、開発プロセスのリファレンスとして使用できます。 IV. 要約Vue のソースコード全体を見始めたときは、詳細や方法の多くがよくわかりませんでした。特定の機能ごとの実装を整理することで、徐々に Vue の全体像が見えてくると同時に、開発や使用における落とし穴を回避することができました。 上記は、Vue ディレクティブの実装原理を分析する詳細な内容です。Vue ディレクティブの原理の詳細については、123WORDPRESS.COM の他の関連記事に注目してください。 以下もご興味があるかもしれません:
|
<<: Puppeteer を使用して Linux (CentOS) で Web ページのスクリーンショット機能を実装する
作業の過程で、暗黙的な変換が発生するケースが数多くあります。暗黙的な変換は、クエリの速度低下を引き起...
目次1. MySQLをダウンロードする1.1 ダウンロード1.2 インストール1. MySQLをダウ...
どの要素でもスクロールできるようにしながら、スクロールバーを非表示にするにはどうすればよいでしょうか...
質問: コンピュータを再起動した後、docker の mysql コンテナを再起動できません。原因が...
1. イベントバブリング: JavaScript イベント伝播のプロセスでは、要素でイベントがトリガ...
この記事では、CSS ワープ シャドウの実装コードを紹介し、皆さんと共有します。詳細は以下の通りです...
ローカルでコンテナを作成した後、このコンテナに基づいてローカル イメージを作成し、このイメージを D...
目次1. ポーテナーの紹介2. Portainer アーキテクチャの概要3. Portainerのイ...
CSS 3.0 とビデオを組み合わせて実現したクリエイティブなオープニングをご紹介します。効果は次の...
1. はじめに要件は、特定の時間範囲内で、1 時間ごとのデータと前の 1 時間ごとのデータの差と比率...
1.Tomcatの最適化構成(1)Tomcatのcatalina.batを変更するJavaをサーバー...
この記事の例では、要素UIテーブルにドロップダウンフィルタリングを実装するための具体的なコードを参考...
目次導入説明書実際の経験長所と短所総括する導入mysqlpump は mysqldump の派生です...
システムド: CentOS 7のサービスsystemctlスクリプトは、/usr/lib/syste...
目次序文Ajax シリアルおよびパラレルAjaxの同時リクエスト制御のための2つのソリューションPr...