共计 3630 个字符,预计需要花费 10 分钟才能阅读完成。
前言
vue3 新个性,hooks 钩子函数,用过 react hooks 的应该很相熟,思维是统一的,学习老本也不高,跟一般的函数相比,最大的特点就是能再函数中应用组件 / 页面的一些生命周期了,这时你应该会马上想到 vue2 的 mixin 函数,然而 hooks 会比 mixin 更加灵便,且副作用更小。
vue3 组件传值除了 emit 和 props,举荐 provide/injeect,store。跨组件传值曾经不能像之前一样 new Vue(),因为实例上的 $on
、$off
和 $once
实例办法被删除了。所以我找了他的代替计划:miit
:Tiny 200b functional event emitter / pubsub.
即小型性能事件发射接管,相似 vue2 版本中的$emit
、$on
npm install --save mitt
在组件层级简单的状况下,这十分有必要,且集体认为,应用频率较高,该需要出险频率十分高
附上链接:https://www.npmjs.com/package…
前置知识点
getCurrentInstance
: 获取以后组件的实例,顺便提一句,vue3 不反对操作 prototype 一样去挂载全局办法,倡议应用 getCurrentInstance().appContext.config.globalProperties
拜访全局属性 getCurrentInstance
只能 在 setup 或生命周期钩子中调用。
需要
在组件开发中,不仅有子组件须要调用父组件的场景,也有场景是相同的,父组件须要调用子组件的计划,vue3+ts
如果你间接 ref.xxx 拜访子组件的办法或报错,因为它并不知道你的子组件中有什么办法,有人间接把整个子组件 $emit 给父组件,而后调用他的属性,不举荐此计划,不思考性能,我感觉很繁琐。那么,咱们实现的 emitter 就必须要有以下的根底性能:
- 向上发送事件(dispatch)
- 向下发送事件(broadcast)
- 只调用一次工夫(once)
- 接管事件(on)
- 销毁事件(off)
代码实
function on(type, handler) {const handleWrapper = (e) => {const { value, type, emitComponentInstance} = e
if (type === BROADCAST) { // 监听是否是播送事件
if (isChildComponent(currentComponentInstance, emitComponentInstance)) { // 播送即以后接管的组件是子组件时,因为播送是从向往下
handler && handler(...value)
}
} else if (type === DISPATCH) { // 监听是 dispatch,以后接管的组件是父组件
if (isChildComponent(emitComponentInstance, currentComponentInstance)) { // 判断以后组件是否是发送事件组件的子组件的判断逻辑
handler && handler(...value)
}
} else {handler && handler(...value)
}
}
// 把接管到的 handler 保存起来,因为须要 off 掉,留神这个 handler 必要放弃惟一,function 是援用类型,传递给 off 的时候,必须找到雷同的地址才行。这里的 `wrapper` 变量能够应用 `Symbol` 类型
handler[wrapper] = handleWrapper
emitter.on(type, handleWrapper)
}
function broadcast(type, ...args) {
emitter.emit(type, {
type: BROADCAST, // 事件类型,辨别第一个参数,第一个参数是事件名称
emitComponentInstance: currentComponentInstance, // 以后组件实例
value: args
})
}
// 逻辑同上,工夫类型不同
function dispatch(type, ...args) {
emitter.emit(type, {
type: DISPATCH,
emitComponentInstance: currentComponentInstance,
value: args
})
}
// off 掉的是惟一雷同的 handler!function off(type, handler) {emitter.off(type, handler[wrapper])
}
// 执行一次的原理就是调用后即 off 掉
function once(type, handler) {const handleOn = (...args) => {handler && handler(...args)
off(type, handleOn)
}
on(type, handleOn)
}
残缺代码
import {getCurrentInstance} from 'vue'
import mitt from 'mitt'
const DISPATCH = 'dispatch'
const BROADCAST = 'broadcast'
const wrapper = Symbol('wrapper')
const emitter = mitt()
export function useEmitter() {const currentComponentInstance = getCurrentInstance()
function on(type, handler) {const handleWrapper = (e) => {const { value, type, emitComponentInstance} = e
if (type === BROADCAST) {if (isChildComponent(currentComponentInstance, emitComponentInstance)) {handler && handler(...value)
}
} else if (type === DISPATCH) {if (isChildComponent(emitComponentInstance, currentComponentInstance)) {handler && handler(...value)
}
} else {handler && handler(...value)
}
}
// Save the real handler because the need to call off
handler[wrapper] = handleWrapper
emitter.on(type, handleWrapper)
}
function broadcast(type, ...args) {
emitter.emit(type, {
type: BROADCAST,
emitComponentInstance: currentComponentInstance,
value: args
})
}
function dispatch(type, ...args) {
emitter.emit(type, {
type: DISPATCH,
emitComponentInstance: currentComponentInstance,
value: args
})
}
function off(type, handler) {emitter.off(type, handler[wrapper])
}
function once(type, handler) {const handleOn = (...args) => {handler && handler(...args)
off(type, handleOn)
}
on(type, handleOn)
}
return {
on,
broadcast,
dispatch,
off,
once
}
}
/**
* check componentChild is componentParent child components
* @param {*} componentChild
* @param {*} componentParent
*/
function isChildComponent(componentChild, componentParent) {
const parentUId = componentParent.uid
while (componentChild && componentChild?.parent?.uid !== parentUId) {componentChild = componentChild.parent}
return Boolean(componentChild)
}
根本应用
const {dispatch, on, broadcast} = useEmitter()
on('update:modelValue', (v) => {emit('update:modelValue', v)
dispatch('custom.value.change', v)
})
broadcast('fieldReset', 'reset')