Vue3设计思路 -- 实现简单的createApp工厂函数,带你全面理解View的绘制流程

47 阅读4分钟

mount期间都做了什么

mount()我们都知道是挂载,到底是怎么挂载的呢?实际的操作是什么呢?其实做的事情有很多。

  • 根据用户传入的选择器,就以这个选择器选中的元素作为宿主元素

  • 拿到宿主元素之后 拿到这个元素的 innerHTML 作为template,通过编译的方式得到渲染函数 – 根据渲染函数可以真正的得到DOM节点(再根据用户传入的状态数据最终生成)

  • 最终将render函数生成的DOM节点追加到宿主元素身上

编译过程的理解

浏览器是不认识vue风格的一些语法的,像{{}}@clickv-if等等这些特殊的指令的,所以在mount方法执行到最终挂载在宿主元素上之前需要先进行一次编译,把这些指令处理成浏览器认识的语法。

真实的这一块是极其复杂的,代码量极其的大,这里我处理的地方只是最最简单的处理{{}}插值表达式,同时阉割掉了动态的DOM,只生成一个写死的h3标签,我们只是要理解设计思路就可以了,我对自己的要求暂时也没有那么高,到这一步就可以了。

通过编译完成之后会生成一个render函数。

render函数的理解

render(渲染),着是个函数,我们只需要理解成执行了一个函数之后,会返回一个具有真是DOM的对象。

在vue的组件中是也可以自己写render方法,也可以不写,所以根据这个细节我们就知道要判断一下用户传入的配置对象中是否含有rende方法,如果没有就使用自己的render方法

composition API 与 option API的兼容

composition API就是在代码块处写setup函数。option API就是vue2的代码风格,通过写各种的配置项。

这里就用到了ES6的proxy代理api

this.proxy = new Proxy(this, {

get(target, key) {

if (key in target.setupCtx) {

return target.setupCtx[key]

} else {

return target.dataCtx[key]

}

},

set(target, key, value) {

if (key in target.setupCtx) {

target.setupCtx[key] = value

} else {

target.dataCtx[key] = value

}

}

})

上面就是代理的部分,当我们访问this.proxy的某个值的时候,会走入get方法,在get方法中就可以做优先级判断等等操作,如果使用了setupAPI就从setup函数中取值,否则就从传统的optionAPI中的data中取值。设置值也是一样的理解

proxyObject.defineProperty的api风格非常像,但是性能更高,同时也可以做更多的事情,具体红宝书上有详细的描述功能

代码部分

代码不长,但是很值得多看几遍,我也是自己看了一些源码,再跟着老师一起写下来的。收获很大。

function createApp(config) {

let app = {}

app.mount = function (id) {

// 宿主元素

this.rootDOM = document.getElementById(id)

if (!config.render) {

/*

判断传入的配置项是是否有render方法 如果没有就自己通过编译生成render方法

*/

config.render = this.compile(this.rootDOM.innerHTML)

// 将宿主元素的 innerHTML 部分进行编译,主要就是为了处理 像 {{}} @ v-if 等等插值表达式,指定进行编译成浏览器认识的代码

}

if (config.setup) {

this.setupCtx = config.setup()

} else {

this.dataCtx = config.data()

}

/*

封装一个 proxy 对象代理, 相比于Object.defineProperty 性能更高,且可以代理代理至别的对象,有他可以很好的兼容vue2和vue3的api

*/

this.proxy = new Proxy(this, {

get(target, key) {

if (key in target.setupCtx) {

return target.setupCtx[key]

} else {

return target.dataCtx[key]

}

},

set(target, key, value) {

if (key in target.setupCtx) {

target.setupCtx[key] = value

} else {

target.dataCtx[key] = value

}

}

})

/*

这里把这个代理传进入 将来访问代理的某个值 就会进入代理的 get方法

get方法就会去判断是否又 setup api 如果有就用 setup api 没有就用 option api

*/

const el = config.render.call(this.proxy)

this.rootDOM.innerHTML = ''

this.rootDOM.appendChild(el)

return this

}

app.compile = function (template) {

/*

这个是个阉割版本的 生成写死的h3标签了

尤大大完整版这里是会根据 template 去解析有什么dom,把DOM中的 {{title}} 替换成用户传入对象的数据的那个title

目前只是为了理解mount的过程 所以就写一个阉割版本,但是这样写下来,已经能够很好的理解和明白 mount 函数的作用和意义了

*/

return function () {

const h3 = document.createElement('h3')

h3.textContent = this.title

React

  • 介绍一下react

  • React单项数据流

  • react生命周期函数和react组件的生命周期

  • react和Vue的原理,区别,亮点,作用

  • reactJs的组件交流

  • 有了解过react的虚拟DOM吗,虚拟DOM是怎么对比的呢

  • 项目里用到了react,为什么要选择react,react有哪些好处

  • 怎么获取真正的dom

  • 选择react的原因

  • react的生命周期函数

  • setState之后的流程

  • react高阶组件知道吗?

  • React的jsx,函数式编程

  • react的组件是通过什么去判断是否刷新的

  • 如何配置React-Router

  • 路由的动态加载模块

  • Redux中间件是什么东西,接受几个参数

  • redux请求中间件如何处理并发

开源分享:docs.qq.com/doc/DSmRnRG…