思考
setup可选的第二个参数是context,里面有emit
- setup接收一个对象,里面有emit
- 子组件触发emit(xx),父组件可以通过onXx接收回调
- emit还可以传数据,emit(xx,...arg)
写个例子
/*
* @Author: Lin zefan
* @Date: 2022-03-21 21:46:14
* @LastEditTime: 2022-03-26 12:29:28
* @LastEditors: Lin zefan
* @Description:
* @FilePath: \mini-vue3\example\helloWorld\App.js
*
*/
import { h } from "../../lib/mini-vue.esm.js";
const Foo = {
setup(props, { emit }) {
const emitAdd = () => {
console.log("子组件调用emit/add");
emit(
"add",
{
a: 1,
},
2
);
};
const addCount = () => {
console.log("子组件调用emit/add-count");
emit("add-count", 2);
};
return {
emitAdd,
addCount,
};
},
render() {
return h("div", {}, [
h(
"button",
{
onClick: this.emitAdd,
},
"emit为add"
),
h(
"button",
{
onClick: this.addCount,
},
"emit为add-count"
),
]);
},
};
export default {
name: "App",
setup() {
const onAdd = (...arg) => {
console.log("父组件接收到emit/add", arg);
};
const onAddCount = (...arg) => {
console.log("父组件接收到emit/add-count", arg);
};
return {
onAdd,
onAddCount,
};
},
render() {
return h("div", {}, [
h(Foo, {
onAdd: this.onAdd,
onAddCount: this.onAddCount,
}),
]);
},
};
实现setup注册emit
component.ts
/*
* @Author: Lin zefan
* @Date: 2022-03-21 22:08:11
* @LastEditTime: 2022-03-26 12:57:43
* @LastEditors: Lin zefan
* @Description: 处理组件类型
* @FilePath: \mini-vue3\src\runtime-core\component.ts
*
*/
import { emit } from "./componentEmit";
// 初始化Component结构
function createComponentInstance(initVNode) {
const component = {
vnode: initVNode,
type: initVNode.type,
proxy: null,
setupState: {},
props: {},
emit: () => {},
};
/** 注册emit
* 1. 通过bind把当前实例给到emit函数
*/
component.emit = emit.bind(null, component) as any;
return component;
}
// 初始化setup返回值
function setupStatefulComponent(instance, container) {
/** 获取用户声明的setup函数过程
* 1. 前面通过createApp将根组件转换为vnode
* 2. 之后通过createComponentInstance将vnode进行二次包装
* 3. 最后可以通过instance.type 获取根组件(rootComponent)
*/
const component = instance.type;
const { setup } = component;
if (setup) {
/**
* 1. setup接收props、context
* 2. 执行setup
*/
const setupResult = setup(shallowReadonly(instance.props), {
emit: instance.emit,
});
handleSetupResult(instance, setupResult);
}
}
componentEmit.ts
/*
* @Author: Lin zefan
* @Date: 2022-03-26 11:27:22
* @LastEditTime: 2022-03-26 12:34:05
* @LastEditors: Lin zefan
* @Description: emit
* @FilePath: \mini-vue3\src\runtime-core\componentEmit.ts
*
*/
export function emit({ props }, event) {
/**
* 1. event 为当前emit触发的事件名
* 2. 根据事件名去找到props注册的对应事件,进行调用
*/
const capitalize = (event: string) => {
// 取出首字母,转换为大写 + 切割掉首字母
return event ? event.charAt(0).toLocaleUpperCase() + event.slice(1) : "";
};
const handlerEventName = (event) => {
return "on" + capitalize(event);
};
// 调用props的对应事件
const handler = props[handlerEventName(event)];
handler && handler();
}
实现emit接收参数
componentEmit.ts
/*
* @Author: Lin zefan
* @Date: 2022-03-26 11:27:22
* @LastEditTime: 2022-03-26 12:34:05
* @LastEditors: Lin zefan
* @Description: emit
* @FilePath: \mini-vue3\src\runtime-core\componentEmit.ts
*
*/
export function emit({ props }, event, ...arg) {
/**
* 1. event 为当前emit触发的事件名
* 2. 根据事件名去找到props注册的对应事件,进行调用
* 3. arg是emit接收的数据
*/
const capitalize = (event: string) => {
// 取出首字母,转换为大写 + 切割掉首字母
return event ? event.charAt(0).toLocaleUpperCase() + event.slice(1) : "";
};
const handlerEventName = (event) => {
return "on" + capitalize(event);
};
// 调用props的对应事件
const handler = props[handlerEventName(event)];
handler && handler(...arg);
}
兼容短横线格式
/*
* @Author: Lin zefan
* @Date: 2022-03-26 11:27:22
* @LastEditTime: 2022-03-26 13:04:08
* @LastEditors: Lin zefan
* @Description: emit
* @FilePath: \mini-vue3\src\runtime-core\componentEmit.ts
*
*/
import { handlerEventName } from "../shared/index";
export function emit({ props }, event, ...arg) {
/**
* 1. event 为当前emit触发的事件名
* 2. 根据事件名去找到props注册的对应事件,进行调用
* 3. arg是emit接收的数据
*/
// const handler = props[handlerEventName(event)];
// handler && handler(...arg);
// 将 -字母 转换为 大驼
const camelize = (event: string) => {
/** replace的第二个参数为函数
* 参数一:正则匹配的结,即-x
* 参数二:为\w
*/
// replace的函数回调有个特点,正则里面只要有()包裹的,判断为分组($1),都会单独返会一个结果,所以这里参数2是-后的字母,如果(\w)换成\w,那参数2会是匹配结果的下标
return event.replace(/-(\w)/g, (_, str: string) => {
return str.toUpperCase();
});
};
const capitalize = (event: string) => {
// 取出首字母,转换为大写 + 切割掉首字母
return event ? event.charAt(0).toLocaleUpperCase() + event.slice(1) : "";
};
const handlerEventName = (event) => {
return "on" + capitalize(camelize(event));
};
// other code
}