跳到主要内容

霍春阳版

2023年09月11日
柏拉文
越努力,越幸运

一、实现


1.1 this.$emit

1.2 steup emit

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)
};

function emit(event, ...payload) {
const eventName = `on${event[0].toUpperCase() + event.slice(1)}`;
const handler = instance.props[eventName];
if (handler) {
handler(...payload);
} else {
console.log(`${eventName} 事件不存在`);
}
}

const setupContext = { emit, 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
}
);
};

二、测试


2.1 this.$emit

2.2 setup emit

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(props, setupContext) {
const { emit } = setupContext;

emit('handleChange', 1, 2);

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: '父组件内容',
onHandleChange: (argu1, argu2) => {
console.log('自定义事件', argu1, argu2);
}
}
};

renderer.render(vnode, document.querySelector('#app'));