vue3 中实现类似于 React 高阶组件的功能?
#vue3
目录
- 1. 总结
- 2. 组合式函数(Composables)
- 3. 渲染函数 和 JSX:withLogger
- 4. 插件和全局混入(Mixin)
- 5. 自定义指令
- 6. 提供/注入(Provide/Inject)
1. 总结
- 使用
h 函数
再包装一下可以实现 HOC - 组合式函数
- 混入 app.mixin 等
- 自定义指令
- provide & inject
2. 组合式函数(Composables)
这是Vue 3中最接近React高阶组件概念的方法。组合式函数允许我们封装和重用有状态逻辑。
示例:useLogger
// useLogger.ts
import { ref, onMounted, onUnmounted } from 'vue'
export function useLogger(componentName: string) {
const mountedTime = ref<number>(0)
onMounted(() => {
console.log(`${componentName} mounted`)
mountedTime.value = Date.now()
})
onUnmounted(() => {
console.log(`${componentName} unmounted after ${Date.now() - mountedTime.value}ms`)
})
}
// 使用方式
// MyComponent.vue
import { defineComponent } from 'vue'
import { useLogger } from './useLogger'
export default defineComponent({
name: 'MyComponent',
setup() {
useLogger('MyComponent')
// 组件的其他逻辑...
}
})
3. 渲染函数 和 JSX:withLogger
Vue 3支持使用渲染函数和JSX,这让我们可以更接近 React的编程方式,包括实现类似HOC的模式。
示例:
// withLogger.tsx
import { defineComponent, h, onMounted, onUnmounted } from 'vue'
export function withLogger(WrappedComponent: any) {
return defineComponent({
setup(props, { attrs, slots }) {
onMounted(() => {
console.log(`${WrappedComponent.name} mounted`)
})
onUnmounted(() => {
console.log(`${WrappedComponent.name} unmounted`)
})
return () => h(WrappedComponent, { ...props, ...attrs }, slots)
}
})
}
// 使用方式
// MyComponent.tsx
import { defineComponent } from 'vue'
import { withLogger } from './withLogger'
const MyComponent = defineComponent({
name: 'MyComponent',
setup() {
return () => <div>Hello from MyComponent</div>
}
})
export default withLogger(MyComponent)
4. 插件和全局混入(Mixin)
虽然不如组合式函数灵活,但对于某些场景,我们可以使用Vue插件或全局混入来添加跨组件的功能。
示例:
// logger-plugin.ts
import { Plugin } from 'vue'
export const LoggerPlugin: Plugin = {
install(app) {
app.mixin({
mounted() {
console.log(`${this.$options.name} mounted`)
},
unmounted() {
console.log(`${this.$options.name} unmounted`)
}
})
}
}
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { LoggerPlugin } from './logger-plugin'
const app = createApp(App)
app.use(LoggerPlugin)
app.mount('#app')
5. 自定义指令
对于一些特定的功能,我们可以使用Vue的自定义指令来实现类似HOC的效果。
示例:
// logger-directive.ts
import { Directive } from 'vue'
export const logger: Directive = {
mounted(el, binding) {
console.log(`${binding.value} mounted`)
},
unmounted(el, binding) {
console.log(`${binding.value} unmounted`)
}
}
// 使用方式
// MyComponent.vue
<template>
<div v-logger="'MyComponent'">
<!-- 组件内容 -->
</div>
</template>
<script>
import { logger } from './logger-directive'
export default {
directives: {
logger
}
}
</script>
6. 提供/注入(Provide/Inject)
对于需要在组件树中==共享数据或功能==的场景,我们可以使用 Vue 3的 provide/inject
特性。
示例:
// LoggerProvider.vue
<script lang="ts">
import { defineComponent, provide } from 'vue'
export const loggerKey = Symbol()
export default defineComponent({
setup() {
const logger = {
log: (componentName: string, message: string) => {
console.log(`[${componentName}] ${message}`)
}
}
provide(loggerKey, logger)
return () => null
}
})
</script>
// 使用方式
// MyComponent.vue
<script lang="ts">
import { defineComponent, inject, onMounted } from 'vue'
import { loggerKey } from './LoggerProvider.vue'
export default defineComponent({
setup() {
const logger = inject(loggerKey)
onMounted(() => {
logger?.log('MyComponent', 'Component mounted')
})
// 组件的其他逻辑...
}
})
</script>