携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情
在 vue3 中使用 高阶组件(HOC)
本篇是关于 vue 编写高阶组件的理论篇,后面会出一篇来编写代码实例。
对于HOC的理解开始是来自 React 文档中:
高阶组件 (HOC) 是 React 中用于重用组件逻辑的高级技术。HOC 本身并不是 React API 的一部分。它们是从 React 的组合性质中出现的一种模式。
因为一直使用 vue 模板语法开发,对于高阶组件的使用一直迷迷糊糊,直到项目启动重构,在使用 vue3 中引入了 jsx 写法,vue2 其实也可以通过引入插件来支持 jsx,而不是说 vue2 就不能写。
本篇我们先了解,下一篇开始编写高阶组件
- 什么是高阶函数
- 什么是高阶组件
- vue渲染函数 h() 应用
高阶函数(HOF)
为了能够理解 Vue 中的高阶组件,我们首先需要了解 JavaScript 中的高阶函数是什么。Javascript 中的高阶函数是将其他函数作为参数并返回另一个函数的函数。
我们可能曾经在 JavaScript 中使用过高阶组件
高阶函数是将函数作为参数接受或将函数作为返回的函数。
一些示例
function add(number, count, callback) {
return callback(number + count)
}
我们可能在不知道高阶函数之前,也不知不觉中使用过高阶函数,看看 Javascript 中内置的一些高阶函数
例如,一些比较常用的Array方法,这些方法都将一个函数作为参数。
- map: 接收一个函数作为回调,每次迭代的返回值组成一个数组作为该函数的返回值
- forEach: 接受一个函数值作为回调,返回 undefined
- filter
- reduce
- ...
JavaScript 中有很多高阶函数可以使用,为我们日常开发提供了便利。
有时候也可以使用其他第三方库的高阶函数,如 Lodash...
- curry
- compose
- partial
- ...
纯函数
高阶函数对它们接收的函数或者值没有影响。它们不会改变输入的参数的值。
高阶组件也必须是纯函数,对它们接收的组件没有影响,它们不会改变输入组件的所有东西。
高阶组件(HOC)
上文提到,高阶函数是将另一个函数作为参数接受或将函数作为返回值的函数;那么 HOC 就是可以将另一个组件作为参数接受或将组件作为返回值的函数。
使用 HOC 我们可以包装子组件,并且在其基础上添加业务逻辑或扩展子组件本身的功能。
返回的组件渲染将包含传递的子组件,但是具有更高级的功能。
渲染函数 h 的应用
关于语法,vue 文档已经非常清楚了,我们通过一个例子来看看在 vue 中的一种使用场景。
我们想要编写这样一个列表:
Item.vue
<template>
<li>
<slot/>
</li>
</template>
ListContainer.vue
<template>
<ul>
<slot />
</ul>
</template>
app.vue
<template>
<ListContainer>
<Item v-for="item in 3" :key="item">
<img width="50" height="50" src="https://images.dog.ceo/breeds/brabancon/n02112706_292.jpg" alt="">
</Item>
</ListContainer>
</template>
可能你们觉得到这里就结束了,现在又有个列表,想要输出一个有序的,如:
或者仅仅将其渲染出来:
现在开始改造 ListContainer.vue
<script setup lang="ts">
import { Component, defineProps, h, useAttrs, useSlots } from 'vue'
interface FlexContainerProps {
tag: keyof HTMLElementTagNameMap;
}
const slots = useSlots()
const attrs = useAttrs()
const props = withDefaults(defineProps<FlexContainerProps>(), {
tag: 'ul'
})
const ListContainer: Component = () => h(props.tag, attrs, slots)
</script>
<template>
<ListContainer>
<slot />
</ListContainer>
</template>
通过使用 h(props.tag, attrs, slots)
,我们编写了一个函数式组件,只需要传递 tag 标签名,就可以渲染对应的标签。
<ListContainer tag="ul">
<Item v-for="item in 3" :key="item">
<img width="50" height="50" src="https://images.dog.ceo/breeds/brabancon/n02112706_292.jpg" alt="">
</Item>
</ListContainer>
<ListContainer tag="ol">
<Item v-for="item in 3" :key="item">
<img width="50" height="50" src="https://images.dog.ceo/breeds/brabancon/n02112706_292.jpg" alt="">
</Item>
</ListContainer>
<ListContainer tag="div">
<Item v-for="item in 3" :key="item">
<img width="50" height="50" src="https://images.dog.ceo/breeds/brabancon/n02112706_292.jpg" alt="">
</Item>
</ListContainer>
这个属性 tag 也可以是组件,那能做的事情就很多了。
我们下一篇就开始编写一些实例来理解它。