实现目标:render函数中通过this获取setup返回的属性,获取$el
export const App = {
render() {
return h(
"div",
{
id: "root",
class: ["red", "blue"],
},
"hi, " + this.msg
)
},
setup() {
return {
msg: 'mini-vue'
}
}
}
setupState 获取
在之前实现Component初始化主流程的过程中,我们将setup的返回值赋值给了组件实例instance的属性setupState。因此我们只需要
- 在初始化有状态的组件时,通过
proxy的get操作进行代理。 - 在调用组件实例对象的
render函数时将this指向proxy。
// component.ts
function setupStatefulComponent(instance) {
const Component = instance.type
// 利用 Proxy 对组件实例对象的 proxy property 的 get 进行代理
instance.proxy = new Proxy(
{},
{
get(target, key) {
const { setupState } = instance
if (key in setupState) {
return setupState[key]
}
}
}
)
...
}
// renderer.ts
function setupRenderEffect(instance, container) {
const { proxy } = instance
// 获取 VNode 树,将 this 指向 proxy
const subTree = instance.render.call(proxy)
patch(subTree, container);
}
$el 获取
首先在创建VNode的时候增加一个el的属性,用于保存组件的根元素
// vnode.ts
export function createVNode(type, props?, children?) {
const vnode = {
type,
props,
children,
el: null
};
return vnode;
}
在Element初始化的时候赋值将根据vnode.type创建的DOM元素赋值给vnode.el
// renderder.ts
function mountElement(vnode, container) {
const el = (vnode.el = document.createElement(vnode.type));
...
}
在获取VNode树并且递归patch后,将VNode树的el赋值给VNode
// renderder.ts
function setupRenderEffect(instance, vnode, container) {
const { proxy } = instance;
const subTree = instance.render.call(proxy);
patch(subTree, container);
vnode.el = subTree.el;
}
在处理instance.proxy的地方,判断$el
// component.ts
function setupStatefulComponent(instance) {
const Component = instance.type
// 利用 Proxy 对组件实例对象的 proxy property 的 get 进行代理
instance.proxy = new Proxy(
{},
{
get(target, key) {
const { setupState } = instance
if (key in setupState) {
return setupState[key]
}
if (key === '$el') {
return instance.vnode.el
}
}
}
)
...
}
代码重构
// componentPublicInstance.ts
// 保存 instance上的 property 对应的 getter
const publicPropertiesMap = {
$el: i => i.vnode.el
}
export const PublicInstanceHandlers = {
get({_:instance}, key) {
const { setupState } = instance;
if (key in setupState) {
return setupState[key];
}
const publicGetter = publicPropertiesMap[key];
if (publicGtter) {
return publicGetter()
}
}
}
// component.ts
function setupStatefulComponent(instance) {
const component = instance.type;
instance.proxy = new Proxy({ _: instance }, PublicInstanceProxyhandlers);
...
}