Maison > Article > interface Web > Analyse du code source de la vue de contrôle des données Vue
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
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.
Ensuite, nous commençons ici par enregistrer le Watcher lié au rendu.
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
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
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.
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 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!