vue组件渲染过程

206 阅读2分钟

vue组件渲染过程:

  1. template模板渲染成render函数
  2. data响应式,触发getter、setter数据监听
  3. 执行render函数生成vnode,调用patch(elem, vnode)挂载dom节点

vue组件修改data更新过程:

  1. 修改data,触发setter
  2. 重新执行render函数生成newvnode
  3. 调用patch(oldvnode, newvnode),比较新旧虚拟dom生成新dom

那么模板是如何渲染成render函数的呢?

初始化npm环境

npm init -y

添加vue-template-compiler

vue-template-compiler是编译vue模板的包,传入模板返回AST抽象语法树和render

npm install vue-template-compiler --dev-save

index.js

const compiler = require("vue-template-compiler");

const str = '<template><div class="container">name: {{name}}</div></template>';

const compile = compiler.compile(str);
console.log(compile)

打印compile

{
  ast: {
    type: 1,
    tag: 'template',
    attrsList: [],
    attrsMap: {},
    rawAttrsMap: {},
    parent: undefined,
    children: [ [Object] ],
    plain: true,
    slotScope: undefined,
    static: false,
    staticRoot: false
  },
  render: `with(this){return [_c('div',{staticClass:"container"},[_v("name: "+_s(name))])]}`,
  staticRenderFns: [],
  errors: [
    'Cannot use <template> as component root element because it may contain multiple nodes.'
  ],
  tips: []
}

主要看render属性

with(this){return [_c('div',{staticClass:"container"},[_v("name: "+_s(name))])]}

先了解下with语法:

一个可以按序检索的对象列表,通过它可以进行变量名的解析。with语句用于临时拓展作用域链,

eg:

const obj = {
  a: 1,
  b: 2
}
with(obj) {
  console.log(a);
  console.log(b);
}

with(this){return [_c('div',{staticClass:"container"},[_v("name: "+_s(name))])]}中的this指代的当前组件的vue实例,因此template模板里面的变量是经过with的变量名解析,可以直接使用,如vue的data设置了name,name挂载在vue实例里面,在template就可以直接使用name,而不是this.name


说完with, 那render函数里面的_c 、_s又是什么呢

我们在vue源码vue/blob/dev/src/core/instance/render-helpers/index.js里面可以找到对应的函数

export function installRenderHelpers (target: any) {
  target._o = markOnce
  target._n = toNumber
  target._s = toString
  target._l = renderList
  target._t = renderSlot
  target._q = looseEqual
  target._i = looseIndexOf
  target._m = renderStatic
  target._f = resolveFilter
  target._k = checkKeyCodes
  target._b = bindObjectProps
  target._v = createTextVNode
  target._e = createEmptyVNode
  target._u = resolveScopedSlots
  target._g = bindObjectListeners
  target._d = bindDynamicKeys
  target._p = prependModifier
}

_c可以在vue/blob/dev/src/core/instance/render.js里面找到

// 创建虚拟节点vnode
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)  

_c入参类似虚拟dom的js表达

像其他的插值、表达式、属性和动态属性、v-if、v-for等都可以通过compiler转化看看生成的render函数是什么样子的