实现 emit 功能
- 功能点
- 实现 emit 函数,用于触发组件自定义事件
- 需求分析
- 在组件实例上挂载 emit 方法
- 组件通过 props 接收事件处理函数
- 触发事件时调用对应的处理函数
- 代码实现
js
复制代码
// Foo.js 子组件
import { h } from '../lib/guide-mini-vue.esm.js'
export const Foo = {
setup(props,{emit}) { // ✅ emit 解构传递
// console.log(props.count, 'count');
const handleClick = () => {
emit('add',1,2,3) // ✅ emit 触发父组件自定义事件,并进行参数传递
emit('add-money-brother') // ✅ emit 触发父组件自定义事件(烤肉串命名)
}
return {
handleClick
}
},
render() {
const btn = h('button', {
onClick: this.handleClick
}, 'emit click')
return h("div", {
}, ["foo:" + this.count, btn])
},
}
js
复制代码
// App.js 父组件
import { h } from '../lib/guide-mini-vue.esm.js'
import { Foo } from './Foo.js'
window.self = null
export const App = {
name: 'App',
render() {
window.self = this
return h("div",{
id: 'root',
class: ['red', 'hard'],
},
[h("p",{class:"red"}, "hi"),h(Foo,{count: 1,
onAdd(a,b,c) { // ✅ 自定义事件触发 + 传参
console.log('我是父组件',a,b,c);
},
onAddMoneyBrother() { // ✅ 烤肉串式命名自定义事件触发
console.log('I am Father Component');
}
})]
)
},
setup() {
return {
msg: "mini-vue-haha",
}
}
}
js
复制代码
// componentEmit.ts
// ✅ 这里的 emit 方法,挂载在实例上,后续直接传入 setup, 在页面被解构出来使用
// 换句话说,这里的 emit 就是页面上调用的 emit
// 可是页面上 传入的都是 add / add-money-brother 这些名称参数,要和父组件中的 自定义事件名对上,拿到对应的方法执行,就得做一番转换
// TPP 先实现一个特定的需求,然后扩展到通用的需求,这里直接展示的是最终结果
export function emit(instance, event, ...args) {
// 调用 子组件传过来的函数
const { props } = instance
const camelize = (str) => { // ✅ 驼峰转化
return str.replaceAll(/-(\w)/g, (_, c: string) => {
return c ? c.toUpperCase() : ''
})
}
const capitalize = (str) => { // ✅ 首字母大写
return str.charAt(0).toLocaleUpperCase() + str.slice(1)
}
const handlerkey = (str) => { // ✅ 拼接出 事件处理函数名
return str ? 'on' + capitalize(str) : ''
}
const handlerName = handlerkey(camelize(event))
const handler = props[handlerName]
handler && handler(...args)
}
js
复制代码
// component.ts
import { emit } from "./componentEmit"
export function createComponentInstance(vnode) {
const component = {
vnode,
type: vnode.type,
setupState: {},
props: {},
emit:()=>{} // ✅ 这里初始化占一个位置
}
component.emit = emit.bind(null, component) as any // ✅ 这里 null 是this指向, component 作为第一个参数传入
return component
}
function setupStatefulComponents(instance: any) {
const Component = instance.vnode.type
const { setup } = Component
instance.proxy = new Proxy({_: instance}, PublicInstanceProxyHandlers)
if(setup) {
const setupResult = setup(shallowReadonly(instance.props),{
emit: instance.emit // ✅ 这里将实例上的 emit 传入
})
handleSetupResult(instance, setupResult)
}
}
js
复制代码
// shared/ index.ts ✅ 方法抽离
export const camelize = (str) => {
return str.replaceAll(/-(\w)/g, (_, c: string) => {
return c ? c.toUpperCase() : ''
})
}
const capitalize = (str) => {
return str.charAt(0).toLocaleUpperCase() + str.slice(1)
}
export const handlerkey = (str) => {
return str ? 'on' + capitalize(str) : ''
}