把Vue3模板复用玩到了极致,少封装几十个组件!
创始人
2025-07-11 12:51:56
0

 普通的场景

最近在做 Vue3 项目的时候,在思考一个小问题,其实是每个人都做过的一个场景,很简单,看下方代码:

其实就是一个普通的不能再普通的循环遍历渲染的案例,咱们往下接着看,如果这样的遍历在同一个组件里出现了很多次,比如下方代码:

这个时候我们应该咋办呢?诶!很多人很快就能想出来了,那就是把循环的项抽取出来成一个组件,这样就能减少很多代码量了,比如我抽取成 Item.vue 这个组件:

然后直接可以引用并使用它,这样大大减少了代码量,并且统一管理,提高代码可维护性!!!

不难受吗?

但是我事后越想越难受,就一个这么丁点代码量的我都得抽取成组件,那我不敢想象以后我的项目组件数会多到什么地步,而且组件粒度太细,确实也增加了后面开发者的负担~

那么有没有办法,可以不抽取成组件呢?我可以在当前组件里去提取吗,而不需要去重新定义一个组件呢?例如下面的效果:

useTemplate 代码实现

想到这,马上行动起来,需要封装一个 useTemplate来实现这个功能:

用的不爽

尽管做到这个地步,我还是觉得用的不爽,因为没有类型提示:

我们想要的是比较爽的使用,那肯定得把类型的提示给支持上啊!!!于是给 useTemplate 加上泛型!!加上之后就有类型提示啦~~~~

加上泛型后的 useTemplate 代码如下:

完整代码

import { defineComponent, shallowRef } from 'vue';

import { camelCase } from 'lodash';
import type { DefineComponent, Slot } from 'vue';

// 将横线命名转大小驼峰
function keysToCamelKebabCase(obj: Record) {
  const newObj: typeof obj = {};
  for (const key in obj) newObj[camelCase(key)] = obj[key];
  return newObj;
}

export type DefineTemplateComponent<
  Bindings extends object,
  Slots extends Record,
> = DefineComponent & {
  new (): { $slots: { default(_: Bindings & { $slots: Slots }): any } };
};

export type ReuseTemplateComponent<
  Bindings extends object,
  Slots extends Record,
> = DefineComponent & {
  new (): { $slots: Slots };
};

export type ReusableTemplatePair<
  Bindings extends object,
  Slots extends Record,
> = [DefineTemplateComponent, ReuseTemplateComponent];

export const useTemplate = <
  Bindings extends object,
  Slots extends Record = Record,
>(): ReusableTemplatePair => {
  const render = shallowRef();

  const define = defineComponent({
    setup(_, { slots }) {
      return () => {
        // 将复用模板的渲染函数内容保存起来
        render.value = slots.default;
      };
    },
  }) as DefineTemplateComponent;

  const reuse = defineComponent({
    setup(_, { attrs, slots }) {
      return () => {
        // 还没定义复用模板,则抛出错误
        if (!render.value) {
          throw new Error('你还没定义复用模板呢!');
        }
        // 执行渲染函数,传入 attrs、slots
        const vnode = render.value({ ...keysToCamelKebabCase(attrs), $slots: slots });
        return vnode.length === 1 ? vnode[0] : vnode;
      };
    },
  }) as ReuseTemplateComponent;

  return [define, reuse];
};                 
            
            
            

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
Windows恶意软件20年“... 在Windows的早期年代,病毒游走于系统之间,偶尔删除文件(但被删除的文件几乎都是可恢复的),并弹...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
着眼MAC地址,解救无法享受D... 在安装了DHCP服务器的局域网环境中,每一台工作站在上网之前,都要先从DHCP服务器那里享受到地址动...
为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...