初試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>