模拟
2024年03月15日
一、实现
1.1 renderer.js
const mountComponent = (vnode, container, anchor) => {
const componentOptions = vnode.type;
const {
data,
setup,
props: propsOption,
render,
beforeCreate,
created,
beforeMount,
mounted,
beforeUpdate,
updated
} = componentOptions;
beforeCreate && beforeCreate();
const state = data ? reactive(data()) : null;
const [props, attrs] = resolveProps(propsOption, vnode.props);
/**
* @description: 组件实例
*/
const instance = {
state,
attrs,
subTree: null,
isMounted: false,
props: shallowReactive(props)
};
const setupContext = { attrs };
const setupResult = setup(shallowReadonly(instance.props), setupContext);
let setupState = null;
if (typeof setupResult === 'function') {
if (render) {
console.log('setup 返回值为函数,将作为渲染函数, render 选项将被忽略');
}
render = setupResult;
} else {
setupState = setupResult;
}
vnode.component = instance;
const renderContext = new Proxy(instance, {
get(target, key, receiver) {
const { state, props } = target;
if (state && key in state) {
return Reflect.get(state, key, receiver);
} else if (key in props) {
return Reflect.get(props, key, receiver);
} else if (setupState && key in setupState) {
return Reflect.get(setupState, key, receiver);
} else {
console.error(`${key} 不存在`);
return false;
}
},
set(target, key, value, receiver) {
const { state, props } = target;
if (state && key in state) {
return Reflect.set(state, key, value, receiver);
} else if (key in props) {
console.warn(`props 是只读的`);
return false;
} else if (key in setupState) {
return Reflect.set(setupState, key, value, receiver);
} else {
console.error(`${key} 不存在`);
return false;
}
}
});
created && created.call(renderContext);
effect(
() => {
const subTree = render.call(renderContext, renderContext);
if (!instance.isMounted) {
beforeMount && beforeMount.call(renderContext);
patch(null, subTree, container, anchor);
instance.isMounted = true;
mounted && mounted.call(renderContext);
} else {
beforeUpdate && beforeUpdate.call(renderContext);
patch(instance.subTree, subTree, container, anchor);
updated && updated.call(renderContext);
}
instance.subTree = subTree;
},
{
scheduler: queueJob
}
);
};
二、测试
const renderOps = {
setText(el, text) {
el.nodeValue = text;
},
createText(text) {
return document.createTextNode(text);
},
createComment(comment) {
return document.createComment(comment);
},
createElement(tag) {
return document.createElement(tag);
},
setElementText(el, text) {
el.textContent = text;
},
insert(el, parent, anchor = null) {
parent.insertBefore(el, anchor);
}
};
const renderer = createRenderer({ ...renderOps, patchProp });
const Component = {
name: 'MyComponent',
props: {
msgProp: String
},
data() {
return {
msg: '组件内容'
};
},
setup() {
const obj = reactive({ a: 1, b: 2 });
return { obj };
},
render() {
return {
type: 'div',
shapeFlag: 9,
children: this.msg + this.msgProp + this.obj.a
};
},
beforeCreate() {
console.log('beforeCreaate');
},
created() {
console.log('created');
},
beforeMount() {
console.log('beforeMount');
},
mounted() {
console.log('mounted');
}
};
const vnode = {
type: Component,
shapeFlag: 4,
props: {
msgProp: '父组件内容'
}
};
renderer.render(vnode, document.querySelector('#app'));