实现事件注册 & props 添加到标签
// runtime-core/renderer.ts
function mountElement() {
// 1. 创建标签
const el = (vnode.el = hostCreateElement(vnode.type));
// 2. 处理标签内容
const { children, shapeFlag } = vnode;
// children 是字符串
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
el.textContent = children;
// children 是数组
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
mountChildren(vnode.children, el, parentComponent, anchor);
}
const { props } = vnode;
+ // 3. 处理标签的 props 属性
+ // 循环 props
+ for (const key in props) {
+ const val = props[key];
+ // 处理注册事件和 props
+ hostPatchProp(el, key, null, val);
+ }
// 4. 把 el 添加到 container
container.append(el);
}
function hostPatchProp(el, key, prevVal, nextVal) {
const isOn = (key: string) => /^on[A-Z]/.test(key);
if (isOn(key)) {
const event = key.slice(2).toLowerCase();
el.addEventListener(event, nextVal);
} else {
if(nextVal === undefined || nextVal === null) {
el.removeAttribute(key);
} else {
el.setAttribute(key, nextVal);
}
}
}
props 挂载到 instance
export function createComponentInstance(vnode, parent) {
const component = {
props: {},
...
};
return component;
}
export function setupComponent(instance, container) {
initProps(instance, instance.vnode.props);
setupStatefulComponent(instance);
}
props 作为参数传给 setup
export function initProps(instance, rawProps) {
instance.props = rawProps || {};
}
function setupStatefulComponent(instance) {
const Component = instance.type;
const { setup } = Component;
...
if(setup) {
const setupResult = setup(shallowReadonly(instance.props));
...
}
}
$porps 挂载到组件代理对象
const PublicPropertiesMaps = {
$props: (i) => i.props,
}
export const PublicInstanceProxyHandlers = {
get({ _: instance }, key) {
const { setupState, props } = instance;
const hasOwn = (val, key) => Object.prototype.hasOwnProperty.call(val, key);
if(hasOwn(setupState, key)) {
return setupState[key];
} else if(hasOwn(props, key)) {
return props[key];
}
const publicGetter = PublicPropertiesMaps[key];
if(publicGetter) {
return publicGetter(instance);
}
}
}
实现 emit
emit 挂载到 instance
import { emit } from './componentEmit';
export function createComponentInstance(vnode, parent) {
const component = {
emit: () => {},
};
component.emit = emit.bind(null, component) as any;
return component;
}
从 props 中找到事件名称并执行事件
import { camelize, toHandlerKey } from '../shared/index';
export function emit(instance, event, ...args) {
const { props } = instance;
const handlerName = toHandlerKey(camelize(event));
const hanlder = props[handlerName];
hanlder && hanlder(...args);
}
export const camelize = (str: string) => {
return str.replace(/-(\w)/g, (_, c: string) => {
return c ? c.toUpperCase() : '';
})
}
export const toHandlerKey = (str) => {
return str ? 'on' + capitalize(str) : '';
}
const capitalize = (str: string) => {
return str ? str.charAt(0).toUpperCase() + str.slice(1) : '';
}
emit 作为参数传给 setup
function setupStatefulComponent(instance) {
const Component = instance.type;
const { setup } = Component;
...
if(setup) {
const setupResult = setup(shallowReadonly(instance.props), {
emit: instance.emit,
});
...
}
}