ホームページ > 記事 > ウェブフロントエンド > Vue データ コントロール ビューのソース コード分析
この記事では、Vue データ コントロール ビューのソース コードと注釈付きの重要なポイントの詳細な分析を提供します。興味のある方は参照してください。
vue がデータの変更を実装し、ビューを更新する方法を分析する
前書き
この記事は、レスポンシブ データを実現する方法を分析するために、vue のソース コードを読みました。データ変更後に Watcher の update() メソッドが呼び出されることが分析されたため、3 か月後に引き続き update() が何を行うかを見てみましょう (私は過去 3 年間、react-native を使用してプロジェクトを構築しました。 (単純すぎるので)
この記事の説明方法は、ソースコードを参照することです。私が確認した vue のバージョンは 2.5.2 です。コメントを記録するためにソース コードのコピーをフォークしました
目的
調査の方向性を明確にすることによってのみ、目標を達成することができます。つまり、その後ビューを更新するためにどのようなメソッドが実行されるかについて説明します。データが変更されたら、この方向で vue のソース コードの入り口から答えを探し始める準備をします。
前の結論から始めます
ファイルは src/core/instance/lifecycle.js にあります。
new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */)mountComponent
レンダリング関連の Watcher は、mountComponent() メソッドで呼び出されます。なので、このメソッドが呼び出される場所は 2 つだけです。Web を例にすると、src/platforms/web/runtime/index.js と src/platforms/weex/runtime/index.js です。 web と weex のレンダリング オブジェクト (何weex は以前に他の記事で紹介しました) とは異なるため、パブリッシュ時に別のファイルを導入する必要があり、最終的なパブリッシュは異なりません (この問題は後で vue のプロセス全体を検討することにします)。 以下は mountComponent メソッドです:
Vue.prototype.$mount = function ( el?: string | Element, hydrating?: boolean ): Component { el = el && inBrowser ? query(el) : undefined return mountComponent(this, el, hydrating) }このコードは実際には 3 つのことだけを行います: beforeMount フックを呼び出す Watcher を作成する マウントされたフックを呼び出す
export function mountComponent ( vm: Component, el: ?Element, hydrating?: boolean ): Component { vm.$el = el // 放一份el到自己的属性里 if (!vm.$options.render) { // render应该经过处理了, 因为我们经常都是用template或者vue文件 // 判断是否存在render函数, 如果没有就把render函数写成空VNode来避免红错, 并报出黄错 vm.$options.render = createEmptyVNode if (process.env.NODE_ENV !== 'production') { /* istanbul ignore if */ if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') || vm.$options.el || el) { warn( 'You are using the runtime-only build of Vue where the template ' + 'compiler is not available. Either pre-compile the templates into ' + 'render functions, or use the compiler-included build.', vm ) } else { warn( 'Failed to mount component: template or render function not defined.', vm ) } } } callHook(vm, 'beforeMount') let updateComponent /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { // 不看这里的代码了, 直接看else里的, 行为是一样的 updateComponent = () => { const name = vm._name const id = vm._uid const startTag = `vue-perf-start:${id}` const endTag = `vue-perf-end:${id}` mark(startTag) const vnode = vm._render() mark(endTag) measure(`vue ${name} render`, startTag, endTag) mark(startTag) vm._update(vnode, hydrating) mark(endTag) measure(`vue ${name} patch`, startTag, endTag) } } else { updateComponent = () => { vm._update(vm._render(), hydrating) } } // we set this to vm._watcher inside the watcher's constructor // since the watcher's initial patch may call $forceUpdate (e.g. inside child // component's mounted hook), which relies on vm._watcher being already defined // 注册一个Watcher new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */) hydrating = false // manually mounted instance, call mounted on self // mounted is called for render-created child components in its inserted hook if (vm.$vnode == null) { vm._isMounted = true callHook(vm, 'mounted') } return vm }
updateComponent
Watcher のコンストラクターの 2 番目のパラメーターに関数が渡され、この関数が Watcher のゲッターになることは、賢明な方ならおわかりでしょう。ビュー内のすべてのデータのゲッターは、この updateComponent で呼び出す必要があります。これにより、ウォッチャーで依存関係を確立し、ビューがデータの変更に応答できるようになります。
if (isRenderWatcher) { vm._watcher = this }
その後、 vm._update()と vm._render(). src/core/instance/render.js で ._render() メソッドを見つけました。
updateComponent = () => { vm._update(vm._render(), hydrating) }このメソッドは次のことを行います: render メソッドに基づいて VNode を生成します現在の VM の (レンダリング メソッドはテンプレートまたは vue ファイルからコンパイルされる可能性があるため、レンダリング メソッドを直接記述するのが最も効率的であると推測されます) レンダリング メソッドに問題がある場合は、それを呼び出します。最初の renderError メソッドで、それでも失敗する場合は、最後の vnode または null を読み取り、親ノードがある場合は、それを独自の .parent 属性に入れます したがって、核心は次の文です。
Vue.prototype._render = function (): VNode { const vm: Component = this const { render, _parentVnode } = vm.$options // todo: render和_parentVnode的由来 // reset _rendered flag on slots for duplicate slot check if (process.env.NODE_ENV !== 'production') { for (const key in vm.$slots) { // $flow-disable-line vm.$slots[key]._rendered = false } } if (_parentVnode) { vm.$scopedSlots = _parentVnode.data.scopedSlots || emptyObject } // set parent vnode. this allows render functions to have access // to the data on the placeholder node. vm.$vnode = _parentVnode // render self let vnode try { vnode = render.call(vm._renderProxy, vm.$createElement) } catch (e) { // catch其实不需要看了, 都是做异常处理, _vnode是在vm._update的时候保存的, 也就是上次的状态或是null(init的时候给的) handleError(e, vm, `render`) // return error render result, // or previous vnode to prevent render error causing blank component /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { if (vm.$options.renderError) { try { vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e) } catch (e) { handleError(e, vm, `renderError`) vnode = vm._vnode } } else { vnode = vm._vnode } } else { vnode = vm._vnode } } // return empty vnode in case the render function errored out if (!(vnode instanceof VNode)) { if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) { warn( 'Multiple root nodes returned from render function. Render function ' + 'should return a single root node.', vm ) } vnode = createEmptyVNode() } // set parent vnode.parent = _parentVnode return vnode } }
vnode = render.call(vm._renderProxy, vm.$createElement)気になる部分実際には __patch() の部分です。 __patch() は dom に対して操作を実行し、それが _update() 内にあるかどうかを判断します。そうである場合は、新しい dom を作成します。そうでない場合は、古い dom と新しい dom を渡します。ノードを比較してから操作します。
結論
Vue のビューのレンダリングは特別な種類の Watcher であり、関数を実行するプロセスは render 関数を呼び出します (テンプレートには監視されたデータが含まれています)。そのため、テンプレート内の観測データが変更されると、Watcher の update() メソッドがトリガーされて、ビューが再レンダリングされます。
レガシー
レンダリング関数はどこでコンパイルされていますか
vue ソース コードがリリースされると、さまざまなプラットフォームが導入して最終的に印刷しました dist
__patch__ の作成プロセスと VNode
の分析について 以上、皆さんの参考になれば幸いです。
関連記事:
Ajaxクロスドメイン(基本ドメイン名は同じ)フォーム送信方法
以上がVue データ コントロール ビューのソース コード分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。