文中的reactive和effectWatch出自上一篇文章juejin.cn/post/704773…
入口
import App from './core/init.js';
import { createApp } from './core/createApp.js';
// 根据vue的初始化顺序实现createApp和mount函数
createApp(App).mount(document.querySelector('#app'));
App
import { reactive } from './reactive/index.js';
import { h } from './parse/vNode.js';
export default {
// template --> render 此过程省略,直接模拟一个h函数
render(context) {
return h('div',
{
class: "demo"
},
`${context.state.count}`
// [
// h('p', null, '哈哈哈'),
// h('p', null, '嘿嘿嘿'),
// ]
);
},
setup() {
// 响应式数据
const state = reactive({
count: 1
});
window.state = state;
return {
state
}
}
}
createApp
import { effectWatch } from './reactive/index.js';
import { mountElement } from './renderer/index.js';
export function createApp(rootComponent) {
// rootComponent App对象,有render和setup函数
return {
// 根节点容器,比如默认vue项目的index.html中的id='app'的div
mount(rootContainer) {
// 获取setup返回的对象
const context = rootComponent.setup();
// 做依赖收集
effectWatch(() => {
// 清空页面
rootContainer.innerHTML = ``;
const subTree = rootComponent.render(context);
// 将vNode转为真实dom
mountElement(subTree, rootContainer);
})
}
}
}
创建虚拟DOM
// 创建虚拟dom
export function h(tag, props, children) {
return {
tag,
props,
children
}
}
render
export function mountElement(vNode, container) {
const { tag, props, children } = vNode;
// tag
const el = document.createElement(tag);
// props
if (props) {
Object.entries(props).forEach(([key, value]) => {
el.setAttribute(key, value);
});
}
// children <Array|String>
if (typeof children === 'string') {
const node = document.createTextNode(children);
el.append(node);
}
else if (Array.isArray(children)) {
// 递归渲染
children.forEach(vNode => {
// 切记传入el作为下一次的渲染容器
mountElement(vNode, el);
});
}
// 插入
container.append(el);
}