初试Vue3-手动实现render挂载
只实现了最简单的render新增节点挂载到指定dom上,数据响应式没实现,一点一点的研究吧
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script>
// const {createApp} = Vue;
const createAppAPI = (render) => {
return function createApp(rootComponent) {
// 返回真正的应用程序实例,执行mount方法
const app = {
// 将传入的容器选择器 把vnode转变成dom 并挂载到容器
mount(rootContainer) {
// rootContainer 就是#app
// rootComponent 是传进来的 {data(){return{}},render(){tag:"h2",children:this.foo}}
// v3里面虚拟dom的变化
const vnode = {
tag: rootComponent
}
// 这一步非常重要哦,执行渲染
render(vnode, rootContainer)
}
}
return app
}
}
// 2.实现renderer工厂函数
const createRenderer = options => {
const patch = (n1, n2, container) => {
/**
* n1 旧的虚拟节点
* n2 新的虚拟节点 根组件的配置在这里
* container 容器挂载
* */
const rootComponent = n2.tag; //为啥是n2.tag 可以往上找,createAppAPI里面定义的,当然也可以不叫tag
// 数据的上下文,要指向data的执行结果
const ctx = {...rootComponent.data()
}
/*
*ctx 的值是{foot:'hellov3'}
**/
// 执行render获取vnode
const vnode = rootComponent.render.call(ctx);
/**
执行实例里面render方法,拿到data里面的数据
**/
// 转换vnode dom 拿到要挂载的节点dom
const parent = options.querySelector(container);
// render里要添加的tagname,创建新节点
const child = options.createElement(vnode.tag);
//这里只判断了最简单的情况,其他类型还得再去源码中好好看
if (typeof vnode.children === 'string') {
child.textContent = vnode.children;
// textContent 文本内容
}
// 最后一步将虚拟dom转完的真实dom挂载到指定元素上
options.insert(child, parent)
}
const render = (vnode, container) => {
// 把虚拟dom变成真实dom并添加到container里
// 判断container上有没有虚拟dom
patch(container._vode || null, vnode, container);
container._vode = vnode;
}
// 该对象就是renderer
return {
render,
createApp: createAppAPI(render)
}
}
// 1.createApp runtimedom->index.js createApp ensureRender createRenderer
const Vue = {
createApp(options) {
// 执行的实际是renderer.createApp()
return renderer.createApp(options)
}
}
const renderer = createRenderer({
querySelector(sel) {
return document.querySelector(sel)
},
createElement(tag) {
return document.createElement(tag)
},
insert(child, parent) {
parent.appendChild(child)
}
})
const {
createApp
} = Vue;
createApp({
data() {
return {
foo: "hello,vue3!"
}
},
render() {
return {
tag: "h2",
children: this.foo
}
}
}).mount('#app');
</script>
</body>
</html>