携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第19天,点击查看活动详情
上一篇文章中,我们简单了解了什么是高阶函数、什么是高阶组件、h函数的应用场景。
这篇文章开始使用一些案例学习:
- HOC的主要作用是什么
- 正确使用 hoc 以及在哪些场景可以使用;
- 重用逻辑的提取;
使用HOC(高阶组件),我们可以创建一个函数组件,这些函数组件既可以接受内部使用的选项,也可以传递额外的功能。这个功能有助于实现许多事情,而无需在使用它的每个组件中编写相同的逻辑。
作用
在业务开发中,不使用高阶组件也能完成项目的开发,使用它让我更加有编写代码的兴致,使用它写的组件变得比较灵活,也能够提高项目代码复用性,提升开发效率。
1. 注入属性
最常见的功能,将组件的属性,注入到子组件,强化子组件。
我们之前将所有属性注入到单个组件的时候,使用了 v-bind="$attrs",现在使用函数式组件,就需要使用 useAttrs() 这个hook来拿到所有的属性包括事件。
type AttrType = Record<string, unknown>
function WithAttr(WrapperComponent: DefineComponent<{
a: number,
} & Record<string, unknown>>) {
return defineComponent({
setup() {
const number = ref(123)
onMounted(() => {
console.log('with attr mounted')
})
const attr: AttrType = useAttrs()
return () => h(WrapperComponent, {
...attr,
a: number.value
})
}
})
}
// base.vue
<template>
<div>{{ a }} --- {{ b }}</div>
</template>
<script lang="ts" setup>
defineProps({
a: {
type: Number,
default: 0
},
b: {
type: String,
default: ''
}
})
</script>
<template>
<useBaseHoc :b="'bbbb'"></useBaseHoc>
<template>
<script lang="ts" setup>
const UseBaseHoc = withHoc.WithBase(baseHoc)
</script>
2. 状态修改、属性代理
function WithCounter(WrapperComponent: DefineComponent, count) {
return defineComponent({
setup() {
const counter = ref(0)
const increment = () => {
counter.value = counter.value + count
}
const attrs = useAttrs()
return () => h(WrapperComponent, {
increment,
counter: counter.value,
...attrs
})
}
})
}
使用同理,传递第二个参数即可,我们将为计数器创建两个组件,CounterOne.vue
和CounterFive.vue
,第一个组件加上一,第二个组件加上五。
注意:通过属性代理方式实现的高阶组件无法直接操作原组件的属性,但是可以通过 props
回调函数对 属性 进行更新。️
// countHoc.vue
<template>
<div>
<div>count: {{ counter }}</div>
<button @click="increment">+++</button>
</div>
</template>
<script setup lang="ts">
defineProps({
counter: {
type: Number,
default: 0
},
increment: {
type: Function,
default() {
return () => null
}
}
})
</script>
const CounterOne = WithCounter(countHoc, 1)
const CounterFive = WithCounter(countHoc, 5)
3. 控制渲染
应用场景:按钮权限组件,页面权限,懒加载等等。
内部组件不需要也不能操控是否渲染,而是把这一切交给外层组件来判断,比如使用了高阶组件判断访问页面组件是否拥有权限,没有的话通过 useRouter() 跳转到登录页面...
按钮权限:可以封装一个权限按钮
export const superBtn = withAuth(Button, 'super') // 此时 roleType: 'super'
// export const xxxBtn = withAuth(Button, 'xxx')
const { query } = useRouter()
const { role }= query
const isDisable = role === roleType
return () => {
h(WrapperComponent, {
...attr,
disable: isDisable
})
}
通过这样来渲染一个按钮是否显示或者禁用。
4. 列表渲染通用逻辑复用
我们采用之前一篇文章的例子来做修改:
WithRequestDog
function WithRequestDog(WrapperComponent: DefineComponent) {
return (title: string, url: string) => {
return defineComponent({
setup() {
const list = ref([])
const count = ref(2)
const loading = ref(false)
const page = ref(1)
const attr = useAttrs()
const fetchDog = async () => {
loading.value = true
const res = await axios.get(`${url}/${count.value}`)
list.value = res.data.message
loading.value = false
}
onMounted(async () => {
await fetchDog()
})
const loadMore = async () => {
count.value += 2
await fetchDog()
page.value += 1
}
return () => h(WrapperComponent, {
list: list.value,
page: page.value,
loadMore,
title,
loading: loading.value,
...attr
})
}
})
}
}
视图组件
<template>
<div>
<h1>{{ title }}</h1>
<ul v-if="list.length >0">
<li v-for="item in list">
<img :src="item" alt="">
</li>
</ul>
<p>当前第{{ page }}页</p>
<template v-if="loading">loading...</template>
<button @click="loadMore">加载更多</button>
</div>
</template>
<script lang="ts" setup>
import { useAttrs } from 'vue'
defineProps({
list: {
type: Array,
default() {
return []
}
},
title: {
type: String,
default: ''
},
page: {
type: Number,
default: 0
},
loadMore: {
type: Function,
default() {
return () => {
}
}
},
loading: {
type: Boolean,
default: false
}
})
console.log(useAttrs())
</script>
<style>
img {
width: 50px;
height: 50px;
}
</style>
逻辑组件 UseDogA
<template>
<UseDogA></UseDogA>
</template>
<script lang="ts" setup>
// ...
const UseDogA = withbase.WithRequestDog(Base)('Adog', 'https://dog.ceo/api/breeds/image/random')
</script>
逻辑组件 UseDogB
<template>
<UseDogB :a="'asdasda'"></UseDogB>
</template>
<script lang="ts" setup>
// ...
const UseDogB = withbase.WithRequestDog(Base)('Bdog', 'https://dog.ceo/api/breeds/image/random')
</script>
总结
以上就是关于 vue 编写 hoc 的方法与作用的例子,感谢观看