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函数会经过三个过程:

  1. parse,将 template 模板中进行字符串解析,得到指令、class、style等数据,形成 AST
  2. optimize,这个阶段用于优化 patch阶段 ,标记节点的 static 属性是否是静态的
  3. 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实例化的最后一步就是挂载节点。该阶段会分为两步:

  1. 通过render函数获得vnode
  2. 通过传入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函数会被执行的时机有:

  1. Vue初始化的时候,会执行一次
  2. 当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里。

  1. 在Vue初始化的时候,会生成真实DOM并挂载到document上
  2. 当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。

参考

template 模板是怎样通过 Compile 编译的 Vue.js 技术揭秘


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Mastering Regular Expressions, Second Edition

Mastering Regular Expressions, Second Edition

Jeffrey E F Friedl / O'Reilly Media / 2002-07-15 / USD 39.95

Regular expressions are an extremely powerful tool for manipulating text and data. They have spread like wildfire in recent years, now offered as standard features in Perl, Java, VB.NET and C# (and an......一起来看看 《Mastering Regular Expressions, Second Edition》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

URL 编码/解码
URL 编码/解码

URL 编码/解码