vue组件渲染过程:
- template模板渲染成render函数
- data响应式,触发getter、setter数据监听
- 执行render函数生成vnode,调用patch(elem, vnode)挂载dom节点
vue组件修改data更新过程:
- 修改data,触发setter
- 重新执行render函数生成newvnode
- 调用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函数是什么样子的