找回密码
 立即注册
首页 业界区 业界 让我看看有多少人不知道Vue3中也能实现高阶组件HOC ...

让我看看有多少人不知道Vue3中也能实现高阶组件HOC

仲水悦 3 天前
前言

高阶组件HOC在React社区是非常常见的概念,但是在Vue社区中却是很少人使用。主要原因有两个:1、Vue中一般都是使用SFC,实现HOC比较困难。2、HOC能够实现的东西,在Vue2时代mixins能够实现,在Vue3时代Composition API能够实现。如果你不知道HOC,那么你平时绝对没有场景需要他。但是如果你知道HOC,那么在一些特殊的场景使用他就可以很优雅的解决一些问题。
欧阳也在找工作,坐标成都求内推!
什么是高阶组件HOC

HOC使用场景就是加强原组件。
HOC实际就是一个函数,这个函数接收的参数就是一个组件,并且返回一个组件,返回的就是加强后组件。如下图:
1.png

在Composition API出现之前HOC还有一个常见的使用场景就是提取公共逻辑,但是有了Composition API后这种场景就无需使用HOC了。
高阶组件HOC使用场景

很多同学觉得有了Composition API后,直接无脑使用他就完了,无需费时费力的去搞什么HOC。那如果是下面这个场景呢?
有一天产品找到你,说要给我们的系统增加会员功能,需要让系统中的几十个功能块增加会员可见功能。如果不是会员这几十个功能块都显示成引导用户开通会员的UI,并且这些功能块涉及到几十个组件,分布在系统的各个页面中。
如果不知道HOC的同学一般都会这样做,将会员相关的功能抽取成一个名为useVip.ts的hooks。代码如下:
  1. export function useVip() {
  2.   function getShowVipContent() {
  3. <template>
  4.   <Block1
  5.     v-if="showVipContent"
  6.     :name="name1"
  7.     @changeName="(value) => (name1 = value)"
  8.   />
  9.   <OpenVipTip v-else />
  10. </template>// 一些业务逻辑判断是否是VIP
  11. <template>
  12.   <Block1
  13.     v-if="showVipContent"
  14.     :name="name1"
  15.     @changeName="(value) => (name1 = value)"
  16.   />
  17.   <OpenVipTip v-else />
  18. </template>return false;
  19.   }
  20.   return {
  21. <template>
  22.   <Block1
  23.     v-if="showVipContent"
  24.     :name="name1"
  25.     @changeName="(value) => (name1 = value)"
  26.   />
  27.   <OpenVipTip v-else />
  28. </template>showVipContent: getShowVipContent(),
  29.   };
  30. }
复制代码
然后再去每个具体的业务模块中去使用showVipContent变量判断,v-if="showVipContent"显示原模块,v-else显示引导开通会员UI。代码如下:
  1. <template>
  2.   <Block1
  3.     v-if="showVipContent"
  4.     :name="name1"
  5.     @changeName="(value) => (name1 = value)"
  6.   />
  7.   <OpenVipTip v-else />
  8. </template>
复制代码
我们系统中有几十个这样的组件,那么我们就需要这样去改几十次。非常麻烦,如果有些模块是其他同事写的代码还很容易改错!!!
而且现在流行搞SVIP,也就是光开通VIP还不够,需要再开通一个SVIP。当你后续接到SVIP需求时,你又需要去改这几十个模块。v-if="SVIP"显示某些内容,v-else-if="VIP"显示提示开通SVIP,v-else显示提示开通VIP。
上面的这一场景使用hooks去实现,虽然能够完成,但是因为入侵了这几十个模块的业务逻辑。所以容易出错,也改起来比较麻烦,代码也不优雅。
那么有没有一种更好的解决方案,让我们可以不入侵这几十个模块的业务逻辑的实现方式呢?
答案是:高阶组件HOC。
HOC的一个用途就是对组件进行增强,并且不会入侵原有组件的业务逻辑,在这里就是使用HOC判断会员相关的逻辑。如果是会员那么就渲染原本的模块组件,否则就渲染引导开通VIP的UI
实现一个简单的HOC


首先我们要明白Vue的组件经过编译后就是一个对象,对象中的props属性对应的就是我们写的defineProps。对象中的setup方法,对应的就是我们熟知的[/code]这个代码相比前面的hooks的实现就简单很多了,只需要使用高阶组件WithVip对原来的Block1组件包一层,然后将原本使用Block1的地方改为使用EnhancedBlock1。对原本的代码基本没有入侵。
上面的例子只是一个简单的demo,他是不满足我们实际的业务场景。比如子组件有props、emit、插槽。还有我们在父组件中可能会直接调用子组件expose暴露的方法。
因为我们使用了HOC对原本的组件进行了一层封装,那么上面这些场景HOC都是不支持的,我们需要添加一些额外的代码去支持。
高阶组件HOC实现props和emit

在Vue中属性分为两种,一种是使用props和emit声明接收的属性。第二种是未声明的属性attrs,比如class、style、id等。
在setup函数中props是作为第一个参数返回,attrs是第二个参数中返回。
所以为了能够支持props和emit,我们的高阶组件WithVip将会变成下面这样:
  1. import { SetupContext, h } from "vue";import OpenVipTip from "./open-vip-tip.vue";export default function WithVip(BaseComponent: any) {  return {<template>
  2.   <Block1
  3.     v-if="showVipContent"
  4.     :name="name1"
  5.     @changeName="(value) => (name1 = value)"
  6.   />
  7.   <OpenVipTip v-else />
  8. </template>props: BaseComponent.props,  // 新增代码<template>
  9.   <Block1
  10.     v-if="showVipContent"
  11.     :name="name1"
  12.     @changeName="(value) => (name1 = value)"
  13.   />
  14.   <OpenVipTip v-else />
  15. </template>setup(props, { attrs, slots, expose }: SetupContext) {  // 新增代码<template>
  16.   <Block1
  17.     v-if="showVipContent"
  18.     :name="name1"
  19.     @changeName="(value) => (name1 = value)"
  20.   />
  21.   <OpenVipTip v-else />
  22. </template>  const showVipContent = getShowVipContent();<template>
  23.   <Block1
  24.     v-if="showVipContent"
  25.     :name="name1"
  26.     @changeName="(value) => (name1 = value)"
  27.   />
  28.   <OpenVipTip v-else />
  29. </template>  function getShowVipContent() {<template>
  30.   <Block1
  31.     v-if="showVipContent"
  32.     :name="name1"
  33.     @changeName="(value) => (name1 = value)"
  34.   />
  35.   <OpenVipTip v-else />
  36. </template><template>
  37.   <Block1
  38.     v-if="showVipContent"
  39.     :name="name1"
  40.     @changeName="(value) => (name1 = value)"
  41.   />
  42.   <OpenVipTip v-else />
  43. </template>// 一些业务逻辑判断是否是VIP<template>
  44.   <Block1
  45.     v-if="showVipContent"
  46.     :name="name1"
  47.     @changeName="(value) => (name1 = value)"
  48.   />
  49.   <OpenVipTip v-else />
  50. </template><template>
  51.   <Block1
  52.     v-if="showVipContent"
  53.     :name="name1"
  54.     @changeName="(value) => (name1 = value)"
  55.   />
  56.   <OpenVipTip v-else />
  57. </template>return true;<template>
  58.   <Block1
  59.     v-if="showVipContent"
  60.     :name="name1"
  61.     @changeName="(value) => (name1 = value)"
  62.   />
  63.   <OpenVipTip v-else />
  64. </template>  }<template>
  65.   <Block1
  66.     v-if="showVipContent"
  67.     :name="name1"
  68.     @changeName="(value) => (name1 = value)"
  69.   />
  70.   <OpenVipTip v-else />
  71. </template>  return () => {<template>
  72.   <Block1
  73.     v-if="showVipContent"
  74.     :name="name1"
  75.     @changeName="(value) => (name1 = value)"
  76.   />
  77.   <OpenVipTip v-else />
  78. </template><template>
  79.   <Block1
  80.     v-if="showVipContent"
  81.     :name="name1"
  82.     @changeName="(value) => (name1 = value)"
  83.   />
  84.   <OpenVipTip v-else />
  85. </template>return showVipContent<template>
  86.   <Block1
  87.     v-if="showVipContent"
  88.     :name="name1"
  89.     @changeName="(value) => (name1 = value)"
  90.   />
  91.   <OpenVipTip v-else />
  92. </template><template>
  93.   <Block1
  94.     v-if="showVipContent"
  95.     :name="name1"
  96.     @changeName="(value) => (name1 = value)"
  97.   />
  98.   <OpenVipTip v-else />
  99. </template>  ? h(BaseComponent, {<template>
  100.   <Block1
  101.     v-if="showVipContent"
  102.     :name="name1"
  103.     @changeName="(value) => (name1 = value)"
  104.   />
  105.   <OpenVipTip v-else />
  106. </template><template>
  107.   <Block1
  108.     v-if="showVipContent"
  109.     :name="name1"
  110.     @changeName="(value) => (name1 = value)"
  111.   />
  112.   <OpenVipTip v-else />
  113. </template><template>
  114.   <Block1
  115.     v-if="showVipContent"
  116.     :name="name1"
  117.     @changeName="(value) => (name1 = value)"
  118.   />
  119.   <OpenVipTip v-else />
  120. </template>  ...props, // 新增代码<template>
  121.   <Block1
  122.     v-if="showVipContent"
  123.     :name="name1"
  124.     @changeName="(value) => (name1 = value)"
  125.   />
  126.   <OpenVipTip v-else />
  127. </template><template>
  128.   <Block1
  129.     v-if="showVipContent"
  130.     :name="name1"
  131.     @changeName="(value) => (name1 = value)"
  132.   />
  133.   <OpenVipTip v-else />
  134. </template><template>
  135.   <Block1
  136.     v-if="showVipContent"
  137.     :name="name1"
  138.     @changeName="(value) => (name1 = value)"
  139.   />
  140.   <OpenVipTip v-else />
  141. </template>  ...attrs, // 新增代码<template>
  142.   <Block1
  143.     v-if="showVipContent"
  144.     :name="name1"
  145.     @changeName="(value) => (name1 = value)"
  146.   />
  147.   <OpenVipTip v-else />
  148. </template><template>
  149.   <Block1
  150.     v-if="showVipContent"
  151.     :name="name1"
  152.     @changeName="(value) => (name1 = value)"
  153.   />
  154.   <OpenVipTip v-else />
  155. </template><template>
  156.   <Block1
  157.     v-if="showVipContent"
  158.     :name="name1"
  159.     @changeName="(value) => (name1 = value)"
  160.   />
  161.   <OpenVipTip v-else />
  162. </template>})<template>
  163.   <Block1
  164.     v-if="showVipContent"
  165.     :name="name1"
  166.     @changeName="(value) => (name1 = value)"
  167.   />
  168.   <OpenVipTip v-else />
  169. </template><template>
  170.   <Block1
  171.     v-if="showVipContent"
  172.     :name="name1"
  173.     @changeName="(value) => (name1 = value)"
  174.   />
  175.   <OpenVipTip v-else />
  176. </template>  : h(OpenVipTip);<template>
  177.   <Block1
  178.     v-if="showVipContent"
  179.     :name="name1"
  180.     @changeName="(value) => (name1 = value)"
  181.   />
  182.   <OpenVipTip v-else />
  183. </template>  };<template>
  184.   <Block1
  185.     v-if="showVipContent"
  186.     :name="name1"
  187.     @changeName="(value) => (name1 = value)"
  188.   />
  189.   <OpenVipTip v-else />
  190. </template>},  };}
复制代码
在setup方法中接收的第一个参数就是props,没有在props中定义的属性就会出现在attrs对象中。
所以我们调用h函数时分别将props和attrs透传给子组件。
同时我们还需要一个地方去定义props,props的值就是直接读取子组件对象中的BaseComponent.props。所以我们给高阶组件声明一个props属性:props: BaseComponent.props,。
这样props就会被透传给子组件了。
看到这里有的小伙伴可能会问,那emit触发事件没有看见你处理呢?
答案是:我们无需去处理,因为父组件上面的@changeName="(value) => (name1 = value)"经过编译后就会变成属性:nChangeName="(value) => (name1 = value)"。而这个属性由于我们没有在props中声明,所以他会作为attrs直接透传给子组件。
高阶组件实现插槽

我们的正常子组件一般还有插槽,比如下面这样:
  1. <template>
  2.   <Block1
  3.     v-if="showVipContent"
  4.     :name="name1"
  5.     @changeName="(value) => (name1 = value)"
  6.   />
  7.   <OpenVipTip v-else />
  8. </template>  [size=6]{{ name }}[/size]
  9. <template>
  10.   <Block1
  11.     v-if="showVipContent"
  12.     :name="name1"
  13.     @changeName="(value) => (name1 = value)"
  14.   />
  15.   <OpenVipTip v-else />
  16. </template>change name<template>
  17.   <Block1
  18.     v-if="showVipContent"
  19.     :name="name1"
  20.     @changeName="(value) => (name1 = value)"
  21.   />
  22.   <OpenVipTip v-else />
  23. </template><template>
  24.   <Block1
  25.     v-if="showVipContent"
  26.     :name="name1"
  27.     @changeName="(value) => (name1 = value)"
  28.   />
  29.   <OpenVipTip v-else />
  30. </template>这里是block1的一些业务代码<template>
  31.   <Block1
  32.     v-if="showVipContent"
  33.     :name="name1"
  34.     @changeName="(value) => (name1 = value)"
  35.   />
  36.   <OpenVipTip v-else />
  37. </template>  
复制代码
在上面的例子中,子组件有个默认插槽和name为footer的插槽。此时我们来看看高阶组件中如何处理插槽呢?
直接看代码:
  1. import { SetupContext, h } from "vue";import OpenVipTip from "./open-vip-tip.vue";export default function WithVip(BaseComponent: any) {  return {<template>
  2.   <Block1
  3.     v-if="showVipContent"
  4.     :name="name1"
  5.     @changeName="(value) => (name1 = value)"
  6.   />
  7.   <OpenVipTip v-else />
  8. </template>props: BaseComponent.props,<template>
  9.   <Block1
  10.     v-if="showVipContent"
  11.     :name="name1"
  12.     @changeName="(value) => (name1 = value)"
  13.   />
  14.   <OpenVipTip v-else />
  15. </template>setup(props, { attrs, slots, expose }: SetupContext) {<template>
  16.   <Block1
  17.     v-if="showVipContent"
  18.     :name="name1"
  19.     @changeName="(value) => (name1 = value)"
  20.   />
  21.   <OpenVipTip v-else />
  22. </template>  const showVipContent = getShowVipContent();<template>
  23.   <Block1
  24.     v-if="showVipContent"
  25.     :name="name1"
  26.     @changeName="(value) => (name1 = value)"
  27.   />
  28.   <OpenVipTip v-else />
  29. </template>  function getShowVipContent() {<template>
  30.   <Block1
  31.     v-if="showVipContent"
  32.     :name="name1"
  33.     @changeName="(value) => (name1 = value)"
  34.   />
  35.   <OpenVipTip v-else />
  36. </template><template>
  37.   <Block1
  38.     v-if="showVipContent"
  39.     :name="name1"
  40.     @changeName="(value) => (name1 = value)"
  41.   />
  42.   <OpenVipTip v-else />
  43. </template>// 一些业务逻辑判断是否是VIP<template>
  44.   <Block1
  45.     v-if="showVipContent"
  46.     :name="name1"
  47.     @changeName="(value) => (name1 = value)"
  48.   />
  49.   <OpenVipTip v-else />
  50. </template><template>
  51.   <Block1
  52.     v-if="showVipContent"
  53.     :name="name1"
  54.     @changeName="(value) => (name1 = value)"
  55.   />
  56.   <OpenVipTip v-else />
  57. </template>return true;<template>
  58.   <Block1
  59.     v-if="showVipContent"
  60.     :name="name1"
  61.     @changeName="(value) => (name1 = value)"
  62.   />
  63.   <OpenVipTip v-else />
  64. </template>  }<template>
  65.   <Block1
  66.     v-if="showVipContent"
  67.     :name="name1"
  68.     @changeName="(value) => (name1 = value)"
  69.   />
  70.   <OpenVipTip v-else />
  71. </template>  return () => {<template>
  72.   <Block1
  73.     v-if="showVipContent"
  74.     :name="name1"
  75.     @changeName="(value) => (name1 = value)"
  76.   />
  77.   <OpenVipTip v-else />
  78. </template><template>
  79.   <Block1
  80.     v-if="showVipContent"
  81.     :name="name1"
  82.     @changeName="(value) => (name1 = value)"
  83.   />
  84.   <OpenVipTip v-else />
  85. </template>return showVipContent<template>
  86.   <Block1
  87.     v-if="showVipContent"
  88.     :name="name1"
  89.     @changeName="(value) => (name1 = value)"
  90.   />
  91.   <OpenVipTip v-else />
  92. </template><template>
  93.   <Block1
  94.     v-if="showVipContent"
  95.     :name="name1"
  96.     @changeName="(value) => (name1 = value)"
  97.   />
  98.   <OpenVipTip v-else />
  99. </template>  ? h(<template>
  100.   <Block1
  101.     v-if="showVipContent"
  102.     :name="name1"
  103.     @changeName="(value) => (name1 = value)"
  104.   />
  105.   <OpenVipTip v-else />
  106. </template><template>
  107.   <Block1
  108.     v-if="showVipContent"
  109.     :name="name1"
  110.     @changeName="(value) => (name1 = value)"
  111.   />
  112.   <OpenVipTip v-else />
  113. </template><template>
  114.   <Block1
  115.     v-if="showVipContent"
  116.     :name="name1"
  117.     @changeName="(value) => (name1 = value)"
  118.   />
  119.   <OpenVipTip v-else />
  120. </template>  BaseComponent,<template>
  121.   <Block1
  122.     v-if="showVipContent"
  123.     :name="name1"
  124.     @changeName="(value) => (name1 = value)"
  125.   />
  126.   <OpenVipTip v-else />
  127. </template><template>
  128.   <Block1
  129.     v-if="showVipContent"
  130.     :name="name1"
  131.     @changeName="(value) => (name1 = value)"
  132.   />
  133.   <OpenVipTip v-else />
  134. </template><template>
  135.   <Block1
  136.     v-if="showVipContent"
  137.     :name="name1"
  138.     @changeName="(value) => (name1 = value)"
  139.   />
  140.   <OpenVipTip v-else />
  141. </template>  {<template>
  142.   <Block1
  143.     v-if="showVipContent"
  144.     :name="name1"
  145.     @changeName="(value) => (name1 = value)"
  146.   />
  147.   <OpenVipTip v-else />
  148. </template><template>
  149.   <Block1
  150.     v-if="showVipContent"
  151.     :name="name1"
  152.     @changeName="(value) => (name1 = value)"
  153.   />
  154.   <OpenVipTip v-else />
  155. </template><template>
  156.   <Block1
  157.     v-if="showVipContent"
  158.     :name="name1"
  159.     @changeName="(value) => (name1 = value)"
  160.   />
  161.   <OpenVipTip v-else />
  162. </template><template>
  163.   <Block1
  164.     v-if="showVipContent"
  165.     :name="name1"
  166.     @changeName="(value) => (name1 = value)"
  167.   />
  168.   <OpenVipTip v-else />
  169. </template>...props,<template>
  170.   <Block1
  171.     v-if="showVipContent"
  172.     :name="name1"
  173.     @changeName="(value) => (name1 = value)"
  174.   />
  175.   <OpenVipTip v-else />
  176. </template><template>
  177.   <Block1
  178.     v-if="showVipContent"
  179.     :name="name1"
  180.     @changeName="(value) => (name1 = value)"
  181.   />
  182.   <OpenVipTip v-else />
  183. </template><template>
  184.   <Block1
  185.     v-if="showVipContent"
  186.     :name="name1"
  187.     @changeName="(value) => (name1 = value)"
  188.   />
  189.   <OpenVipTip v-else />
  190. </template><template>
  191.   <Block1
  192.     v-if="showVipContent"
  193.     :name="name1"
  194.     @changeName="(value) => (name1 = value)"
  195.   />
  196.   <OpenVipTip v-else />
  197. </template>...attrs,<template>
  198.   <Block1
  199.     v-if="showVipContent"
  200.     :name="name1"
  201.     @changeName="(value) => (name1 = value)"
  202.   />
  203.   <OpenVipTip v-else />
  204. </template><template>
  205.   <Block1
  206.     v-if="showVipContent"
  207.     :name="name1"
  208.     @changeName="(value) => (name1 = value)"
  209.   />
  210.   <OpenVipTip v-else />
  211. </template><template>
  212.   <Block1
  213.     v-if="showVipContent"
  214.     :name="name1"
  215.     @changeName="(value) => (name1 = value)"
  216.   />
  217.   <OpenVipTip v-else />
  218. </template>  },<template>
  219.   <Block1
  220.     v-if="showVipContent"
  221.     :name="name1"
  222.     @changeName="(value) => (name1 = value)"
  223.   />
  224.   <OpenVipTip v-else />
  225. </template><template>
  226.   <Block1
  227.     v-if="showVipContent"
  228.     :name="name1"
  229.     @changeName="(value) => (name1 = value)"
  230.   />
  231.   <OpenVipTip v-else />
  232. </template><template>
  233.   <Block1
  234.     v-if="showVipContent"
  235.     :name="name1"
  236.     @changeName="(value) => (name1 = value)"
  237.   />
  238.   <OpenVipTip v-else />
  239. </template>  slots // 新增代码<template>
  240.   <Block1
  241.     v-if="showVipContent"
  242.     :name="name1"
  243.     @changeName="(value) => (name1 = value)"
  244.   />
  245.   <OpenVipTip v-else />
  246. </template><template>
  247.   <Block1
  248.     v-if="showVipContent"
  249.     :name="name1"
  250.     @changeName="(value) => (name1 = value)"
  251.   />
  252.   <OpenVipTip v-else />
  253. </template><template>
  254.   <Block1
  255.     v-if="showVipContent"
  256.     :name="name1"
  257.     @changeName="(value) => (name1 = value)"
  258.   />
  259.   <OpenVipTip v-else />
  260. </template>)<template>
  261.   <Block1
  262.     v-if="showVipContent"
  263.     :name="name1"
  264.     @changeName="(value) => (name1 = value)"
  265.   />
  266.   <OpenVipTip v-else />
  267. </template><template>
  268.   <Block1
  269.     v-if="showVipContent"
  270.     :name="name1"
  271.     @changeName="(value) => (name1 = value)"
  272.   />
  273.   <OpenVipTip v-else />
  274. </template>  : h(OpenVipTip);<template>
  275.   <Block1
  276.     v-if="showVipContent"
  277.     :name="name1"
  278.     @changeName="(value) => (name1 = value)"
  279.   />
  280.   <OpenVipTip v-else />
  281. </template>  };<template>
  282.   <Block1
  283.     v-if="showVipContent"
  284.     :name="name1"
  285.     @changeName="(value) => (name1 = value)"
  286.   />
  287.   <OpenVipTip v-else />
  288. </template>},  };}
复制代码
插槽的本质就是一个对象里面拥有多个方法,这些方法的名称就是每个具名插槽,每个方法的参数就是插槽传递的变量。这里我们只需要执行h函数时将slots对象传给h函数,就能实现插槽的透传(如果你看不懂这句话,那就等欧阳下篇插槽的文章写好后再来看这段话你就懂了)。
我们在控制台中来看看传入的slots插槽对象,如下图:
2.png

从上面可以看到插槽对象中有两个方法,分别是default和footer,对应的就是默认插槽和footer插槽。
大家熟知h函数接收的第三个参数是children数组,也就是有哪些子元素。但是他其实还支持直接传入slots对象,下面这个是他的一种定义:
  1. export function h<P>(
  2.   type: Component<P>,
  3.   props?: (RawProps & P) | null,
  4.   children?: RawChildren | RawSlots,
  5. ): VNode
  6. export type RawSlots = {
  7.   [name: string]: unknown
  8.   // ...省略
  9. }
复制代码
所以我们可以直接把slots对象直接丢给h函数,就可以实现插槽的透传。
父组件调用子组件的方法

有的场景中我们需要在父组件中直接调用子组件的方法,按照以前的场景,我们只需要在子组件中expose暴露出去方法,然后在父组件中使用ref访问到子组件,这样就可以调用了。
但是使用了HOC后,中间层多了一个高阶组件,所以我们不能直接访问到子组件expose的方法。
怎么做呢?答案很简单,直接上代码:
  1. import { SetupContext, h, ref } from "vue";import OpenVipTip from "./open-vip-tip.vue";export default function WithVip(BaseComponent: any) {  return {<template>
  2.   <Block1
  3.     v-if="showVipContent"
  4.     :name="name1"
  5.     @changeName="(value) => (name1 = value)"
  6.   />
  7.   <OpenVipTip v-else />
  8. </template>props: BaseComponent.props,<template>
  9.   <Block1
  10.     v-if="showVipContent"
  11.     :name="name1"
  12.     @changeName="(value) => (name1 = value)"
  13.   />
  14.   <OpenVipTip v-else />
  15. </template>setup(props, { attrs, slots, expose }: SetupContext) {<template>
  16.   <Block1
  17.     v-if="showVipContent"
  18.     :name="name1"
  19.     @changeName="(value) => (name1 = value)"
  20.   />
  21.   <OpenVipTip v-else />
  22. </template>  const showVipContent = getShowVipContent();<template>
  23.   <Block1
  24.     v-if="showVipContent"
  25.     :name="name1"
  26.     @changeName="(value) => (name1 = value)"
  27.   />
  28.   <OpenVipTip v-else />
  29. </template>  function getShowVipContent() {<template>
  30.   <Block1
  31.     v-if="showVipContent"
  32.     :name="name1"
  33.     @changeName="(value) => (name1 = value)"
  34.   />
  35.   <OpenVipTip v-else />
  36. </template><template>
  37.   <Block1
  38.     v-if="showVipContent"
  39.     :name="name1"
  40.     @changeName="(value) => (name1 = value)"
  41.   />
  42.   <OpenVipTip v-else />
  43. </template>// 一些业务逻辑判断是否是VIP<template>
  44.   <Block1
  45.     v-if="showVipContent"
  46.     :name="name1"
  47.     @changeName="(value) => (name1 = value)"
  48.   />
  49.   <OpenVipTip v-else />
  50. </template><template>
  51.   <Block1
  52.     v-if="showVipContent"
  53.     :name="name1"
  54.     @changeName="(value) => (name1 = value)"
  55.   />
  56.   <OpenVipTip v-else />
  57. </template>return true;<template>
  58.   <Block1
  59.     v-if="showVipContent"
  60.     :name="name1"
  61.     @changeName="(value) => (name1 = value)"
  62.   />
  63.   <OpenVipTip v-else />
  64. </template>  }<template>
  65.   <Block1
  66.     v-if="showVipContent"
  67.     :name="name1"
  68.     @changeName="(value) => (name1 = value)"
  69.   />
  70.   <OpenVipTip v-else />
  71. </template>  // 新增代码start<template>
  72.   <Block1
  73.     v-if="showVipContent"
  74.     :name="name1"
  75.     @changeName="(value) => (name1 = value)"
  76.   />
  77.   <OpenVipTip v-else />
  78. </template>  const innerRef = ref();<template>
  79.   <Block1
  80.     v-if="showVipContent"
  81.     :name="name1"
  82.     @changeName="(value) => (name1 = value)"
  83.   />
  84.   <OpenVipTip v-else />
  85. </template>  expose(<template>
  86.   <Block1
  87.     v-if="showVipContent"
  88.     :name="name1"
  89.     @changeName="(value) => (name1 = value)"
  90.   />
  91.   <OpenVipTip v-else />
  92. </template><template>
  93.   <Block1
  94.     v-if="showVipContent"
  95.     :name="name1"
  96.     @changeName="(value) => (name1 = value)"
  97.   />
  98.   <OpenVipTip v-else />
  99. </template>new Proxy(<template>
  100.   <Block1
  101.     v-if="showVipContent"
  102.     :name="name1"
  103.     @changeName="(value) => (name1 = value)"
  104.   />
  105.   <OpenVipTip v-else />
  106. </template><template>
  107.   <Block1
  108.     v-if="showVipContent"
  109.     :name="name1"
  110.     @changeName="(value) => (name1 = value)"
  111.   />
  112.   <OpenVipTip v-else />
  113. </template>  {},<template>
  114.   <Block1
  115.     v-if="showVipContent"
  116.     :name="name1"
  117.     @changeName="(value) => (name1 = value)"
  118.   />
  119.   <OpenVipTip v-else />
  120. </template><template>
  121.   <Block1
  122.     v-if="showVipContent"
  123.     :name="name1"
  124.     @changeName="(value) => (name1 = value)"
  125.   />
  126.   <OpenVipTip v-else />
  127. </template>  {<template>
  128.   <Block1
  129.     v-if="showVipContent"
  130.     :name="name1"
  131.     @changeName="(value) => (name1 = value)"
  132.   />
  133.   <OpenVipTip v-else />
  134. </template><template>
  135.   <Block1
  136.     v-if="showVipContent"
  137.     :name="name1"
  138.     @changeName="(value) => (name1 = value)"
  139.   />
  140.   <OpenVipTip v-else />
  141. </template><template>
  142.   <Block1
  143.     v-if="showVipContent"
  144.     :name="name1"
  145.     @changeName="(value) => (name1 = value)"
  146.   />
  147.   <OpenVipTip v-else />
  148. </template>get(_target, key) {<template>
  149.   <Block1
  150.     v-if="showVipContent"
  151.     :name="name1"
  152.     @changeName="(value) => (name1 = value)"
  153.   />
  154.   <OpenVipTip v-else />
  155. </template><template>
  156.   <Block1
  157.     v-if="showVipContent"
  158.     :name="name1"
  159.     @changeName="(value) => (name1 = value)"
  160.   />
  161.   <OpenVipTip v-else />
  162. </template><template>
  163.   <Block1
  164.     v-if="showVipContent"
  165.     :name="name1"
  166.     @changeName="(value) => (name1 = value)"
  167.   />
  168.   <OpenVipTip v-else />
  169. </template>  return innerRef.value?.[key];<template>
  170.   <Block1
  171.     v-if="showVipContent"
  172.     :name="name1"
  173.     @changeName="(value) => (name1 = value)"
  174.   />
  175.   <OpenVipTip v-else />
  176. </template><template>
  177.   <Block1
  178.     v-if="showVipContent"
  179.     :name="name1"
  180.     @changeName="(value) => (name1 = value)"
  181.   />
  182.   <OpenVipTip v-else />
  183. </template><template>
  184.   <Block1
  185.     v-if="showVipContent"
  186.     :name="name1"
  187.     @changeName="(value) => (name1 = value)"
  188.   />
  189.   <OpenVipTip v-else />
  190. </template>},<template>
  191.   <Block1
  192.     v-if="showVipContent"
  193.     :name="name1"
  194.     @changeName="(value) => (name1 = value)"
  195.   />
  196.   <OpenVipTip v-else />
  197. </template><template>
  198.   <Block1
  199.     v-if="showVipContent"
  200.     :name="name1"
  201.     @changeName="(value) => (name1 = value)"
  202.   />
  203.   <OpenVipTip v-else />
  204. </template><template>
  205.   <Block1
  206.     v-if="showVipContent"
  207.     :name="name1"
  208.     @changeName="(value) => (name1 = value)"
  209.   />
  210.   <OpenVipTip v-else />
  211. </template>has(_target, key) {<template>
  212.   <Block1
  213.     v-if="showVipContent"
  214.     :name="name1"
  215.     @changeName="(value) => (name1 = value)"
  216.   />
  217.   <OpenVipTip v-else />
  218. </template><template>
  219.   <Block1
  220.     v-if="showVipContent"
  221.     :name="name1"
  222.     @changeName="(value) => (name1 = value)"
  223.   />
  224.   <OpenVipTip v-else />
  225. </template><template>
  226.   <Block1
  227.     v-if="showVipContent"
  228.     :name="name1"
  229.     @changeName="(value) => (name1 = value)"
  230.   />
  231.   <OpenVipTip v-else />
  232. </template>  return innerRef.value?.[key];<template>
  233.   <Block1
  234.     v-if="showVipContent"
  235.     :name="name1"
  236.     @changeName="(value) => (name1 = value)"
  237.   />
  238.   <OpenVipTip v-else />
  239. </template><template>
  240.   <Block1
  241.     v-if="showVipContent"
  242.     :name="name1"
  243.     @changeName="(value) => (name1 = value)"
  244.   />
  245.   <OpenVipTip v-else />
  246. </template><template>
  247.   <Block1
  248.     v-if="showVipContent"
  249.     :name="name1"
  250.     @changeName="(value) => (name1 = value)"
  251.   />
  252.   <OpenVipTip v-else />
  253. </template>},<template>
  254.   <Block1
  255.     v-if="showVipContent"
  256.     :name="name1"
  257.     @changeName="(value) => (name1 = value)"
  258.   />
  259.   <OpenVipTip v-else />
  260. </template><template>
  261.   <Block1
  262.     v-if="showVipContent"
  263.     :name="name1"
  264.     @changeName="(value) => (name1 = value)"
  265.   />
  266.   <OpenVipTip v-else />
  267. </template>  }<template>
  268.   <Block1
  269.     v-if="showVipContent"
  270.     :name="name1"
  271.     @changeName="(value) => (name1 = value)"
  272.   />
  273.   <OpenVipTip v-else />
  274. </template><template>
  275.   <Block1
  276.     v-if="showVipContent"
  277.     :name="name1"
  278.     @changeName="(value) => (name1 = value)"
  279.   />
  280.   <OpenVipTip v-else />
  281. </template>)<template>
  282.   <Block1
  283.     v-if="showVipContent"
  284.     :name="name1"
  285.     @changeName="(value) => (name1 = value)"
  286.   />
  287.   <OpenVipTip v-else />
  288. </template>  );<template>
  289.   <Block1
  290.     v-if="showVipContent"
  291.     :name="name1"
  292.     @changeName="(value) => (name1 = value)"
  293.   />
  294.   <OpenVipTip v-else />
  295. </template>  // 新增代码end<template>
  296.   <Block1
  297.     v-if="showVipContent"
  298.     :name="name1"
  299.     @changeName="(value) => (name1 = value)"
  300.   />
  301.   <OpenVipTip v-else />
  302. </template>  return () => {<template>
  303.   <Block1
  304.     v-if="showVipContent"
  305.     :name="name1"
  306.     @changeName="(value) => (name1 = value)"
  307.   />
  308.   <OpenVipTip v-else />
  309. </template><template>
  310.   <Block1
  311.     v-if="showVipContent"
  312.     :name="name1"
  313.     @changeName="(value) => (name1 = value)"
  314.   />
  315.   <OpenVipTip v-else />
  316. </template>return showVipContent<template>
  317.   <Block1
  318.     v-if="showVipContent"
  319.     :name="name1"
  320.     @changeName="(value) => (name1 = value)"
  321.   />
  322.   <OpenVipTip v-else />
  323. </template><template>
  324.   <Block1
  325.     v-if="showVipContent"
  326.     :name="name1"
  327.     @changeName="(value) => (name1 = value)"
  328.   />
  329.   <OpenVipTip v-else />
  330. </template>  ? h(<template>
  331.   <Block1
  332.     v-if="showVipContent"
  333.     :name="name1"
  334.     @changeName="(value) => (name1 = value)"
  335.   />
  336.   <OpenVipTip v-else />
  337. </template><template>
  338.   <Block1
  339.     v-if="showVipContent"
  340.     :name="name1"
  341.     @changeName="(value) => (name1 = value)"
  342.   />
  343.   <OpenVipTip v-else />
  344. </template><template>
  345.   <Block1
  346.     v-if="showVipContent"
  347.     :name="name1"
  348.     @changeName="(value) => (name1 = value)"
  349.   />
  350.   <OpenVipTip v-else />
  351. </template>  BaseComponent,<template>
  352.   <Block1
  353.     v-if="showVipContent"
  354.     :name="name1"
  355.     @changeName="(value) => (name1 = value)"
  356.   />
  357.   <OpenVipTip v-else />
  358. </template><template>
  359.   <Block1
  360.     v-if="showVipContent"
  361.     :name="name1"
  362.     @changeName="(value) => (name1 = value)"
  363.   />
  364.   <OpenVipTip v-else />
  365. </template><template>
  366.   <Block1
  367.     v-if="showVipContent"
  368.     :name="name1"
  369.     @changeName="(value) => (name1 = value)"
  370.   />
  371.   <OpenVipTip v-else />
  372. </template>  {<template>
  373.   <Block1
  374.     v-if="showVipContent"
  375.     :name="name1"
  376.     @changeName="(value) => (name1 = value)"
  377.   />
  378.   <OpenVipTip v-else />
  379. </template><template>
  380.   <Block1
  381.     v-if="showVipContent"
  382.     :name="name1"
  383.     @changeName="(value) => (name1 = value)"
  384.   />
  385.   <OpenVipTip v-else />
  386. </template><template>
  387.   <Block1
  388.     v-if="showVipContent"
  389.     :name="name1"
  390.     @changeName="(value) => (name1 = value)"
  391.   />
  392.   <OpenVipTip v-else />
  393. </template><template>
  394.   <Block1
  395.     v-if="showVipContent"
  396.     :name="name1"
  397.     @changeName="(value) => (name1 = value)"
  398.   />
  399.   <OpenVipTip v-else />
  400. </template>...props,<template>
  401.   <Block1
  402.     v-if="showVipContent"
  403.     :name="name1"
  404.     @changeName="(value) => (name1 = value)"
  405.   />
  406.   <OpenVipTip v-else />
  407. </template><template>
  408.   <Block1
  409.     v-if="showVipContent"
  410.     :name="name1"
  411.     @changeName="(value) => (name1 = value)"
  412.   />
  413.   <OpenVipTip v-else />
  414. </template><template>
  415.   <Block1
  416.     v-if="showVipContent"
  417.     :name="name1"
  418.     @changeName="(value) => (name1 = value)"
  419.   />
  420.   <OpenVipTip v-else />
  421. </template><template>
  422.   <Block1
  423.     v-if="showVipContent"
  424.     :name="name1"
  425.     @changeName="(value) => (name1 = value)"
  426.   />
  427.   <OpenVipTip v-else />
  428. </template>...attrs,<template>
  429.   <Block1
  430.     v-if="showVipContent"
  431.     :name="name1"
  432.     @changeName="(value) => (name1 = value)"
  433.   />
  434.   <OpenVipTip v-else />
  435. </template><template>
  436.   <Block1
  437.     v-if="showVipContent"
  438.     :name="name1"
  439.     @changeName="(value) => (name1 = value)"
  440.   />
  441.   <OpenVipTip v-else />
  442. </template><template>
  443.   <Block1
  444.     v-if="showVipContent"
  445.     :name="name1"
  446.     @changeName="(value) => (name1 = value)"
  447.   />
  448.   <OpenVipTip v-else />
  449. </template><template>
  450.   <Block1
  451.     v-if="showVipContent"
  452.     :name="name1"
  453.     @changeName="(value) => (name1 = value)"
  454.   />
  455.   <OpenVipTip v-else />
  456. </template>ref: innerRef,  // 新增代码<template>
  457.   <Block1
  458.     v-if="showVipContent"
  459.     :name="name1"
  460.     @changeName="(value) => (name1 = value)"
  461.   />
  462.   <OpenVipTip v-else />
  463. </template><template>
  464.   <Block1
  465.     v-if="showVipContent"
  466.     :name="name1"
  467.     @changeName="(value) => (name1 = value)"
  468.   />
  469.   <OpenVipTip v-else />
  470. </template><template>
  471.   <Block1
  472.     v-if="showVipContent"
  473.     :name="name1"
  474.     @changeName="(value) => (name1 = value)"
  475.   />
  476.   <OpenVipTip v-else />
  477. </template>  },<template>
  478.   <Block1
  479.     v-if="showVipContent"
  480.     :name="name1"
  481.     @changeName="(value) => (name1 = value)"
  482.   />
  483.   <OpenVipTip v-else />
  484. </template><template>
  485.   <Block1
  486.     v-if="showVipContent"
  487.     :name="name1"
  488.     @changeName="(value) => (name1 = value)"
  489.   />
  490.   <OpenVipTip v-else />
  491. </template><template>
  492.   <Block1
  493.     v-if="showVipContent"
  494.     :name="name1"
  495.     @changeName="(value) => (name1 = value)"
  496.   />
  497.   <OpenVipTip v-else />
  498. </template>  slots<template>
  499.   <Block1
  500.     v-if="showVipContent"
  501.     :name="name1"
  502.     @changeName="(value) => (name1 = value)"
  503.   />
  504.   <OpenVipTip v-else />
  505. </template><template>
  506.   <Block1
  507.     v-if="showVipContent"
  508.     :name="name1"
  509.     @changeName="(value) => (name1 = value)"
  510.   />
  511.   <OpenVipTip v-else />
  512. </template><template>
  513.   <Block1
  514.     v-if="showVipContent"
  515.     :name="name1"
  516.     @changeName="(value) => (name1 = value)"
  517.   />
  518.   <OpenVipTip v-else />
  519. </template>)<template>
  520.   <Block1
  521.     v-if="showVipContent"
  522.     :name="name1"
  523.     @changeName="(value) => (name1 = value)"
  524.   />
  525.   <OpenVipTip v-else />
  526. </template><template>
  527.   <Block1
  528.     v-if="showVipContent"
  529.     :name="name1"
  530.     @changeName="(value) => (name1 = value)"
  531.   />
  532.   <OpenVipTip v-else />
  533. </template>  : h(OpenVipTip);<template>
  534.   <Block1
  535.     v-if="showVipContent"
  536.     :name="name1"
  537.     @changeName="(value) => (name1 = value)"
  538.   />
  539.   <OpenVipTip v-else />
  540. </template>  };<template>
  541.   <Block1
  542.     v-if="showVipContent"
  543.     :name="name1"
  544.     @changeName="(value) => (name1 = value)"
  545.   />
  546.   <OpenVipTip v-else />
  547. </template>},  };}
复制代码
在高阶组件中使用ref访问到子组件赋值给innerRef变量。然后expose一个Proxy的对象,在get拦截中让其直接去执行子组件中的对应的方法。
比如在父组件中使用block1Ref.value.handleClick()去调用handleClick方法,由于使用了HOC,所以这里读取的handleClick方法其实是读取的是HOC中expose暴露的方法。所以就会走到Proxy的get拦截中,从而可以访问到真正子组件中expose暴露的handleClick方法。
那么上面的Proxy为什么要使用has拦截呢?
答案是在Vue源码中父组件在执行子组件中暴露的方法之前会执行这样一个判断:
  1. if (key in target) {
  2.   return target[key];
  3. }
复制代码
很明显我们这里的Proxy代理的原始对象里面什么都没有,执行key in target肯定就是false了。所以我们可以使用has去拦截key in target,意思是只要访问的方法或者属性是子组件中expose暴露的就返回true。
至此,我们已经在HOC中覆盖了Vue中的所有场景。但是有的同学觉得h函数写着比较麻烦,不好维护,我们还可以将上面的高阶组件改为tsx的写法,with-vip.tsx文件代码如下:
  1. import { SetupContext, ref } from "vue";import OpenVipTip from "./open-vip-tip.vue";export default function WithVip(BaseComponent: any) {  return {<template>
  2.   <Block1
  3.     v-if="showVipContent"
  4.     :name="name1"
  5.     @changeName="(value) => (name1 = value)"
  6.   />
  7.   <OpenVipTip v-else />
  8. </template>props: BaseComponent.props,<template>
  9.   <Block1
  10.     v-if="showVipContent"
  11.     :name="name1"
  12.     @changeName="(value) => (name1 = value)"
  13.   />
  14.   <OpenVipTip v-else />
  15. </template>setup(props, { attrs, slots, expose }: SetupContext) {<template>
  16.   <Block1
  17.     v-if="showVipContent"
  18.     :name="name1"
  19.     @changeName="(value) => (name1 = value)"
  20.   />
  21.   <OpenVipTip v-else />
  22. </template>  const showVipContent = getShowVipContent();<template>
  23.   <Block1
  24.     v-if="showVipContent"
  25.     :name="name1"
  26.     @changeName="(value) => (name1 = value)"
  27.   />
  28.   <OpenVipTip v-else />
  29. </template>  function getShowVipContent() {<template>
  30.   <Block1
  31.     v-if="showVipContent"
  32.     :name="name1"
  33.     @changeName="(value) => (name1 = value)"
  34.   />
  35.   <OpenVipTip v-else />
  36. </template><template>
  37.   <Block1
  38.     v-if="showVipContent"
  39.     :name="name1"
  40.     @changeName="(value) => (name1 = value)"
  41.   />
  42.   <OpenVipTip v-else />
  43. </template>// 一些业务逻辑判断是否是VIP<template>
  44.   <Block1
  45.     v-if="showVipContent"
  46.     :name="name1"
  47.     @changeName="(value) => (name1 = value)"
  48.   />
  49.   <OpenVipTip v-else />
  50. </template><template>
  51.   <Block1
  52.     v-if="showVipContent"
  53.     :name="name1"
  54.     @changeName="(value) => (name1 = value)"
  55.   />
  56.   <OpenVipTip v-else />
  57. </template>return true;<template>
  58.   <Block1
  59.     v-if="showVipContent"
  60.     :name="name1"
  61.     @changeName="(value) => (name1 = value)"
  62.   />
  63.   <OpenVipTip v-else />
  64. </template>  }<template>
  65.   <Block1
  66.     v-if="showVipContent"
  67.     :name="name1"
  68.     @changeName="(value) => (name1 = value)"
  69.   />
  70.   <OpenVipTip v-else />
  71. </template>  const innerRef = ref();<template>
  72.   <Block1
  73.     v-if="showVipContent"
  74.     :name="name1"
  75.     @changeName="(value) => (name1 = value)"
  76.   />
  77.   <OpenVipTip v-else />
  78. </template>  expose(<template>
  79.   <Block1
  80.     v-if="showVipContent"
  81.     :name="name1"
  82.     @changeName="(value) => (name1 = value)"
  83.   />
  84.   <OpenVipTip v-else />
  85. </template><template>
  86.   <Block1
  87.     v-if="showVipContent"
  88.     :name="name1"
  89.     @changeName="(value) => (name1 = value)"
  90.   />
  91.   <OpenVipTip v-else />
  92. </template>new Proxy(<template>
  93.   <Block1
  94.     v-if="showVipContent"
  95.     :name="name1"
  96.     @changeName="(value) => (name1 = value)"
  97.   />
  98.   <OpenVipTip v-else />
  99. </template><template>
  100.   <Block1
  101.     v-if="showVipContent"
  102.     :name="name1"
  103.     @changeName="(value) => (name1 = value)"
  104.   />
  105.   <OpenVipTip v-else />
  106. </template>  {},<template>
  107.   <Block1
  108.     v-if="showVipContent"
  109.     :name="name1"
  110.     @changeName="(value) => (name1 = value)"
  111.   />
  112.   <OpenVipTip v-else />
  113. </template><template>
  114.   <Block1
  115.     v-if="showVipContent"
  116.     :name="name1"
  117.     @changeName="(value) => (name1 = value)"
  118.   />
  119.   <OpenVipTip v-else />
  120. </template>  {<template>
  121.   <Block1
  122.     v-if="showVipContent"
  123.     :name="name1"
  124.     @changeName="(value) => (name1 = value)"
  125.   />
  126.   <OpenVipTip v-else />
  127. </template><template>
  128.   <Block1
  129.     v-if="showVipContent"
  130.     :name="name1"
  131.     @changeName="(value) => (name1 = value)"
  132.   />
  133.   <OpenVipTip v-else />
  134. </template><template>
  135.   <Block1
  136.     v-if="showVipContent"
  137.     :name="name1"
  138.     @changeName="(value) => (name1 = value)"
  139.   />
  140.   <OpenVipTip v-else />
  141. </template>get(_target, key) {<template>
  142.   <Block1
  143.     v-if="showVipContent"
  144.     :name="name1"
  145.     @changeName="(value) => (name1 = value)"
  146.   />
  147.   <OpenVipTip v-else />
  148. </template><template>
  149.   <Block1
  150.     v-if="showVipContent"
  151.     :name="name1"
  152.     @changeName="(value) => (name1 = value)"
  153.   />
  154.   <OpenVipTip v-else />
  155. </template><template>
  156.   <Block1
  157.     v-if="showVipContent"
  158.     :name="name1"
  159.     @changeName="(value) => (name1 = value)"
  160.   />
  161.   <OpenVipTip v-else />
  162. </template>  return innerRef.value?.[key];<template>
  163.   <Block1
  164.     v-if="showVipContent"
  165.     :name="name1"
  166.     @changeName="(value) => (name1 = value)"
  167.   />
  168.   <OpenVipTip v-else />
  169. </template><template>
  170.   <Block1
  171.     v-if="showVipContent"
  172.     :name="name1"
  173.     @changeName="(value) => (name1 = value)"
  174.   />
  175.   <OpenVipTip v-else />
  176. </template><template>
  177.   <Block1
  178.     v-if="showVipContent"
  179.     :name="name1"
  180.     @changeName="(value) => (name1 = value)"
  181.   />
  182.   <OpenVipTip v-else />
  183. </template>},<template>
  184.   <Block1
  185.     v-if="showVipContent"
  186.     :name="name1"
  187.     @changeName="(value) => (name1 = value)"
  188.   />
  189.   <OpenVipTip v-else />
  190. </template><template>
  191.   <Block1
  192.     v-if="showVipContent"
  193.     :name="name1"
  194.     @changeName="(value) => (name1 = value)"
  195.   />
  196.   <OpenVipTip v-else />
  197. </template><template>
  198.   <Block1
  199.     v-if="showVipContent"
  200.     :name="name1"
  201.     @changeName="(value) => (name1 = value)"
  202.   />
  203.   <OpenVipTip v-else />
  204. </template>has(_target, key) {<template>
  205.   <Block1
  206.     v-if="showVipContent"
  207.     :name="name1"
  208.     @changeName="(value) => (name1 = value)"
  209.   />
  210.   <OpenVipTip v-else />
  211. </template><template>
  212.   <Block1
  213.     v-if="showVipContent"
  214.     :name="name1"
  215.     @changeName="(value) => (name1 = value)"
  216.   />
  217.   <OpenVipTip v-else />
  218. </template><template>
  219.   <Block1
  220.     v-if="showVipContent"
  221.     :name="name1"
  222.     @changeName="(value) => (name1 = value)"
  223.   />
  224.   <OpenVipTip v-else />
  225. </template>  return innerRef.value?.[key];<template>
  226.   <Block1
  227.     v-if="showVipContent"
  228.     :name="name1"
  229.     @changeName="(value) => (name1 = value)"
  230.   />
  231.   <OpenVipTip v-else />
  232. </template><template>
  233.   <Block1
  234.     v-if="showVipContent"
  235.     :name="name1"
  236.     @changeName="(value) => (name1 = value)"
  237.   />
  238.   <OpenVipTip v-else />
  239. </template><template>
  240.   <Block1
  241.     v-if="showVipContent"
  242.     :name="name1"
  243.     @changeName="(value) => (name1 = value)"
  244.   />
  245.   <OpenVipTip v-else />
  246. </template>},<template>
  247.   <Block1
  248.     v-if="showVipContent"
  249.     :name="name1"
  250.     @changeName="(value) => (name1 = value)"
  251.   />
  252.   <OpenVipTip v-else />
  253. </template><template>
  254.   <Block1
  255.     v-if="showVipContent"
  256.     :name="name1"
  257.     @changeName="(value) => (name1 = value)"
  258.   />
  259.   <OpenVipTip v-else />
  260. </template>  }<template>
  261.   <Block1
  262.     v-if="showVipContent"
  263.     :name="name1"
  264.     @changeName="(value) => (name1 = value)"
  265.   />
  266.   <OpenVipTip v-else />
  267. </template><template>
  268.   <Block1
  269.     v-if="showVipContent"
  270.     :name="name1"
  271.     @changeName="(value) => (name1 = value)"
  272.   />
  273.   <OpenVipTip v-else />
  274. </template>)<template>
  275.   <Block1
  276.     v-if="showVipContent"
  277.     :name="name1"
  278.     @changeName="(value) => (name1 = value)"
  279.   />
  280.   <OpenVipTip v-else />
  281. </template>  );<template>
  282.   <Block1
  283.     v-if="showVipContent"
  284.     :name="name1"
  285.     @changeName="(value) => (name1 = value)"
  286.   />
  287.   <OpenVipTip v-else />
  288. </template>  return () => {<template>
  289.   <Block1
  290.     v-if="showVipContent"
  291.     :name="name1"
  292.     @changeName="(value) => (name1 = value)"
  293.   />
  294.   <OpenVipTip v-else />
  295. </template><template>
  296.   <Block1
  297.     v-if="showVipContent"
  298.     :name="name1"
  299.     @changeName="(value) => (name1 = value)"
  300.   />
  301.   <OpenVipTip v-else />
  302. </template>return showVipContent ? (<template>
  303.   <Block1
  304.     v-if="showVipContent"
  305.     :name="name1"
  306.     @changeName="(value) => (name1 = value)"
  307.   />
  308.   <OpenVipTip v-else />
  309. </template><template>
  310.   <Block1
  311.     v-if="showVipContent"
  312.     :name="name1"
  313.     @changeName="(value) => (name1 = value)"
  314.   />
  315.   <OpenVipTip v-else />
  316. </template><template>
  317.   <Block1
  318.     v-if="showVipContent"
  319.     :name="name1"
  320.     @changeName="(value) => (name1 = value)"
  321.   />
  322.   <OpenVipTip v-else />
  323. </template><template>
  324.   <Block1
  325.     v-if="showVipContent"
  326.     :name="name1"
  327.     @changeName="(value) => (name1 = value)"
  328.   />
  329.   <OpenVipTip v-else />
  330. </template><template>
  331.   <Block1
  332.     v-if="showVipContent"
  333.     :name="name1"
  334.     @changeName="(value) => (name1 = value)"
  335.   />
  336.   <OpenVipTip v-else />
  337. </template>  {slots}<template>
  338.   <Block1
  339.     v-if="showVipContent"
  340.     :name="name1"
  341.     @changeName="(value) => (name1 = value)"
  342.   />
  343.   <OpenVipTip v-else />
  344. </template><template>
  345.   <Block1
  346.     v-if="showVipContent"
  347.     :name="name1"
  348.     @changeName="(value) => (name1 = value)"
  349.   />
  350.   <OpenVipTip v-else />
  351. </template><template>
  352.   <Block1
  353.     v-if="showVipContent"
  354.     :name="name1"
  355.     @changeName="(value) => (name1 = value)"
  356.   />
  357.   <OpenVipTip v-else />
  358. </template><template>
  359.   <Block1
  360.     v-if="showVipContent"
  361.     :name="name1"
  362.     @changeName="(value) => (name1 = value)"
  363.   />
  364.   <OpenVipTip v-else />
  365. </template>  ) : (<template>
  366.   <Block1
  367.     v-if="showVipContent"
  368.     :name="name1"
  369.     @changeName="(value) => (name1 = value)"
  370.   />
  371.   <OpenVipTip v-else />
  372. </template><template>
  373.   <Block1
  374.     v-if="showVipContent"
  375.     :name="name1"
  376.     @changeName="(value) => (name1 = value)"
  377.   />
  378.   <OpenVipTip v-else />
  379. </template><template>
  380.   <Block1
  381.     v-if="showVipContent"
  382.     :name="name1"
  383.     @changeName="(value) => (name1 = value)"
  384.   />
  385.   <OpenVipTip v-else />
  386. </template><template>
  387.   <Block1
  388.     v-if="showVipContent"
  389.     :name="name1"
  390.     @changeName="(value) => (name1 = value)"
  391.   />
  392.   <OpenVipTip v-else />
  393. </template>  );<template>
  394.   <Block1
  395.     v-if="showVipContent"
  396.     :name="name1"
  397.     @changeName="(value) => (name1 = value)"
  398.   />
  399.   <OpenVipTip v-else />
  400. </template>  };<template>
  401.   <Block1
  402.     v-if="showVipContent"
  403.     :name="name1"
  404.     @changeName="(value) => (name1 = value)"
  405.   />
  406.   <OpenVipTip v-else />
  407. </template>},  };}
复制代码
一般情况下h函数能够实现的,使用jsx或者tsx都能实现(除非你需要操作虚拟DOM)。
注意上面的代码是使用ref={innerRef},而不是我们熟悉的ref="innerRef",这里很容易搞错!!
compose函数

此时你可能有个新需求,需要给某些模块显示不同的折扣信息,这些模块可能会和上一个会员需求的模块有重叠。此时就涉及到多个高阶组件之间的组合情况。
同样我们使用HOC去实现,新增一个WithDiscount高阶组件,代码如下:
  1. import { SetupContext, onMounted, ref } from "vue";export default function WithDiscount(BaseComponent: any, item: string) {  return {<template>
  2.   <Block1
  3.     v-if="showVipContent"
  4.     :name="name1"
  5.     @changeName="(value) => (name1 = value)"
  6.   />
  7.   <OpenVipTip v-else />
  8. </template>props: BaseComponent.props,<template>
  9.   <Block1
  10.     v-if="showVipContent"
  11.     :name="name1"
  12.     @changeName="(value) => (name1 = value)"
  13.   />
  14.   <OpenVipTip v-else />
  15. </template>setup(props, { attrs, slots, expose }: SetupContext) {<template>
  16.   <Block1
  17.     v-if="showVipContent"
  18.     :name="name1"
  19.     @changeName="(value) => (name1 = value)"
  20.   />
  21.   <OpenVipTip v-else />
  22. </template>  const discountInfo = ref("");<template>
  23.   <Block1
  24.     v-if="showVipContent"
  25.     :name="name1"
  26.     @changeName="(value) => (name1 = value)"
  27.   />
  28.   <OpenVipTip v-else />
  29. </template>  onMounted(async () => {<template>
  30.   <Block1
  31.     v-if="showVipContent"
  32.     :name="name1"
  33.     @changeName="(value) => (name1 = value)"
  34.   />
  35.   <OpenVipTip v-else />
  36. </template><template>
  37.   <Block1
  38.     v-if="showVipContent"
  39.     :name="name1"
  40.     @changeName="(value) => (name1 = value)"
  41.   />
  42.   <OpenVipTip v-else />
  43. </template>const res = await getDiscountInfo(item);<template>
  44.   <Block1
  45.     v-if="showVipContent"
  46.     :name="name1"
  47.     @changeName="(value) => (name1 = value)"
  48.   />
  49.   <OpenVipTip v-else />
  50. </template><template>
  51.   <Block1
  52.     v-if="showVipContent"
  53.     :name="name1"
  54.     @changeName="(value) => (name1 = value)"
  55.   />
  56.   <OpenVipTip v-else />
  57. </template>discountInfo.value = res;<template>
  58.   <Block1
  59.     v-if="showVipContent"
  60.     :name="name1"
  61.     @changeName="(value) => (name1 = value)"
  62.   />
  63.   <OpenVipTip v-else />
  64. </template>  });<template>
  65.   <Block1
  66.     v-if="showVipContent"
  67.     :name="name1"
  68.     @changeName="(value) => (name1 = value)"
  69.   />
  70.   <OpenVipTip v-else />
  71. </template>  function getDiscountInfo(item: any): Promise {<template>
  72.   <Block1
  73.     v-if="showVipContent"
  74.     :name="name1"
  75.     @changeName="(value) => (name1 = value)"
  76.   />
  77.   <OpenVipTip v-else />
  78. </template><template>
  79.   <Block1
  80.     v-if="showVipContent"
  81.     :name="name1"
  82.     @changeName="(value) => (name1 = value)"
  83.   />
  84.   <OpenVipTip v-else />
  85. </template>// 根据传入的item获取折扣信息<template>
  86.   <Block1
  87.     v-if="showVipContent"
  88.     :name="name1"
  89.     @changeName="(value) => (name1 = value)"
  90.   />
  91.   <OpenVipTip v-else />
  92. </template><template>
  93.   <Block1
  94.     v-if="showVipContent"
  95.     :name="name1"
  96.     @changeName="(value) => (name1 = value)"
  97.   />
  98.   <OpenVipTip v-else />
  99. </template>return new Promise((resolve) => {<template>
  100.   <Block1
  101.     v-if="showVipContent"
  102.     :name="name1"
  103.     @changeName="(value) => (name1 = value)"
  104.   />
  105.   <OpenVipTip v-else />
  106. </template><template>
  107.   <Block1
  108.     v-if="showVipContent"
  109.     :name="name1"
  110.     @changeName="(value) => (name1 = value)"
  111.   />
  112.   <OpenVipTip v-else />
  113. </template>  setTimeout(() => {<template>
  114.   <Block1
  115.     v-if="showVipContent"
  116.     :name="name1"
  117.     @changeName="(value) => (name1 = value)"
  118.   />
  119.   <OpenVipTip v-else />
  120. </template><template>
  121.   <Block1
  122.     v-if="showVipContent"
  123.     :name="name1"
  124.     @changeName="(value) => (name1 = value)"
  125.   />
  126.   <OpenVipTip v-else />
  127. </template><template>
  128.   <Block1
  129.     v-if="showVipContent"
  130.     :name="name1"
  131.     @changeName="(value) => (name1 = value)"
  132.   />
  133.   <OpenVipTip v-else />
  134. </template>resolve("我是折扣信息1");<template>
  135.   <Block1
  136.     v-if="showVipContent"
  137.     :name="name1"
  138.     @changeName="(value) => (name1 = value)"
  139.   />
  140.   <OpenVipTip v-else />
  141. </template><template>
  142.   <Block1
  143.     v-if="showVipContent"
  144.     :name="name1"
  145.     @changeName="(value) => (name1 = value)"
  146.   />
  147.   <OpenVipTip v-else />
  148. </template>  }, 1000);<template>
  149.   <Block1
  150.     v-if="showVipContent"
  151.     :name="name1"
  152.     @changeName="(value) => (name1 = value)"
  153.   />
  154.   <OpenVipTip v-else />
  155. </template><template>
  156.   <Block1
  157.     v-if="showVipContent"
  158.     :name="name1"
  159.     @changeName="(value) => (name1 = value)"
  160.   />
  161.   <OpenVipTip v-else />
  162. </template>});<template>
  163.   <Block1
  164.     v-if="showVipContent"
  165.     :name="name1"
  166.     @changeName="(value) => (name1 = value)"
  167.   />
  168.   <OpenVipTip v-else />
  169. </template>  }<template>
  170.   <Block1
  171.     v-if="showVipContent"
  172.     :name="name1"
  173.     @changeName="(value) => (name1 = value)"
  174.   />
  175.   <OpenVipTip v-else />
  176. </template>  const innerRef = ref();<template>
  177.   <Block1
  178.     v-if="showVipContent"
  179.     :name="name1"
  180.     @changeName="(value) => (name1 = value)"
  181.   />
  182.   <OpenVipTip v-else />
  183. </template>  expose(<template>
  184.   <Block1
  185.     v-if="showVipContent"
  186.     :name="name1"
  187.     @changeName="(value) => (name1 = value)"
  188.   />
  189.   <OpenVipTip v-else />
  190. </template><template>
  191.   <Block1
  192.     v-if="showVipContent"
  193.     :name="name1"
  194.     @changeName="(value) => (name1 = value)"
  195.   />
  196.   <OpenVipTip v-else />
  197. </template>new Proxy(<template>
  198.   <Block1
  199.     v-if="showVipContent"
  200.     :name="name1"
  201.     @changeName="(value) => (name1 = value)"
  202.   />
  203.   <OpenVipTip v-else />
  204. </template><template>
  205.   <Block1
  206.     v-if="showVipContent"
  207.     :name="name1"
  208.     @changeName="(value) => (name1 = value)"
  209.   />
  210.   <OpenVipTip v-else />
  211. </template>  {},<template>
  212.   <Block1
  213.     v-if="showVipContent"
  214.     :name="name1"
  215.     @changeName="(value) => (name1 = value)"
  216.   />
  217.   <OpenVipTip v-else />
  218. </template><template>
  219.   <Block1
  220.     v-if="showVipContent"
  221.     :name="name1"
  222.     @changeName="(value) => (name1 = value)"
  223.   />
  224.   <OpenVipTip v-else />
  225. </template>  {<template>
  226.   <Block1
  227.     v-if="showVipContent"
  228.     :name="name1"
  229.     @changeName="(value) => (name1 = value)"
  230.   />
  231.   <OpenVipTip v-else />
  232. </template><template>
  233.   <Block1
  234.     v-if="showVipContent"
  235.     :name="name1"
  236.     @changeName="(value) => (name1 = value)"
  237.   />
  238.   <OpenVipTip v-else />
  239. </template><template>
  240.   <Block1
  241.     v-if="showVipContent"
  242.     :name="name1"
  243.     @changeName="(value) => (name1 = value)"
  244.   />
  245.   <OpenVipTip v-else />
  246. </template>get(_target, key) {<template>
  247.   <Block1
  248.     v-if="showVipContent"
  249.     :name="name1"
  250.     @changeName="(value) => (name1 = value)"
  251.   />
  252.   <OpenVipTip v-else />
  253. </template><template>
  254.   <Block1
  255.     v-if="showVipContent"
  256.     :name="name1"
  257.     @changeName="(value) => (name1 = value)"
  258.   />
  259.   <OpenVipTip v-else />
  260. </template><template>
  261.   <Block1
  262.     v-if="showVipContent"
  263.     :name="name1"
  264.     @changeName="(value) => (name1 = value)"
  265.   />
  266.   <OpenVipTip v-else />
  267. </template>  return innerRef.value?.[key];<template>
  268.   <Block1
  269.     v-if="showVipContent"
  270.     :name="name1"
  271.     @changeName="(value) => (name1 = value)"
  272.   />
  273.   <OpenVipTip v-else />
  274. </template><template>
  275.   <Block1
  276.     v-if="showVipContent"
  277.     :name="name1"
  278.     @changeName="(value) => (name1 = value)"
  279.   />
  280.   <OpenVipTip v-else />
  281. </template><template>
  282.   <Block1
  283.     v-if="showVipContent"
  284.     :name="name1"
  285.     @changeName="(value) => (name1 = value)"
  286.   />
  287.   <OpenVipTip v-else />
  288. </template>},<template>
  289.   <Block1
  290.     v-if="showVipContent"
  291.     :name="name1"
  292.     @changeName="(value) => (name1 = value)"
  293.   />
  294.   <OpenVipTip v-else />
  295. </template><template>
  296.   <Block1
  297.     v-if="showVipContent"
  298.     :name="name1"
  299.     @changeName="(value) => (name1 = value)"
  300.   />
  301.   <OpenVipTip v-else />
  302. </template><template>
  303.   <Block1
  304.     v-if="showVipContent"
  305.     :name="name1"
  306.     @changeName="(value) => (name1 = value)"
  307.   />
  308.   <OpenVipTip v-else />
  309. </template>has(_target, key) {<template>
  310.   <Block1
  311.     v-if="showVipContent"
  312.     :name="name1"
  313.     @changeName="(value) => (name1 = value)"
  314.   />
  315.   <OpenVipTip v-else />
  316. </template><template>
  317.   <Block1
  318.     v-if="showVipContent"
  319.     :name="name1"
  320.     @changeName="(value) => (name1 = value)"
  321.   />
  322.   <OpenVipTip v-else />
  323. </template><template>
  324.   <Block1
  325.     v-if="showVipContent"
  326.     :name="name1"
  327.     @changeName="(value) => (name1 = value)"
  328.   />
  329.   <OpenVipTip v-else />
  330. </template>  return innerRef.value?.[key];<template>
  331.   <Block1
  332.     v-if="showVipContent"
  333.     :name="name1"
  334.     @changeName="(value) => (name1 = value)"
  335.   />
  336.   <OpenVipTip v-else />
  337. </template><template>
  338.   <Block1
  339.     v-if="showVipContent"
  340.     :name="name1"
  341.     @changeName="(value) => (name1 = value)"
  342.   />
  343.   <OpenVipTip v-else />
  344. </template><template>
  345.   <Block1
  346.     v-if="showVipContent"
  347.     :name="name1"
  348.     @changeName="(value) => (name1 = value)"
  349.   />
  350.   <OpenVipTip v-else />
  351. </template>},<template>
  352.   <Block1
  353.     v-if="showVipContent"
  354.     :name="name1"
  355.     @changeName="(value) => (name1 = value)"
  356.   />
  357.   <OpenVipTip v-else />
  358. </template><template>
  359.   <Block1
  360.     v-if="showVipContent"
  361.     :name="name1"
  362.     @changeName="(value) => (name1 = value)"
  363.   />
  364.   <OpenVipTip v-else />
  365. </template>  }<template>
  366.   <Block1
  367.     v-if="showVipContent"
  368.     :name="name1"
  369.     @changeName="(value) => (name1 = value)"
  370.   />
  371.   <OpenVipTip v-else />
  372. </template><template>
  373.   <Block1
  374.     v-if="showVipContent"
  375.     :name="name1"
  376.     @changeName="(value) => (name1 = value)"
  377.   />
  378.   <OpenVipTip v-else />
  379. </template>)<template>
  380.   <Block1
  381.     v-if="showVipContent"
  382.     :name="name1"
  383.     @changeName="(value) => (name1 = value)"
  384.   />
  385.   <OpenVipTip v-else />
  386. </template>  );<template>
  387.   <Block1
  388.     v-if="showVipContent"
  389.     :name="name1"
  390.     @changeName="(value) => (name1 = value)"
  391.   />
  392.   <OpenVipTip v-else />
  393. </template>  return () => {<template>
  394.   <Block1
  395.     v-if="showVipContent"
  396.     :name="name1"
  397.     @changeName="(value) => (name1 = value)"
  398.   />
  399.   <OpenVipTip v-else />
  400. </template><template>
  401.   <Block1
  402.     v-if="showVipContent"
  403.     :name="name1"
  404.     @changeName="(value) => (name1 = value)"
  405.   />
  406.   <OpenVipTip v-else />
  407. </template>return (<template>
  408.   <Block1
  409.     v-if="showVipContent"
  410.     :name="name1"
  411.     @changeName="(value) => (name1 = value)"
  412.   />
  413.   <OpenVipTip v-else />
  414. </template><template>
  415.   <Block1
  416.     v-if="showVipContent"
  417.     :name="name1"
  418.     @changeName="(value) => (name1 = value)"
  419.   />
  420.   <OpenVipTip v-else />
  421. </template><template>
  422.   <Block1
  423.     v-if="showVipContent"
  424.     :name="name1"
  425.     @changeName="(value) => (name1 = value)"
  426.   />
  427.   <OpenVipTip v-else />
  428. </template><template>
  429.   <Block1
  430.     v-if="showVipContent"
  431.     :name="name1"
  432.     @changeName="(value) => (name1 = value)"
  433.   />
  434.   <OpenVipTip v-else />
  435. </template><template>
  436.   <Block1
  437.     v-if="showVipContent"
  438.     :name="name1"
  439.     @changeName="(value) => (name1 = value)"
  440.   />
  441.   <OpenVipTip v-else />
  442. </template><template>
  443.   <Block1
  444.     v-if="showVipContent"
  445.     :name="name1"
  446.     @changeName="(value) => (name1 = value)"
  447.   />
  448.   <OpenVipTip v-else />
  449. </template><template>
  450.   <Block1
  451.     v-if="showVipContent"
  452.     :name="name1"
  453.     @changeName="(value) => (name1 = value)"
  454.   />
  455.   <OpenVipTip v-else />
  456. </template><template>
  457.   <Block1
  458.     v-if="showVipContent"
  459.     :name="name1"
  460.     @changeName="(value) => (name1 = value)"
  461.   />
  462.   <OpenVipTip v-else />
  463. </template><template>
  464.   <Block1
  465.     v-if="showVipContent"
  466.     :name="name1"
  467.     @changeName="(value) => (name1 = value)"
  468.   />
  469.   <OpenVipTip v-else />
  470. </template>{slots}<template>
  471.   <Block1
  472.     v-if="showVipContent"
  473.     :name="name1"
  474.     @changeName="(value) => (name1 = value)"
  475.   />
  476.   <OpenVipTip v-else />
  477. </template><template>
  478.   <Block1
  479.     v-if="showVipContent"
  480.     :name="name1"
  481.     @changeName="(value) => (name1 = value)"
  482.   />
  483.   <OpenVipTip v-else />
  484. </template><template>
  485.   <Block1
  486.     v-if="showVipContent"
  487.     :name="name1"
  488.     @changeName="(value) => (name1 = value)"
  489.   />
  490.   <OpenVipTip v-else />
  491. </template><template>
  492.   <Block1
  493.     v-if="showVipContent"
  494.     :name="name1"
  495.     @changeName="(value) => (name1 = value)"
  496.   />
  497.   <OpenVipTip v-else />
  498. </template><template>
  499.   <Block1
  500.     v-if="showVipContent"
  501.     :name="name1"
  502.     @changeName="(value) => (name1 = value)"
  503.   />
  504.   <OpenVipTip v-else />
  505. </template><template>
  506.   <Block1
  507.     v-if="showVipContent"
  508.     :name="name1"
  509.     @changeName="(value) => (name1 = value)"
  510.   />
  511.   <OpenVipTip v-else />
  512. </template>{discountInfo.value ? (<template>
  513.   <Block1
  514.     v-if="showVipContent"
  515.     :name="name1"
  516.     @changeName="(value) => (name1 = value)"
  517.   />
  518.   <OpenVipTip v-else />
  519. </template><template>
  520.   <Block1
  521.     v-if="showVipContent"
  522.     :name="name1"
  523.     @changeName="(value) => (name1 = value)"
  524.   />
  525.   <OpenVipTip v-else />
  526. </template><template>
  527.   <Block1
  528.     v-if="showVipContent"
  529.     :name="name1"
  530.     @changeName="(value) => (name1 = value)"
  531.   />
  532.   <OpenVipTip v-else />
  533. </template>  {discountInfo.value}<template>
  534.   <Block1
  535.     v-if="showVipContent"
  536.     :name="name1"
  537.     @changeName="(value) => (name1 = value)"
  538.   />
  539.   <OpenVipTip v-else />
  540. </template><template>
  541.   <Block1
  542.     v-if="showVipContent"
  543.     :name="name1"
  544.     @changeName="(value) => (name1 = value)"
  545.   />
  546.   <OpenVipTip v-else />
  547. </template><template>
  548.   <Block1
  549.     v-if="showVipContent"
  550.     :name="name1"
  551.     @changeName="(value) => (name1 = value)"
  552.   />
  553.   <OpenVipTip v-else />
  554. </template>) : null}<template>
  555.   <Block1
  556.     v-if="showVipContent"
  557.     :name="name1"
  558.     @changeName="(value) => (name1 = value)"
  559.   />
  560.   <OpenVipTip v-else />
  561. </template><template>
  562.   <Block1
  563.     v-if="showVipContent"
  564.     :name="name1"
  565.     @changeName="(value) => (name1 = value)"
  566.   />
  567.   <OpenVipTip v-else />
  568. </template><template>
  569.   <Block1
  570.     v-if="showVipContent"
  571.     :name="name1"
  572.     @changeName="(value) => (name1 = value)"
  573.   />
  574.   <OpenVipTip v-else />
  575. </template><template>
  576.   <Block1
  577.     v-if="showVipContent"
  578.     :name="name1"
  579.     @changeName="(value) => (name1 = value)"
  580.   />
  581.   <OpenVipTip v-else />
  582. </template>  );<template>
  583.   <Block1
  584.     v-if="showVipContent"
  585.     :name="name1"
  586.     @changeName="(value) => (name1 = value)"
  587.   />
  588.   <OpenVipTip v-else />
  589. </template>  };<template>
  590.   <Block1
  591.     v-if="showVipContent"
  592.     :name="name1"
  593.     @changeName="(value) => (name1 = value)"
  594.   />
  595.   <OpenVipTip v-else />
  596. </template>},  };}
复制代码
那么我们的父组件如果需要同时用VIP功能和折扣信息功能需要怎么办呢?代码如下:
  1. const EnhancedBlock1 = WithVip(WithDiscount(Block1, "item1"));
复制代码
如果不是VIP,那么这个模块的折扣信息也不需要显示了。
因为高阶组件接收一个组件,然后返回一个加强的组件。利用这个特性,我们可以使用上面的这种代码将其组合起来。
但是上面这种写法大家觉得是不是看着很难受,一层套一层。如果这里同时使用5个高阶组件,这里就会套5层了,那这个代码的维护难度就是地狱难度了。
所以这个时候就需要compose函数了,这个是React社区中常见的概念。它的核心思想是将多个函数从右到左依次组合起来执行,前一个函数的输出作为下一个函数的输入。
我们这里有多个HOC(也就是有多个函数),我们期望执行完第一个HOC得到一个加强的组件,然后以这个加强的组件为参数去执行第二个HOC,最后得到由多个HOC加强的组件。
compose函数就刚好符合我们的需求,这个是使用compose函数后的代码,如下:
  1. const EnhancedBlock1 = compose(WithVip, WithDiscount("item1"))(Block1);
复制代码
这样就舒服多了,所有的高阶组件都放在第一个括弧里面,并且由右向左去依次执行每个高阶组件HOC。如果某个高阶组件HOC需要除了组件之外的额外参数,像WithDiscount这样处理就可以了。
很明显,我们的WithDiscount高阶组件的代码需要修改才能满足compose函数的需求,这个是修改后的代码:
  1. import { SetupContext, onMounted, ref } from "vue";export default function WithDiscount(item: string) {  return (BaseComponent: any) => {<template>
  2.   <Block1
  3.     v-if="showVipContent"
  4.     :name="name1"
  5.     @changeName="(value) => (name1 = value)"
  6.   />
  7.   <OpenVipTip v-else />
  8. </template>return {<template>
  9.   <Block1
  10.     v-if="showVipContent"
  11.     :name="name1"
  12.     @changeName="(value) => (name1 = value)"
  13.   />
  14.   <OpenVipTip v-else />
  15. </template>  props: BaseComponent.props,<template>
  16.   <Block1
  17.     v-if="showVipContent"
  18.     :name="name1"
  19.     @changeName="(value) => (name1 = value)"
  20.   />
  21.   <OpenVipTip v-else />
  22. </template>  setup(props, { attrs, slots, expose }: SetupContext) {<template>
  23.   <Block1
  24.     v-if="showVipContent"
  25.     :name="name1"
  26.     @changeName="(value) => (name1 = value)"
  27.   />
  28.   <OpenVipTip v-else />
  29. </template><template>
  30.   <Block1
  31.     v-if="showVipContent"
  32.     :name="name1"
  33.     @changeName="(value) => (name1 = value)"
  34.   />
  35.   <OpenVipTip v-else />
  36. </template>const discountInfo = ref("");<template>
  37.   <Block1
  38.     v-if="showVipContent"
  39.     :name="name1"
  40.     @changeName="(value) => (name1 = value)"
  41.   />
  42.   <OpenVipTip v-else />
  43. </template><template>
  44.   <Block1
  45.     v-if="showVipContent"
  46.     :name="name1"
  47.     @changeName="(value) => (name1 = value)"
  48.   />
  49.   <OpenVipTip v-else />
  50. </template>onMounted(async () => {<template>
  51.   <Block1
  52.     v-if="showVipContent"
  53.     :name="name1"
  54.     @changeName="(value) => (name1 = value)"
  55.   />
  56.   <OpenVipTip v-else />
  57. </template><template>
  58.   <Block1
  59.     v-if="showVipContent"
  60.     :name="name1"
  61.     @changeName="(value) => (name1 = value)"
  62.   />
  63.   <OpenVipTip v-else />
  64. </template>  const res = await getDiscountInfo(item);<template>
  65.   <Block1
  66.     v-if="showVipContent"
  67.     :name="name1"
  68.     @changeName="(value) => (name1 = value)"
  69.   />
  70.   <OpenVipTip v-else />
  71. </template><template>
  72.   <Block1
  73.     v-if="showVipContent"
  74.     :name="name1"
  75.     @changeName="(value) => (name1 = value)"
  76.   />
  77.   <OpenVipTip v-else />
  78. </template>  discountInfo.value = res;<template>
  79.   <Block1
  80.     v-if="showVipContent"
  81.     :name="name1"
  82.     @changeName="(value) => (name1 = value)"
  83.   />
  84.   <OpenVipTip v-else />
  85. </template><template>
  86.   <Block1
  87.     v-if="showVipContent"
  88.     :name="name1"
  89.     @changeName="(value) => (name1 = value)"
  90.   />
  91.   <OpenVipTip v-else />
  92. </template>});<template>
  93.   <Block1
  94.     v-if="showVipContent"
  95.     :name="name1"
  96.     @changeName="(value) => (name1 = value)"
  97.   />
  98.   <OpenVipTip v-else />
  99. </template><template>
  100.   <Block1
  101.     v-if="showVipContent"
  102.     :name="name1"
  103.     @changeName="(value) => (name1 = value)"
  104.   />
  105.   <OpenVipTip v-else />
  106. </template>function getDiscountInfo(item: any): Promise {<template>
  107.   <Block1
  108.     v-if="showVipContent"
  109.     :name="name1"
  110.     @changeName="(value) => (name1 = value)"
  111.   />
  112.   <OpenVipTip v-else />
  113. </template><template>
  114.   <Block1
  115.     v-if="showVipContent"
  116.     :name="name1"
  117.     @changeName="(value) => (name1 = value)"
  118.   />
  119.   <OpenVipTip v-else />
  120. </template>  // 根据传入的item获取折扣信息<template>
  121.   <Block1
  122.     v-if="showVipContent"
  123.     :name="name1"
  124.     @changeName="(value) => (name1 = value)"
  125.   />
  126.   <OpenVipTip v-else />
  127. </template><template>
  128.   <Block1
  129.     v-if="showVipContent"
  130.     :name="name1"
  131.     @changeName="(value) => (name1 = value)"
  132.   />
  133.   <OpenVipTip v-else />
  134. </template>  return new Promise((resolve) => {<template>
  135.   <Block1
  136.     v-if="showVipContent"
  137.     :name="name1"
  138.     @changeName="(value) => (name1 = value)"
  139.   />
  140.   <OpenVipTip v-else />
  141. </template><template>
  142.   <Block1
  143.     v-if="showVipContent"
  144.     :name="name1"
  145.     @changeName="(value) => (name1 = value)"
  146.   />
  147.   <OpenVipTip v-else />
  148. </template><template>
  149.   <Block1
  150.     v-if="showVipContent"
  151.     :name="name1"
  152.     @changeName="(value) => (name1 = value)"
  153.   />
  154.   <OpenVipTip v-else />
  155. </template>setTimeout(() => {<template>
  156.   <Block1
  157.     v-if="showVipContent"
  158.     :name="name1"
  159.     @changeName="(value) => (name1 = value)"
  160.   />
  161.   <OpenVipTip v-else />
  162. </template><template>
  163.   <Block1
  164.     v-if="showVipContent"
  165.     :name="name1"
  166.     @changeName="(value) => (name1 = value)"
  167.   />
  168.   <OpenVipTip v-else />
  169. </template><template>
  170.   <Block1
  171.     v-if="showVipContent"
  172.     :name="name1"
  173.     @changeName="(value) => (name1 = value)"
  174.   />
  175.   <OpenVipTip v-else />
  176. </template>  resolve("我是折扣信息1");<template>
  177.   <Block1
  178.     v-if="showVipContent"
  179.     :name="name1"
  180.     @changeName="(value) => (name1 = value)"
  181.   />
  182.   <OpenVipTip v-else />
  183. </template><template>
  184.   <Block1
  185.     v-if="showVipContent"
  186.     :name="name1"
  187.     @changeName="(value) => (name1 = value)"
  188.   />
  189.   <OpenVipTip v-else />
  190. </template><template>
  191.   <Block1
  192.     v-if="showVipContent"
  193.     :name="name1"
  194.     @changeName="(value) => (name1 = value)"
  195.   />
  196.   <OpenVipTip v-else />
  197. </template>}, 1000);<template>
  198.   <Block1
  199.     v-if="showVipContent"
  200.     :name="name1"
  201.     @changeName="(value) => (name1 = value)"
  202.   />
  203.   <OpenVipTip v-else />
  204. </template><template>
  205.   <Block1
  206.     v-if="showVipContent"
  207.     :name="name1"
  208.     @changeName="(value) => (name1 = value)"
  209.   />
  210.   <OpenVipTip v-else />
  211. </template>  });<template>
  212.   <Block1
  213.     v-if="showVipContent"
  214.     :name="name1"
  215.     @changeName="(value) => (name1 = value)"
  216.   />
  217.   <OpenVipTip v-else />
  218. </template><template>
  219.   <Block1
  220.     v-if="showVipContent"
  221.     :name="name1"
  222.     @changeName="(value) => (name1 = value)"
  223.   />
  224.   <OpenVipTip v-else />
  225. </template>}<template>
  226.   <Block1
  227.     v-if="showVipContent"
  228.     :name="name1"
  229.     @changeName="(value) => (name1 = value)"
  230.   />
  231.   <OpenVipTip v-else />
  232. </template><template>
  233.   <Block1
  234.     v-if="showVipContent"
  235.     :name="name1"
  236.     @changeName="(value) => (name1 = value)"
  237.   />
  238.   <OpenVipTip v-else />
  239. </template>const innerRef = ref();<template>
  240.   <Block1
  241.     v-if="showVipContent"
  242.     :name="name1"
  243.     @changeName="(value) => (name1 = value)"
  244.   />
  245.   <OpenVipTip v-else />
  246. </template><template>
  247.   <Block1
  248.     v-if="showVipContent"
  249.     :name="name1"
  250.     @changeName="(value) => (name1 = value)"
  251.   />
  252.   <OpenVipTip v-else />
  253. </template>expose(<template>
  254.   <Block1
  255.     v-if="showVipContent"
  256.     :name="name1"
  257.     @changeName="(value) => (name1 = value)"
  258.   />
  259.   <OpenVipTip v-else />
  260. </template><template>
  261.   <Block1
  262.     v-if="showVipContent"
  263.     :name="name1"
  264.     @changeName="(value) => (name1 = value)"
  265.   />
  266.   <OpenVipTip v-else />
  267. </template>  new Proxy(<template>
  268.   <Block1
  269.     v-if="showVipContent"
  270.     :name="name1"
  271.     @changeName="(value) => (name1 = value)"
  272.   />
  273.   <OpenVipTip v-else />
  274. </template><template>
  275.   <Block1
  276.     v-if="showVipContent"
  277.     :name="name1"
  278.     @changeName="(value) => (name1 = value)"
  279.   />
  280.   <OpenVipTip v-else />
  281. </template><template>
  282.   <Block1
  283.     v-if="showVipContent"
  284.     :name="name1"
  285.     @changeName="(value) => (name1 = value)"
  286.   />
  287.   <OpenVipTip v-else />
  288. </template>{},<template>
  289.   <Block1
  290.     v-if="showVipContent"
  291.     :name="name1"
  292.     @changeName="(value) => (name1 = value)"
  293.   />
  294.   <OpenVipTip v-else />
  295. </template><template>
  296.   <Block1
  297.     v-if="showVipContent"
  298.     :name="name1"
  299.     @changeName="(value) => (name1 = value)"
  300.   />
  301.   <OpenVipTip v-else />
  302. </template><template>
  303.   <Block1
  304.     v-if="showVipContent"
  305.     :name="name1"
  306.     @changeName="(value) => (name1 = value)"
  307.   />
  308.   <OpenVipTip v-else />
  309. </template>{<template>
  310.   <Block1
  311.     v-if="showVipContent"
  312.     :name="name1"
  313.     @changeName="(value) => (name1 = value)"
  314.   />
  315.   <OpenVipTip v-else />
  316. </template><template>
  317.   <Block1
  318.     v-if="showVipContent"
  319.     :name="name1"
  320.     @changeName="(value) => (name1 = value)"
  321.   />
  322.   <OpenVipTip v-else />
  323. </template><template>
  324.   <Block1
  325.     v-if="showVipContent"
  326.     :name="name1"
  327.     @changeName="(value) => (name1 = value)"
  328.   />
  329.   <OpenVipTip v-else />
  330. </template>  get(_target, key) {<template>
  331.   <Block1
  332.     v-if="showVipContent"
  333.     :name="name1"
  334.     @changeName="(value) => (name1 = value)"
  335.   />
  336.   <OpenVipTip v-else />
  337. </template><template>
  338.   <Block1
  339.     v-if="showVipContent"
  340.     :name="name1"
  341.     @changeName="(value) => (name1 = value)"
  342.   />
  343.   <OpenVipTip v-else />
  344. </template><template>
  345.   <Block1
  346.     v-if="showVipContent"
  347.     :name="name1"
  348.     @changeName="(value) => (name1 = value)"
  349.   />
  350.   <OpenVipTip v-else />
  351. </template><template>
  352.   <Block1
  353.     v-if="showVipContent"
  354.     :name="name1"
  355.     @changeName="(value) => (name1 = value)"
  356.   />
  357.   <OpenVipTip v-else />
  358. </template>return innerRef.value?.[key];<template>
  359.   <Block1
  360.     v-if="showVipContent"
  361.     :name="name1"
  362.     @changeName="(value) => (name1 = value)"
  363.   />
  364.   <OpenVipTip v-else />
  365. </template><template>
  366.   <Block1
  367.     v-if="showVipContent"
  368.     :name="name1"
  369.     @changeName="(value) => (name1 = value)"
  370.   />
  371.   <OpenVipTip v-else />
  372. </template><template>
  373.   <Block1
  374.     v-if="showVipContent"
  375.     :name="name1"
  376.     @changeName="(value) => (name1 = value)"
  377.   />
  378.   <OpenVipTip v-else />
  379. </template>  },<template>
  380.   <Block1
  381.     v-if="showVipContent"
  382.     :name="name1"
  383.     @changeName="(value) => (name1 = value)"
  384.   />
  385.   <OpenVipTip v-else />
  386. </template><template>
  387.   <Block1
  388.     v-if="showVipContent"
  389.     :name="name1"
  390.     @changeName="(value) => (name1 = value)"
  391.   />
  392.   <OpenVipTip v-else />
  393. </template><template>
  394.   <Block1
  395.     v-if="showVipContent"
  396.     :name="name1"
  397.     @changeName="(value) => (name1 = value)"
  398.   />
  399.   <OpenVipTip v-else />
  400. </template>  has(_target, key) {<template>
  401.   <Block1
  402.     v-if="showVipContent"
  403.     :name="name1"
  404.     @changeName="(value) => (name1 = value)"
  405.   />
  406.   <OpenVipTip v-else />
  407. </template><template>
  408.   <Block1
  409.     v-if="showVipContent"
  410.     :name="name1"
  411.     @changeName="(value) => (name1 = value)"
  412.   />
  413.   <OpenVipTip v-else />
  414. </template><template>
  415.   <Block1
  416.     v-if="showVipContent"
  417.     :name="name1"
  418.     @changeName="(value) => (name1 = value)"
  419.   />
  420.   <OpenVipTip v-else />
  421. </template><template>
  422.   <Block1
  423.     v-if="showVipContent"
  424.     :name="name1"
  425.     @changeName="(value) => (name1 = value)"
  426.   />
  427.   <OpenVipTip v-else />
  428. </template>return innerRef.value?.[key];<template>
  429.   <Block1
  430.     v-if="showVipContent"
  431.     :name="name1"
  432.     @changeName="(value) => (name1 = value)"
  433.   />
  434.   <OpenVipTip v-else />
  435. </template><template>
  436.   <Block1
  437.     v-if="showVipContent"
  438.     :name="name1"
  439.     @changeName="(value) => (name1 = value)"
  440.   />
  441.   <OpenVipTip v-else />
  442. </template><template>
  443.   <Block1
  444.     v-if="showVipContent"
  445.     :name="name1"
  446.     @changeName="(value) => (name1 = value)"
  447.   />
  448.   <OpenVipTip v-else />
  449. </template>  },<template>
  450.   <Block1
  451.     v-if="showVipContent"
  452.     :name="name1"
  453.     @changeName="(value) => (name1 = value)"
  454.   />
  455.   <OpenVipTip v-else />
  456. </template><template>
  457.   <Block1
  458.     v-if="showVipContent"
  459.     :name="name1"
  460.     @changeName="(value) => (name1 = value)"
  461.   />
  462.   <OpenVipTip v-else />
  463. </template><template>
  464.   <Block1
  465.     v-if="showVipContent"
  466.     :name="name1"
  467.     @changeName="(value) => (name1 = value)"
  468.   />
  469.   <OpenVipTip v-else />
  470. </template>}<template>
  471.   <Block1
  472.     v-if="showVipContent"
  473.     :name="name1"
  474.     @changeName="(value) => (name1 = value)"
  475.   />
  476.   <OpenVipTip v-else />
  477. </template><template>
  478.   <Block1
  479.     v-if="showVipContent"
  480.     :name="name1"
  481.     @changeName="(value) => (name1 = value)"
  482.   />
  483.   <OpenVipTip v-else />
  484. </template>  )<template>
  485.   <Block1
  486.     v-if="showVipContent"
  487.     :name="name1"
  488.     @changeName="(value) => (name1 = value)"
  489.   />
  490.   <OpenVipTip v-else />
  491. </template><template>
  492.   <Block1
  493.     v-if="showVipContent"
  494.     :name="name1"
  495.     @changeName="(value) => (name1 = value)"
  496.   />
  497.   <OpenVipTip v-else />
  498. </template>);<template>
  499.   <Block1
  500.     v-if="showVipContent"
  501.     :name="name1"
  502.     @changeName="(value) => (name1 = value)"
  503.   />
  504.   <OpenVipTip v-else />
  505. </template><template>
  506.   <Block1
  507.     v-if="showVipContent"
  508.     :name="name1"
  509.     @changeName="(value) => (name1 = value)"
  510.   />
  511.   <OpenVipTip v-else />
  512. </template>return () => {<template>
  513.   <Block1
  514.     v-if="showVipContent"
  515.     :name="name1"
  516.     @changeName="(value) => (name1 = value)"
  517.   />
  518.   <OpenVipTip v-else />
  519. </template><template>
  520.   <Block1
  521.     v-if="showVipContent"
  522.     :name="name1"
  523.     @changeName="(value) => (name1 = value)"
  524.   />
  525.   <OpenVipTip v-else />
  526. </template>  return (<template>
  527.   <Block1
  528.     v-if="showVipContent"
  529.     :name="name1"
  530.     @changeName="(value) => (name1 = value)"
  531.   />
  532.   <OpenVipTip v-else />
  533. </template><template>
  534.   <Block1
  535.     v-if="showVipContent"
  536.     :name="name1"
  537.     @changeName="(value) => (name1 = value)"
  538.   />
  539.   <OpenVipTip v-else />
  540. </template><template>
  541.   <Block1
  542.     v-if="showVipContent"
  543.     :name="name1"
  544.     @changeName="(value) => (name1 = value)"
  545.   />
  546.   <OpenVipTip v-else />
  547. </template><template>
  548.   <Block1
  549.     v-if="showVipContent"
  550.     :name="name1"
  551.     @changeName="(value) => (name1 = value)"
  552.   />
  553.   <OpenVipTip v-else />
  554. </template><template>
  555.   <Block1
  556.     v-if="showVipContent"
  557.     :name="name1"
  558.     @changeName="(value) => (name1 = value)"
  559.   />
  560.   <OpenVipTip v-else />
  561. </template><template>
  562.   <Block1
  563.     v-if="showVipContent"
  564.     :name="name1"
  565.     @changeName="(value) => (name1 = value)"
  566.   />
  567.   <OpenVipTip v-else />
  568. </template><template>
  569.   <Block1
  570.     v-if="showVipContent"
  571.     :name="name1"
  572.     @changeName="(value) => (name1 = value)"
  573.   />
  574.   <OpenVipTip v-else />
  575. </template><template>
  576.   <Block1
  577.     v-if="showVipContent"
  578.     :name="name1"
  579.     @changeName="(value) => (name1 = value)"
  580.   />
  581.   <OpenVipTip v-else />
  582. </template><template>
  583.   <Block1
  584.     v-if="showVipContent"
  585.     :name="name1"
  586.     @changeName="(value) => (name1 = value)"
  587.   />
  588.   <OpenVipTip v-else />
  589. </template><template>
  590.   <Block1
  591.     v-if="showVipContent"
  592.     :name="name1"
  593.     @changeName="(value) => (name1 = value)"
  594.   />
  595.   <OpenVipTip v-else />
  596. </template>  {slots}<template>
  597.   <Block1
  598.     v-if="showVipContent"
  599.     :name="name1"
  600.     @changeName="(value) => (name1 = value)"
  601.   />
  602.   <OpenVipTip v-else />
  603. </template><template>
  604.   <Block1
  605.     v-if="showVipContent"
  606.     :name="name1"
  607.     @changeName="(value) => (name1 = value)"
  608.   />
  609.   <OpenVipTip v-else />
  610. </template><template>
  611.   <Block1
  612.     v-if="showVipContent"
  613.     :name="name1"
  614.     @changeName="(value) => (name1 = value)"
  615.   />
  616.   <OpenVipTip v-else />
  617. </template><template>
  618.   <Block1
  619.     v-if="showVipContent"
  620.     :name="name1"
  621.     @changeName="(value) => (name1 = value)"
  622.   />
  623.   <OpenVipTip v-else />
  624. </template><template>
  625.   <Block1
  626.     v-if="showVipContent"
  627.     :name="name1"
  628.     @changeName="(value) => (name1 = value)"
  629.   />
  630.   <OpenVipTip v-else />
  631. </template><template>
  632.   <Block1
  633.     v-if="showVipContent"
  634.     :name="name1"
  635.     @changeName="(value) => (name1 = value)"
  636.   />
  637.   <OpenVipTip v-else />
  638. </template><template>
  639.   <Block1
  640.     v-if="showVipContent"
  641.     :name="name1"
  642.     @changeName="(value) => (name1 = value)"
  643.   />
  644.   <OpenVipTip v-else />
  645. </template>{discountInfo.value ? (<template>
  646.   <Block1
  647.     v-if="showVipContent"
  648.     :name="name1"
  649.     @changeName="(value) => (name1 = value)"
  650.   />
  651.   <OpenVipTip v-else />
  652. </template><template>
  653.   <Block1
  654.     v-if="showVipContent"
  655.     :name="name1"
  656.     @changeName="(value) => (name1 = value)"
  657.   />
  658.   <OpenVipTip v-else />
  659. </template><template>
  660.   <Block1
  661.     v-if="showVipContent"
  662.     :name="name1"
  663.     @changeName="(value) => (name1 = value)"
  664.   />
  665.   <OpenVipTip v-else />
  666. </template><template>
  667.   <Block1
  668.     v-if="showVipContent"
  669.     :name="name1"
  670.     @changeName="(value) => (name1 = value)"
  671.   />
  672.   <OpenVipTip v-else />
  673. </template>{discountInfo.value}<template>
  674.   <Block1
  675.     v-if="showVipContent"
  676.     :name="name1"
  677.     @changeName="(value) => (name1 = value)"
  678.   />
  679.   <OpenVipTip v-else />
  680. </template><template>
  681.   <Block1
  682.     v-if="showVipContent"
  683.     :name="name1"
  684.     @changeName="(value) => (name1 = value)"
  685.   />
  686.   <OpenVipTip v-else />
  687. </template><template>
  688.   <Block1
  689.     v-if="showVipContent"
  690.     :name="name1"
  691.     @changeName="(value) => (name1 = value)"
  692.   />
  693.   <OpenVipTip v-else />
  694. </template>  ) : null}<template>
  695.   <Block1
  696.     v-if="showVipContent"
  697.     :name="name1"
  698.     @changeName="(value) => (name1 = value)"
  699.   />
  700.   <OpenVipTip v-else />
  701. </template><template>
  702.   <Block1
  703.     v-if="showVipContent"
  704.     :name="name1"
  705.     @changeName="(value) => (name1 = value)"
  706.   />
  707.   <OpenVipTip v-else />
  708. </template><template>
  709.   <Block1
  710.     v-if="showVipContent"
  711.     :name="name1"
  712.     @changeName="(value) => (name1 = value)"
  713.   />
  714.   <OpenVipTip v-else />
  715. </template><template>
  716.   <Block1
  717.     v-if="showVipContent"
  718.     :name="name1"
  719.     @changeName="(value) => (name1 = value)"
  720.   />
  721.   <OpenVipTip v-else />
  722. </template><template>
  723.   <Block1
  724.     v-if="showVipContent"
  725.     :name="name1"
  726.     @changeName="(value) => (name1 = value)"
  727.   />
  728.   <OpenVipTip v-else />
  729. </template>  );<template>
  730.   <Block1
  731.     v-if="showVipContent"
  732.     :name="name1"
  733.     @changeName="(value) => (name1 = value)"
  734.   />
  735.   <OpenVipTip v-else />
  736. </template><template>
  737.   <Block1
  738.     v-if="showVipContent"
  739.     :name="name1"
  740.     @changeName="(value) => (name1 = value)"
  741.   />
  742.   <OpenVipTip v-else />
  743. </template>};<template>
  744.   <Block1
  745.     v-if="showVipContent"
  746.     :name="name1"
  747.     @changeName="(value) => (name1 = value)"
  748.   />
  749.   <OpenVipTip v-else />
  750. </template>  },<template>
  751.   <Block1
  752.     v-if="showVipContent"
  753.     :name="name1"
  754.     @changeName="(value) => (name1 = value)"
  755.   />
  756.   <OpenVipTip v-else />
  757. </template>};  };}
复制代码
注意看,WithDiscount此时只接收一个参数item,不再接收BaseComponent组件对象了,然后直接return出去一个回调函数。
准确的来说此时的WithDiscount函数已经不是高阶组件HOC了,他return出去的回调函数才是真正的高阶组件HOC。在回调函数中去接收BaseComponent组件对象,然后返回一个增强后的Vue组件对象。
至于参数item,因为闭包所以在里层的回调函数中还是能够访问的。这里比较绕,可能需要多理解一下。
前面的理解完了后,我们可以再上一点强度了。来看看compose函数是如何实现的,代码如下:
  1. function compose(...funcs) {
  2.   return funcs.reduce((acc, cur) => (...args) => acc(cur(...args)));
  3. }
复制代码
这个函数虽然只有一行代码,但是乍一看,怎么看怎么懵逼,欧阳也是!!我们还是结合demo来看:
  1. const EnhancedBlock1 = compose(WithA, WithB, WithC, WithD)(View);
复制代码
假如我们这里有WithA、WithB、 WithC、 WithD四个高阶组件,都是用于增强组件View。
compose中使用的是...funcs将调用compose函数接收到的四个高阶组件都存到了funcs数组中。
然后使用reduce去遍历这些高阶组件,注意看执行reduce时没有传入第二个参数。
所以第一次执行reduce时,acc的值为WithA,cur的值为WithB。返回结果也是一个回调函数,将这两个值填充进去就是(...args) => WithA(WithB(...args)),我们将第一次的执行结果命名为r1。
我们知道reduce会将上一次的执行结果赋值为acc,所以第二次执行reduce时,acc的值为r1,cur的值为WithC。返回结果也是一个回调函数,同样将这两个值填充进行就是(...args) => r1(WithC(...args))。同样我们将第二次的执行结果命名为r2。
第三次执行reduce时,此时的acc的值为r2,cur的值为WithD。返回结果也是一个回调函数,同样将这两个值填充进行就是(...args) => r2(WithD(...args))。同样我们将第三次的执行结果命名为r3,由于已经将数组遍历完了,最终reduce的返回值就是r3,他是一个回调函数。
由于compose(WithA, WithB, WithC, WithD)的执行结果为r3,那么compose(WithA, WithB, WithC, WithD)(View)就等价于r3(View)。
前面我们知道r3是一个回调函数:(...args) => r2(WithD(...args)),这个回调函数接收的参数args,就是需要增强的基础组件View。所以执行这个回调函数就是先执行WithD对组件进行增强,然后将增强后的组件作为参数去执行r2。
同样r2也是一个回调函数:(...args) => r1(WithC(...args)),接收上一次WithD增强后的组件为参数执行WithC对组件再次进行增强,然后将增强后的组件作为参数去执行r1。
同样r1也是一个回调函数:(...args) => WithA(WithB(...args)),将WithC增强后的组件丢给WithB去执行,得到增强的组件再丢给WithA去执行,最终就拿到了最后增强的组件。
执行顺序就是从右向左去依次执行高阶组件对基础组件进行增强。
至此,关于compose函数已经讲完了,这里对于Vue的同学可能比较难理解,建议多看两遍。
总结

这篇文章我们讲了在Vue3中如何实现一个高阶组件HOC,但是里面涉及到了很多源码知识,所以这是一篇运用源码的实战文章。如果你理解了文章中涉及到的知识,那么就会觉得Vue中实现HOC还是很简单的,反之就像是在看天书。
还有最重要的一点就是Composition API已经能够解决绝大部分的问题,只有少部分的场景才需要使用高阶组件HOC,切勿强行使用HOC,那样可能会有炫技的嫌疑。如果是防御性编程,那么就当我没说。
最后就是我们实现的每个高阶组件HOC都有很多重复的代码,而且实现起来很麻烦,心智负担也很高。那么我们是不是可以抽取一个createHOC函数去批量生成高阶组件呢?这个就留给各位自己去思考了。
还有一个问题,我们这种实现的高阶组件叫做正向属性代理,弊端是每代理一层就会增加一层组件的嵌套。那么有没有方法可以解决嵌套的问题呢?
答案是反向继承,但是这种也有弊端如果业务是setup中返回的render函数,那么就没法重写了render函数了。
关注公众号:【前端欧阳】,给自己一个进阶vue的机会
3.jpeg

另外欧阳写了一本开源电子书vue3编译原理揭秘,看完这本书可以让你对vue编译的认知有质的提升。这本书初、中级前端能看懂,完全免费,只求一个star。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册