Maison > interface Web > js tutoriel > Analyse du code source de la vue de contrôle des données vue

Analyse du code source de la vue de contrôle des données vue

不言
Libérer: 2018-06-29 17:42:29
original
1359 Les gens l'ont consulté

Cet article présente principalement l'analyse du code source de la vue de contrôle des données de Vue. Il a une certaine valeur de référence. Maintenant, je le partage avec tout le monde. Les amis dans le besoin peuvent s'y référer

Analyse de la façon dont Vue est implémentée. vue des modifications et des mises à jour des données.

Préface

Il y a trois mois, j'ai lu le code source de vue pour analyser comment obtenir des données réactives. Le nom de l'article est le code source de vue pour réactif. data , et enfin analysé que la méthode update() de Watcher sera appelée après les modifications de données. Continuons donc à voir ce que fait update() après trois mois (au cours des trois derniers mois, j'ai utilisé React-Native pour construire. un projet, mais je n'ai pas l'intention de le faire. Résumé, car cela semble trop simple).

La méthode narrative de cet article est de suivre la logique de regarder le code source de la version I. vérifié est 2.5.2. J'ai forgé une copie du code source utilisé pour enregistrer les commentaires

Objectif

Effacer la direction de l'enquête pour atteindre le. Objectif.Parlons d'abord du comportement cible : quelle méthode est exécutée pour mettre à jour la vue après la modification des données. Ensuite, préparez-vous à commencer à chercher des réponses dans cette direction et commencez à partir de l'entrée du code source de la vue.

Démarrez. de la conclusion précédente

Revoyons d'abord la conclusion précédente :

Lorsque Vue est construite, un objet Observer sera créé sur les données (et quelques autres champs). sont interceptés. Le getter déclenche la collection de dépendances et le setter déclenche la notification.

Un autre L'objet est un Watcher Lors de l'enregistrement d'une montre, l'objet watch sera appelé une fois, ce qui déclenche le getter de la montre. et collecte les dépendances dans les dépôts du Watcher actuel Lorsqu'un setter du dep est déclenché, le Watcher actuel sera averti d'appeler la mise à jour de la méthode Watcher ().

Ensuite. nous commençons par enregistrer le Watcher lié au rendu.

Le fichier se trouve dans src/core/instance/lifecycle.js.

new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */)
Copier après la connexion

mountComponent

Le Watcher lié au rendu est appelé dans la méthode mountComponent(), cherchons donc où cette méthode est appelée. Il n'y a que 2 endroits, respectivement src/platforms/web/runtime/index.js et src/platforms. /weex/runtime/index.js, en prenant le web comme exemple :

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

Il s'avère que c'est la méthode $mount() que MountComponent() est appelée, (ou en spécifiant le champ el pendant vue la construction appellera automatiquement la méthode $mount()), car les objets rendus par web et weex (qu'est-ce que weex ? Je l'ai déjà présenté dans d'autres articles) sont différents, donc lors de la publication, différents fichiers doivent être introduits et finalement différents dists ne peut pas être publié (ce problème est laissé à l'étude de l'ensemble du processus de vue plus tard).

Ce qui suit est la méthode 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
}
Copier après la connexion

Ce code ne fait en fait que 3 choses :

  • Appelez le hook beforeMount

  • Create Watcher

  • Appelez le hook monté

(hahaha) Ensuite, le noyau est en fait d'établir le Watcher.

Regardez les paramètres du Watcher : vm c'est ça, updateComponent est une fonction, noop est vide, null est vide, et vrai signifie que c'est RenderWatcher

J'ai regardé isRenderWatcher dans Watcher :

if (isRenderWatcher) {
  vm._watcher = this
 }
Copier après la connexion

Oui, je viens de faire une copie à utiliser pour le premier patch dans watcher Pour juger quelque chose ( d'après les commentaires, je ne sais toujours pas à quoi ça sert).

Le seul problème qui reste non résolu est ce qu'est updateComponent.

updateComponent

passe la fonction comme deuxième paramètre du constructeur de Watcher, alors cette fonction devient le getter de watcher. Si vous êtes intelligent, vous auriez dû deviner que dans ce updateComponent, ce n'est qu'en appelant les getters de toutes les données de la vue que vous pouvez le faire. vous établissez des dépendances dans l'observateur afin que la vue puisse répondre aux modifications de données.

updateComponent = () => {
  vm._update(vm._render(), hydrating)
 }
Copier après la connexion

Ensuite, accédez à vm._update() et vm._render().

J'ai trouvé le ._render () dans 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
 }
}
Copier après la connexion

Cette méthode fait :

  • Selon Utiliser la méthode de rendu de la machine virtuelle actuelle pour générer un VNode. (La méthode de rendu peut être compilée sur la base du modèle ou du fichier vue, on en déduit donc que l'écriture directe de la méthode de rendu est la plus efficace)

  • S'il y a un problème. avec la méthode render, puis appelez d'abord la méthode renderError, et si cela ne fonctionne pas, lisez le dernier vnode ou null.

  • S'il y a un nœud parent, mettez-le dans son propre attribut .parent.

  • Renvoie enfin VNode

Le noyau est donc cette phrase :

vnode = render.call(vm._renderProxy, vm.$createElement)
Copier après la connexion

Le rendu (), vm._renderProxy, vm.$ Je ne sais même pas ce qu'est createElement

Regardons d'abord vm._renderProxy : il est défini lors de initMixin(). renvoie le proxy dans l'environnement de développement, nous pensons donc que c'est une vm qui peut être déboguée (c'est vm), nous verrons les détails plus tard

Le code de vm.$createElement est dans le vdom. Après l'avoir regardé, c'est une méthode qui renvoie un VNode.

le rendu est un peu compliqué, mais il ne peut pas être étudié plus tard. En bref, le fichier unique du modèle ou de la vue et la cible de montage sont analysés dans le rendu. function.

Petit résumé : La valeur de retour de vm._render() est VNode, selon la fonction de rendu de la vm actuelle

Ensuite, regardons vm._update()

Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
 const vm: Component = this
 if (vm._isMounted) {
  callHook(vm, 'beforeUpdate')
 }
 // 记录update之前的状态
 const prevEl = vm.$el
 const prevVnode = vm._vnode
 const prevActiveInstance = activeInstance
 activeInstance = vm
 vm._vnode = vnode
 // Vue.prototype.__patch__ is injected in entry points
 // based on the rendering backend used.
 if (!prevVnode) { // 初次加载, 只有_update方法更新vm._vnode, 初始化是null
  // initial render
  vm.$el = vm.__patch__( // patch创建新dom
  vm.$el, vnode, hydrating, false /* removeOnly */,
  vm.$options._parentElm,
  vm.$options._refElm
  )
  // no need for the ref nodes after initial patch
  // this prevents keeping a detached DOM tree in memory (#5851)
  vm.$options._parentElm = vm.$options._refElm = null
 } else {
  // updates
  vm.$el = vm.__patch__(prevVnode, vnode) // patch更新dom
 }
 activeInstance = prevActiveInstance
 // update __vue__ reference
 if (prevEl) {
  prevEl.__vue__ = null
 }
 if (vm.$el) {
  vm.$el.__vue__ = vm
 }
 // if parent is an HOC, update its $el as well
 if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
  vm.$parent.$el = vm.$el
 }
 // updated hook is called by the scheduler to ensure that children are
 // updated in a parent's updated hook.
 }
Copier après la connexion

La partie qui nous intéresse est en fait la partie __patch(). __patch() effectue l'opération sur le dom, et elle est jugée dans _update(). Si c'est le cas, créez-en un nouveau ? dom. Sinon, transmettez l'ancien et le nouveau nœuds pour comparaison, puis opérez

Conclusion

.

Le rendu de vue de Vue est un type spécial de Watcher. Le contenu de la montre est une fonction. La fonction de rendu est appelée pendant le processus d'exécution de la fonction. Le rendu est compilé à partir du modèle ou du dom d'el (le modèle en contient). objets observés). Par conséquent, les modifications dans les données observées dans le modèle déclenchent la méthode update() de Watcher pour restituer la vue

Legacy
.

Où la fonction de rendu est-elle compilée ?
Quel est le processus d'introduction des différentes plates-formes lorsque le code source de vue est publié et enfin de sa saisie dans dist ?
Analyse de __patch__ et de VNode

Ce qui précède est l'intégralité du contenu de cet article. J'espère qu'il vous sera utile. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois !

Recommandations associées :

À propos des idées de Vue pour résoudre les conflits de routage entre domaines

À propos de la définition dynamique des paramètres de routage par Vue Introduction

À propos de l'analyse du code source du patch Vue Virtual Dom

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal