代码运行结果
代码示例
<script>
let { createApp, reactive, toRefs, h } = Vue
const useCount = () => {
const state = reactive({ count: 0 })
const setCount = () => {
state.count++
}
return { state, setCount }
}
const MyCpn = {
props: {
count: {},
onChildUpdate: {}
},
setup(props, { emit }) {
return () =>
h(
'button',
{
onClick: () => {
emit('childUpdate')
}
},
[props.count]
)
}
}
const App = {
setup() {
let { state, setCount } = useCount()
return {
...toRefs(state),
setCount
}
},
render() {
return h(MyCpn, {
count: this.count,
onChildUpdate: () => {
this.setCount()
}
})
}
}
createApp(App).mount('#app')
</script>
emit 原理
第一:挂载逻辑可以看以前的文章:Vue 组件挂载原理, 这里我们聊关键的部分。
- 挂载子组件的时候,将 count, onChildUpdate 自定义事件函数 作为了子组件的 props, 根据上图也可以得知。
- 当我们在子组件中 emit 的时候,实际上调用调用的是 instance.emit 函数。
- 在 emit 函数中,对于本例子来说,emit("childUpdate"), emit 函数会将 childUpdate 进行 handleKey, camelize 函数处理得到 onChildUpdate 事件名字,然后去 Props 当中去寻找。并且调用。
- 另一种,情况是 v-model 的情况,也就是以update 开头的事件名字,Vue 同样也会对这种情况从 Props 中去找 update:modleValue 事件名, 同时也会找受控值 modelValue。
export function emit(
instance: ComponentInternalInstance,
event: string,
...rawArgs: any[]
) {
const props = instance.vnode.props || EMPTY_OBJ
let args = rawArgs
const isModelListener = event.startsWith('update:')
// for v-model update:xxx events, apply modifiers on args
const modelArg = isModelListener && event.slice(7)
if (modelArg && modelArg in props) {
const modifiersKey = `${
modelArg === 'modelValue' ? 'model' : modelArg
}Modifiers`
const { number, trim } = props[modifiersKey] || EMPTY_OBJ
if (trim) {
args = rawArgs.map(a => (isString(a) ? a.trim() : a))
}
if (number) {
args = rawArgs.map(looseToNumber)
}
}
let handlerName
let handler =
props[(handlerName = toHandlerKey(event))] ||
// also try camelCase event handler (#2249)
props[(handlerName = toHandlerKey(camelize(event)))]
// for v-model update:xxx events, also trigger kebab-case equivalent
// for props passed via kebab-case
if (!handler && isModelListener) {
handler = props[(handlerName = toHandlerKey(hyphenate(event)))]
}
if (handler) {
callWithAsyncErrorHandling(
handler,
instance,
ErrorCodes.COMPONENT_EVENT_HANDLER,
args
)
}