VueJS 的编译阶段到挂载节点
栏目: JavaScript · 发布时间: 6年前
内容简介:为了实现响应式模式,Vue用render函数来生成vnode,并使用diff算法对比新旧vnode,最后更新到真实DOM上。由于是在编译阶段而不是在监听阶段,所以vnode没有对比的对象,直接通过vnode生成真实DOM。Vnode是Vdom上的一个节点,是对真实DOM的抽象,在Vue中,我们可以通过对比新旧Vnode和Vdom来得到需要更新真实DOM的操作,并通过Vue框架来执行这些操作。于是我们可以把更多的精力投放到业务逻辑上。
为了实现响应式模式,Vue用render函数来生成vnode,并使用diff算法对比新旧vnode,最后更新到真实DOM上。
由于是在编译阶段而不是在监听阶段,所以vnode没有对比的对象,直接通过vnode生成真实DOM。
Vnode是Vdom上的一个节点,是对真实DOM的抽象,在Vue中,我们可以通过对比新旧Vnode和Vdom来得到需要更新真实DOM的操作,并通过Vue框架来执行这些操作。于是我们可以把更多的精力投放到业务逻辑上。
编译阶段
该阶段会解析template,把template转化为render函数会经过三个过程:
- parse,将 template 模板中进行字符串解析,得到指令、class、style等数据,形成 AST
- optimize,这个阶段用于优化 patch阶段 ,标记节点的 static 属性是否是静态的
- generate,将 AST 转化成 render funtion 字符串,最终得到 render 的字符串以及 staticRenderFns 字符串
如果使用vue-cli工具的话,借助webpack可以在打包过程中把template转化为render函数和staticRenderFns函数
render 的字符串与render 函数的关系
render函数内部包含render字符串:
function render(vm) { with(vm) { eval(render_string) } } 复制代码
挂载节点
Vue实例化的最后一步就是挂载节点。该阶段会分为两步:
- 通过render函数获得vnode
- 通过传入vnode给patch函数生成真实DOM并挂载到页面上
render函数被执行时机
那么render函数在什么时候会被再次执行呢?
在解释VueJS 响应式原理的时候有提到过,Render-Watcher实例的getter就是执行render函数的:
updateComponent = () => { vm._update(vm._render(), hydrating) } new Watcher(vm, updateComponent, noop, { before () { if (vm._isMounted) { callHook(vm, 'beforeUpdate') } } }, true /* isRenderWatcher */) 复制代码
所以,render函数会被执行的时机有:
- Vue初始化的时候,会执行一次
- 当template(模板)中需要观察的数据对象更新值的时候,也会触发render函数(render-watcher)执行
render函数的关键是 _createElement
,负责返回VNode,它会根据标签名是否存在已注册的组件中,返回普通VNode或是组件VNode:
export function _createElement ( context: Component, tag?: string | Class<Component> | Function | Object, data?: VNodeData, children?: any, normalizationType?: number ): VNode | Array<VNode> { // ....... let vnode, ns if (typeof tag === 'string') { let Ctor ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag) if (config.isReservedTag(tag)) { // platform built-in elements vnode = new VNode( config.parsePlatformTagName(tag), data, children, undefined, undefined, context ) } else if (isDef(Ctor = resolveAsset(context.$options, 'components', tag))) { // component vnode = createComponent(Ctor, data, context, children, tag) } else { // unknown or unlisted namespaced elements // check at runtime because it may get assigned a namespace when its // parent normalizes children vnode = new VNode( tag, data, children, undefined, undefined, context ) } } else { // direct component options / constructor vnode = createComponent(tag, data, context, children) } if (Array.isArray(vnode)) { return vnode } else if (isDef(vnode)) { if (isDef(ns)) applyNS(vnode, ns) if (isDef(data)) registerDeepBindings(data) return vnode } else { return createEmptyVNode() } } 复制代码
patch函数执行时机
和render函数的一样,因为patch函数就在 vm._update(vm._render(), hydrating)
中的_update里。
- 在Vue初始化的时候,会生成真实DOM并挂载到document上
- 当template(模板)中需要观察的数据对象更新值的时候,会对比新旧vnode,并返回新vnode对应的真实DOM
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) { const vm: Component = this 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) { // 初始化渲染 vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */) } else { // 更新渲染 vm.$el = vm.__patch__(prevVnode, vnode) } 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. } 复制代码
更新的patch函数的核心是diff算法,类似git的diff指令,大致逻辑如下:
通过对比新旧vnode,找到更新真实DOM需要的所有操作,比如新增、删除、替换节点的操作。然后通过Vue框架来执行这些更新DOM的操作,最后返回更新的DOM。
参考
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Kubernetes集群跨节点挂载CephFS
- Kubernetes集群跨节点挂载CephFS
- 文件系统挂载
- vue 源码学习 - 实例挂载
- linux 磁盘管理与文件挂载
- [Recovery]自动挂载system分区
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。