- vue的render函数是可以通过this.xxx去获取options api和 composition api的数据的
- 基于hello word的例子,实现数据代理
实现setupState注册
思考
- handleSetupResult函数中把setup返回的数据注入到了实例的setupState对象中
- 所以可以在setupComponent执行完后,去挂载render的this,让其可以取到setupState对象中的数据
- vue中render挂载的this实例中可能有 props之类的数据,所以统一用proxy来收集
- setupRenderEffect函数中会去执行render,并绑定render的this
function mountComponent(vnode, container) {
// 初始化Component实例
const instance = createComponentInstance(vnode);
// 初始化setup函数return的数据
setupComponent(instance, container);
+ /** 挂载render的this
+ * 1. 我们可以借助proxy来挂载我们的实例属性,让proxy代理
+ * 2. 最后render的时候,把this指向这个proxy,这样就可以通过 this.xx -> proxy.get(xx) 获取数 + 据
+ */
+ createProxyInstance(instance);
// setupRenderEffect
setupRenderEffect(instance, container);
}
// 初始化组件代理
function createProxyInstance(instance) {
instance.proxy = new Proxy(
{},
{
get(target, key) {
let { setupState } = instance;
// 获取setup返回的数据
if (key in setupState) {
return setupState[key];
}
},
}
);
}
function setupRenderEffect(instance, container) {
+ const { proxy } = instance;
+ // 通过render函数,获取render返回虚拟节点,并绑定render的this
+ const subTree = instance.render.call(proxy);
// 最后通过patch的processElement,将subTree渲染到container(节点)上
patch(subTree, container);
}
实现 $el
思考
- this.$el 取的是当前组件dom
- 在 processElement函数中 创建的el其实就是当前的$el
- 可以在setupRenderEffect函数中将processElement函数创建的el挂载到vnode上
component.ts
- 注册$el到实例
function setupRenderEffect(instance, container) {
----other code ----
/** 挂载当前的dom元素到$el
* 1. 当遍历完所有Component组件后,会调用processElement
* 2. 在processElement中,会创建dom元素,把创建的dom元素挂载到传入的vnode里面
* 3. 当前的dom元素也就是processElement中创建的dom元素
*/
+ vnode.el = subTree.$el;
}
- proxy收集
function createProxyInstance(instance) {
// instance.proxy = new Proxy({ _: instance }, PublicInstanceProxyHandlers);
instance.proxy = new Proxy(
{},
{
get(target, key) {
+ let { setupState, vnode } = instance;
+ if (key === "$el") {
+ return vnode.el;
+ }
// 获取 setup返回的数据
if (key in setupState) {
return setupState[key];
}
},
}
);
}
element.ts
export function processElement(vnode, container) {
const { type, props, children } = vnode;
// 创建根元素
const el = document.createElement(type);
// 将dom元素挂载到实例
vnode.$el = el;
----other code----
}
优化代码
优化 createProxyInstance
function createProxyInstance(instance) {
instance.proxy = new Proxy({ _: instance }, PublicInstanceProxyHandlers);
}
抽离proxy,创建 componentPublicInstanceProxyHandlers.ts
/*
* @Author: Lin zefan
* @Date: 2022-03-23 17:52:57
* @LastEditTime: 2022-03-23 22:26:50
* @LastEditors: Lin zefan
* @Description:
* @FilePath: \mini-vue3\src\runtime-core\componentPublicInstanceProxyHandlers.ts
*
*/
// 扩展的实例Map
const PublicInstanceMap = {
$el: (i) => i.vnode.el,
};
export const PublicInstanceProxyHandlers = {
get({ _: instance }, key) {
let { setupState } = instance;
// 获取 setup返回的数据
if (key in setupState) {
return setupState[key];
}
// 获取instance实例对象
const publicGetter = PublicInstanceMap[key];
if (publicGetter) {
return publicGetter(instance);
}
},
};