vue源码学习9: 虚拟Dom实现原理

523 阅读2分钟

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中使用到createElementcreateTextElement的作用,就是生成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
    }
}

以上内容仅学习使用,