vue源码学习(8):codegen如何将ast语法树转换成render字符串?
在vue源码学习(8)一文中,详细说明了ast语法树如何转变成render字符串。
今天的内容就是如何利用render字符串生成vnode。
模板引擎的
在Vue中的模板引擎,是用new Function + with来实现。
当我们把字符串传递给new Function()
之后会得到下面这个函数。
function anonymous() {
_c('div', {id:"app",a:"111",style:{"color":" red","background":" green"}}),_v("hello"+_s(arr)+"word")
}
此时,能看到_s(arr)
来源于vm,但是new Function是获取不到vm的。所以这里就需要用到with了。
于是就做了下面的处理:
// 把code变成`with(this){ return ${code} }`
let render = new Function(code)
// 替换成下面的写法
let render = new Function(`with(this){ return ${code} }`)
通过上面这个下发,就可以这样使用:
render.call(vm)
小提示💡:new Function可以看做一个沙箱,创建和全局作用域平行的作用域
with的用法
with 语句的原本用意是为逐级的对象访问提供命名空间式的速写方式.
举一个例子:
// 假设有一个obj对象,要修改obj中的每一项
var obj = {
a: 1,
b: 2,
c: 3
};
// 方式1:逐一修改obj,使用3次obj
obj.a = 2;
obj.b = 2;
obj.c = 2;
// 方式2: 只用一次obj
with (obj) {
a = 3;
b = 3;
c = 3;
}
mountComponent
mountComponent顾名思义,就是生成组件。
实现代码如下:
export function mountComponent(vm, el) {
console.log(vm, el)
// 数据变化后,会再次调用更新函数
let updateComponent = () => {
// 1. 通过render生成虚拟dom
vm._update(vm._render()) // 后续更新可以调动updateComponent方法
// 2. 虚拟Dom生成真实Dom
}
updateComponent()
}
- updateComponent:当数据发生变化后,更新函数
- vm._update:在Vue的原型上面挂载_update方法,来更新
- vm._render:在Vue的原型上面挂载_c、_v、_s、_render四个方法
export function renderMixin(Vue) {
Vue.prototype._c = function (tag, data, ...children) {
console.log(tag, data, children)
// 产生虚拟节点
return createElement(this, ...arguments)
}
Vue.prototype._v = function (text) {
console.log(text)
// 产生虚拟节点
return createTextElement(this, text)
}
Vue.prototype._s = function (val) {
console.log(val)
// 产生虚拟节点
return JSON.stringify(val)
}
Vue.prototype._render = function () {
console.log('render')
const vm = this
let render = vm.$options.render
let vnode = render.call(vm)
return vnode;
}
}
VDom
vm._render中使用到createElement
和createTextElement
的作用,就是生成VDom(虚拟Dom)。
实现代码如下:
export function createElement(vm, tag, data = {}, ...children) {
console.log('abc', vm, tag, data, children)
return vnode(vm, tag, data, data.key, children, undefined)
}
export function createTextElement(vm, text) {
console.log('def', vm, text)
return vnode(vm, undefined, undefined, undefined, undefined, text)
}
function vnode(vm, tag, data, key, children, text) {
return {
vm,
tag,
data,
key,
children,
text
}
}
以上内容仅学习使用,