Maison  >  Article  >  interface Web  >  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

亚连
亚连original
2018-05-28 16:49:371682parcourir

Cet article vous fournit une analyse détaillée du code source de la vue de contrôle des données de vue et des points clés annotés. Les amis intéressés peuvent s'y référer pour référence.

Analysez comment vue implémente les modifications de données et met à jour les vues.

Préface

J'ai analysé le code source de vue il y a trois mois Comment y parvenir données réactives ? Le nom de l'article est Responsive Data in Vue Source Code. Enfin, il est analysé que la méthode update() de Watcher sera appelée après les modifications des données. Continuons donc à voir ce que fait update() après trois mois. . (J'ai réalisé un projet utilisant React-Native au cours des trois derniers mois, mais je n'ai pas l'intention de le résumer car cela semble trop simple.

La méthode narrative de cet article est de suivre la logique de). en regardant le code source. , la version de vue consultée est 2.5.2. J'ai forgé un code source pour enregistrer les commentaires

Effacer le. direction de l'enquête pour atteindre l'objectif, parlons d'abord du comportement cible : quelle méthode est exécutée pour mettre à jour la vue après les changements de données. Ensuite, préparez-vous à commencer avec cette direction comme objectif et commencez à chercher des réponses depuis l'entrée de la vue. code source.
Partez de la conclusion précédente

Revoyons d'abord les conclusions précédentes :


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

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

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

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


mountComponent

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

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


Il s'avère que la méthode $mount() appelle mountComponent(), (ou dans Spécifier le champ el lors de la construction de vue appellera également automatiquement la méthode $mount() Parce que les objets de rendu de web et weex (quoi). est-ce que weex ? je l'ai déjà présenté dans d'autres articles) sont différents, donc différents fichiers doivent être introduits lors de la publication, différentes distes ne peuvent pas être envoyées (ce problème est laissé à l'étude de l'ensemble du processus de vue plus tard

Ce qui suit est la méthode mountComponent :

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

Ce code ne fait en réalité que 3 choses :

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
}
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 est-ce, updateComponent est une fonction, noop est vide, null est vide, true signifie RenderWatcher. 🎜> J'ai vu isRenderWatcher dans Watcher :

Oui Oui, je viens de copier une copie pour l'utiliser pour juger quelque chose lorsque l'observateur corrige pour la première fois fois (d'après les commentaires, je ne sais toujours pas ce que ça fait).

Ensuite, il n'y a qu'un seul problème qui n'a pas été résolu, c'est ce qu'est updateComponent.

if (isRenderWatcher) {
  vm._watcher = this
 }
Si la fonction est passée dans le 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 les getters de toutes les données de la vue. doit être appelé dans ce updateComponent, afin que les dépendances puissent être établies dans l'observateur afin que la vue puisse répondre aux modifications de données

Ensuite, accédez à. vm._update() et vm._render(). Trouvé la méthode ._render() dans src/core/instance/render.js

Cette méthode fait :
updateComponent = () => {
  vm._update(vm._render(), hydrating)
 }

Générer un VNode basé sur la méthode de rendu de la machine virtuelle actuelle (render La méthode peut être compilée en fonction du modèle ou du fichier vue, on en déduit donc qu'écrire directement la méthode de rendu est le plus efficace)

S'il y a un problème avec la méthode de rendu, appelez d'abord la méthode renderError, et si elle échoue, lisez it Le dernier vnode peut être nul.

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
 }
}

S'il existe un nœud parent, placez-le dans son propre attribut .parent.

  • Enfin, retournez VNode

  • Le noyau est donc cette phrase :

  • The render(), vm._renderProxy, vm. Je ne sais même pas ce qu'est $createElement.
  • Premier coup d'œil à vm._renderProxy : il est défini lors de initMixin(). Ensuite, nous pensons 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 dossier vdom. c'est une méthode qui renvoie un VNode.

le rendu est un peu compliqué. Pouvons-nous l'étudier à l'avenir ? En bref, nous devons analyser le modèle ou voir un seul fichier et monter la cible dans la fonction de rendu.

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

vnode = render.call(vm._renderProxy, vm.$createElement)
Regardons vm._update()

La partie qui nous intéresse est en fait la partie __patch(), __patch() le fait. Pour les opérations dom, il est jugé dans _update() s'il s'agit du premier appel. Si c'est le cas, créez un nouveau dom. Sinon, transmettez l'ancien et le nouveau nœuds pour comparaison, puis exploitez

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 finalement de le transformer en dist ?
Analyse de __patch__ et de VNode

Ce qui précède est ce que j'ai compilé pour tout le monde. J'espère que cela sera utile à tout le monde à l'avenir.

Articles connexes :

Méthode de soumission de formulaire Ajax inter-domaines (même nom de domaine de base)

Implémentation d'images ajax basées sur Firefox Upload

Ajax chargeant la méthode de mise en œuvre de l'effet de couche contextuelle de page externe

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!

Déclaration:
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