ホームページ  >  記事  >  ウェブフロントエンド  >  vueデータコントロールビューのソースコードの解析

vueデータコントロールビューのソースコードの解析

不言
不言オリジナル
2018-06-29 17:42:291301ブラウズ

この記事では主に Vue データ コントロール ビューのソース コードの分析を紹介します。これには特定の参考値があります。必要な友達はそれを参照できます。

Vue がデータの変更とビューを更新する方法の分析。 。

はじめに

3 か月前、私はレスポンシブ データを実現する方法を分析するために vue ソース コードを読みました。最後に、データが変更された後、 Watcher の update() メソッドが呼び出されます。それでは、3 か月後に update() が何を行うかを見てみましょう (私は過去 3 か月で React-native を使用したプロジェクトを実行しましたが、それを要約するつもりはありません。簡単すぎるようです)

この記事のストーリー この方法は、ソース コードを確認するロジックに従うことです。私がチェックした vue のバージョンは、コメントを記録するためにソース コードのコピーをフォークしました。

目的

調査の方向性を明確にすることによってのみ、目標を達成することができます。まず、ターゲットの動作について説明します。データが変更された後にビューを更新するためにどのメソッドが実行されるかについて説明します。次に、この方向に向けて開始する準備をし、調査を開始します。 vue のソースコードの入り口から答えを求めます

前の結論から始めましょう

まずは復習しましょう 前の結論を見てみましょう:

vue が構築されると、Observer オブジェクトがデータ上に作成されます (ゲッターとセッターはインターセプトされます。ゲッターは依存関係の収集をトリガーし、セッターは通知をトリガーします。ウォッチを登録するときにウォッチ オブジェクトが 1 回呼び出され、ゲッターがトリガーされます。監視オブジェクトの依存関係を現在の Watcher の deps に収集します。dep の setter がトリガーされると、現在の Watcher に通知され、Watcher の update() メソッドが呼び出されます。

次に、レンダリング関連の Watcher を登録することから始めます。 .

src/core/instance/lifecycle.jsでファイルを見つけました。

new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */)

mountComponent

レンダリング関連のWatcherはmountComponent()内にあり、このメソッドで呼び出されるので、このメソッドがどこにあるかを検索しましょうWeb を例にすると、 src/platforms/web/runtime/index.js と src/platforms/weex/runtime/index.js という 2 つの場所しかありません。

Vue.prototype.$mount = function (
 el?: string | Element,
 hydrating?: boolean
): Component {
 el = el && inBrowser ? query(el) : undefined
 return mountComponent(this, el, hydrating)
}

$mount であることがわかります。 () メソッドは mountComponent() を呼び出します (または、vue 構築中に el フィールドを指定すると、$mount() メソッドも自動的に呼び出されます)。これは、Web と weex (weex とは何ですか? 以前に他の記事で紹介しました) レンダリング オブジェクトが異なるためです。したがって、パブリッシュ時に異なるファイルが導入され、最終的には異なる dist としてパブリッシュされる必要があります (この問題は後で vue のプロセス全体を検討することになります

以下は mountComponent メソッドです:

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
}

このコードは実際には 3 つのことだけを実行します:

beforeMountフックを呼び出す
  • Watcherを構築する
  • マウントされたフックを呼び出す
  • (笑) ということで、実際に中心となるのは今すぐWatcherを作成することです。
Watcherのパラメータを見てください: vmはこれ、updateComponent は関数、noop は空、null は空、true は RenderWatcher を意味します。

Watcher で isRenderWatcher を確認しました:

if (isRenderWatcher) {
  vm._watcher = this
 }

はい、コピーしただけです。1 つは、Watcher が初めてパッチを適用するときに何かを判断するために使用されます (からコメント、それが何のためのものなのかはまだわかりません)

次に、未解決の問題が 1 つだけあります。それは、UpdateComponent

が Watcher のコンストラクターの 2 番目のパラメーターとして関数を渡すことです。賢明な方なら、この updateComponent でビュー内のすべてのデータを呼び出す必要があると推測できるはずです。これにより、ビューがデータの変更に応答できるようになります。
updateComponent = () => {
  vm._update(vm._render(), hydrating)
 }

次に、vm._update() と vm._render() に移動します。 src/core/instance/render.js でそれらを見つけます。

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 を生成します。現在の VM の render メソッドに基づいて (render メソッドはテンプレートまたは vue ファイルからコンパイルされる可能性があるため、render メソッドを直接記述するのが最も効率的であると推測されます)

render メソッドを呼び出し、最初に renderError メソッドを呼び出し、失敗した場合は最後の vnode または null を読み取ります。
  • 親ノードがある場合は、それを独自の .parent 属性に入れます。
  • 最後に VNode に戻ります
  • それで、核心はこの文です:
  • vnode = render.call(vm._renderProxy, vm.$createElement)
  • render()、vm._renderProxy、vm.$createElement が何なのかわかりません。

    まず vm._renderProxy を見てみましょう: それは initMixin() ですこれが設定され、運用環境では vm が返され、開発環境ではプロキシが返されます。その後、それがデバッグ可能な vm (つまり vm) であると考えられます。vm のコードの詳細は後で説明します。 .$createElement は vdom フォルダーにあります。以下は VNode を返すメソッドです。
render は少し複雑です。簡単に言うと、テンプレートまたは vue の単一ファイルとマウントを解析することです。ターゲットをレンダー関数に追加します

簡単な要約: vm._render() の戻り値は、現在の vm のレンダー関数に従って VNode になります

気になる部分を見てみましょう。 about は実際には __patch() の部分であり、 _update( ) で dom に対して操作を実行し、それが最初の呼び出しであるかどうかを判断します。そうでない場合は、古いノードと新しいノードを渡します。比較してから操作してください。

結論

Vue のビューのレンダリングは特別な種類の Watcher であり、関数の実行プロセスは render 関数を呼び出します (テンプレートには監視されたデータが含まれています)。そのため、テンプレート内の観察されたデータが変更されると、Watcher の update() メソッドがトリガーされてビューが再レンダリングされます。 dist
__patch__ と VNode 分析の作成プロセスとは何ですか

以上がこの記事の全内容です。その他の関連コンテンツについては、PHP 中国語 Web サイトをご覧ください。 !
関連する推奨事項:

クロスドメインルーティングの競合を解決するための vue のアイデアについて

ルーティング パラメーターを動的に設定するための Vue の概要について

vue の仮想 dom パッチのソース コード分析について


以上がvueデータコントロールビューのソースコードの解析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。