vue利用provide和inject做套娃组件设计

news2024/12/28 5:10:30

provide和inject原来用的不多,只是见人引用axios的时候在main.js里使用provide来注入

app.provide('axios', axios)

这样,在所有的vue文件里都可以使用inject来获取这个注入的axios

const axios = inject("axios");

这种利用provide和inject做全局变量的注入是比较常见的用法,后来我也一直使用这种方式进行axios的引用。

直到我看到ElementUI Tabs的做法后,才发现provide和inject还可以用到多级组件的协同上。

我们知道Tabs是由多个TabPane组成的,Tabs要负责显示标题,提供统一的切换功能,TabPane提供具体单个内容的展示

 上面这个就是由两个TabPane组成的Tabs

这个两级关系的复杂关系在于,在具体实现的时候,每个pane的标题等信息是在pane里指定的,但是具体的展示实现,却是在父级的tabs里面去提供一个切换按钮。类似这样的结构(APIcat/ui/src/plugins/tabs.vue):

<div class="tabs-wrapper">
    <ul class="tabs-labels">
      <li v-for="(label, i) in tabs" :key="label?.name || i" @click="selectTab(label.name)"
        :class="{ active: state.selectedIndex == (label?.name || i) }">
        {{
    label?.label
        }}</li>
    </ul>
    <div class="contents">
      <slot></slot>
    </div>
  </div>

父级通过一个独立的ul控件实现按钮和选中状态的展示,并通过另外一个div来包装子级内容。

如果子级的TabPane无法动态向父级提供数据的话,能想到的解决方案大概有两个:

一般方案1:使用template渲染子级

父级改为这样:

<div class="tabs-wrapper">
    <ul class="tabs-labels">
      <li v-for="(label, i) in tabs" :key="label?.name || i" @click="selectTab(label.name)"
        :class="{ active: state.selectedIndex == (label?.name || i) }">
        {{
    label?.label
        }}</li>
    </ul>
    <div class="contents">
      <slot v-for="pane,index in panes" :key="index" :name="'pane-'+index"></slot>
    </div>
  </div>

 使用时,不适用tab-pane组件,改为在tabs使用template-1,2,3来提供具体内容

<tabs :panes="['统计简报','客户报告']">
  <template #pane-0></template>
  <template #pane-1></template>
</tabs>

这样的写法能解决问题,但是缺乏子层造成子层不能做更多自定义属性的设置,同时,总的观感也差了很多,理解起来也更为费劲,远比“在tabs再套一层tab-pane”来的复杂。

一般方案2:使用props向下层传递

在调用方中进行tab-pane和tabs的数据传递

<tabs :panes="panes">
  <tab-pane label="统计简报" @add-pane="pane=>panes.push(pane)"></tab-pane>
  <tab-pane label="客户报告" @add-pane="pane=>panes.push(pane)"></tab-pane>
</tabs>

tab-pane通过emit向调用方传递pane的具体信息,调用方再通过tabs的props把具体的pane传递给父级。

这样的话,信息还需要调用页码来处理,整体的模块设计的独立性就破坏了。

优化方案:使用provide和inject

使用provide和inject,为我们提供了一套优化方案:

第一步,在tabs定义一个状态参数,利用reactive将其设置为响应式参数,再通过provide提供

<script setup>
import { provide, reactive } from "vue";

const state = reactive({
  selectedIndex: null,
  count: 0,
  labels: [], //可选,子层上传自己的名称
});

provide("tabsProvider", state)

</script>

状态里主要是两个属性:

  1. selectedIndex表示当前的选中面板的index
  2. count表示tab的总数

第二步,在tab-pane子级中,通过inject获取到这个状态参数,并在onBeforeMount回调中更新该值

import { inject, onBeforeMount, watch, ref } from "@vue/runtime-core";

const tabs = inject("tabsProvider");
const no = ref(-1);

onBeforeMount(() => {
  //设置一个默认的选中项
  if (tabs.selectedIndex == null) {
    tabs.selectedIndex = tabs.count
  }
  //把目前的count设置为自己的序号
  no.value = tabs.count;
  // 向上层设置标签内容
  tabs.labels[no.value]=props.label;
  //如果预设了选中index并且和自己的序号相同,则显示自己的内容
  if (tabs.selectedIndex == no.value) {
    isActive.value = true;
  }
  
  tabs.count++;
});

//监听选中index的变化情况,根据情况设置显示或隐藏
watch(
  () => tabs.selectedIndex,
  () => {
    isActive.value = no.value === tabs.selectedIndex;
    if (isActive.value) {
      emits("shown")
    }
  }
);

这种方案的巧妙之处在于他充分利用了dom加载顺序,在onBeforeMount回调中实际利用了tab-pane顺序加载的过程,实现了父级计数和子级序号获取,并依靠provide提供的响应式全局状态变量实现了对选中状态的子父级传递。

上面的示例基本完成了子级的按选中状态设置,和tab-pane的标题属性传递给父级的主要设置功能。

这样设计的组件就可以完美的达到两级自定义组件协同的效果:

<tabs>
  <tab-pane label="统计简报"></tab-pane>
  <tab-pane label="客户报告"></tab-pane>
</tabs>

官方文档关于provide和inject的介绍,主要介绍了在多级父子关系中的全局穿透能力:

依赖注入 | Vue.js (vuejs.org)icon-default.png?t=MBR7https://cn.vuejs.org/guide/components/provide-inject.html这当然是provide的重要功能之一,但是缺了本文介绍的在组件中和响应式配合达到动态协同效果这样的奇妙用法介绍,因此今天补充一文介绍这一特性,如果有需要参考的,可以参考我们的开源项目APIcat的相关代码,在ui/src/plugins里面,基本是Element-UI的简化版本,主要为了节省代码大小,自己做了一个。

并祝大家新年快乐!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/173299.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(考研湖科大教书匠计算机网络)第一章概述-第五节3:计算机网络体系结构之相关专业术语

文章目录一&#xff1a;实体二&#xff1a;协议三&#xff1a;服务四&#xff1a;协议数据单元本节对应视频 【计算机网络微课堂&#xff08;有字幕无背景音乐版&#xff09;】&#xff1a;1.6 计算机网络体系结构&#xff08;4&#xff09;—专用术语 注意&#xff1a;本节内容…

2023MyBatis精选面试题2(8道)

一. MyBatis的框架架构设计是怎么样的这张图从上往下看。MyBatis的初始化&#xff0c;会从mybatis-config.xml配置文件&#xff0c;解析构造成Configuration这个类&#xff0c;就是图中的红框。1. 加载配置&#xff1a;配置来源于两个地方&#xff0c;一处是配置文件&#xff0…

【阅读笔记】《重构》 第一二章

第一章 重构&#xff0c;第一个案例 编译器不会在乎代码好不好看&#xff0c;都是正常运行的。但人在乎&#xff0c;差劲的系统很难修改&#xff0c;因为很难找到修改点&#xff0c;导致程序员很有可能犯错&#xff0c;从而引入bug 重构的第一步 得为即将修改的代码建立一组…

自动化测试Selenium【基础篇一】

自动化测试Selenium【基础篇一】&#x1f34e;一.什么是自动化测试&#x1f352;1.1 自动化测试介绍&#x1f352;1.2 单元测试&#x1f352;1.3 接口自动化&#x1f352;1.4 UI自动化&#x1f352;1.5 为什么选择selenium作为我们的web自动化工具?&#x1f352;1.6什么是驱动…

DaVinci:限定器 - RGB

调色页面&#xff1a;限定器Color&#xff1a;Qualifier限定器 - RGB Qualifier - RGB根据像素的三原色通道&#xff08;红、绿、蓝&#xff09;的值来选择画面上的对应区域&#xff0c;从而限制节点调色的范围。限定器 - RGB 根据指定的各个原色通道的色阶范围来选择连续的近似…

Java 对象处理流(ObjectOutputStream\ObjectInputStream)

文章目录前言什么是对象流&#xff1f;基本介绍ObjectOutputStreamObjectInputStream对象处理流的使用细节前言 处理流&#xff1a;是对一个已存在的流进行处理和封装&#xff0c;通过所封装的流的功能调用实现对数据的操作。而处理流中也有不同的分类&#xff0c;此片介绍的是…

C规范编辑笔记(十一)

往期文章&#xff1a; C规范编辑笔记(一) C规范编辑笔记(二) C规范编辑笔记(三) C规范编辑笔记(四) C规范编辑笔记(五) C规范编辑笔记(六) C规范编辑笔记(七) C规范编辑笔记(八) C规范编辑笔记(九) C规则编辑笔记(十) 正文&#xff1a; 因为太久没有更新了&#xff0c;今天就…

Elasticsearch7.8.0版本高级查询—— 多关键字精确查询文档

目录一、初始化文档数据二、多关键字精确查询文档2.1、概述2.2、示例一、初始化文档数据 在 Postman 中&#xff0c;向 ES 服务器发 POST 请求 &#xff1a;http://localhost:9200/user/_doc/1&#xff0c;请求体内容为&#xff1a; {"name":"张三","…

干货 | 算力网络节点可信度评估和安全管控方案

以下内容整理自清华大学《数智安全与标准化》课程大作业期末报告同学的汇报内容。第一部分&#xff1a;算力网络第二部分&#xff1a;可信度评估一、可信度评估在整个算力网络处理任务的实施流程中&#xff0c;不同部分有不同可信度评估的方法&#xff0c;具体包括&#xff1a;…

忙活了一年的开源社区,终于赶上了春节前的末班车!

随着春节的临近&#xff0c;忙碌了一年的小伙伴们&#xff0c;是不是都已经踏上了回乡的列车&#xff1f;我呢也终于在春节前&#xff0c;完成了 HelloGitHub.com 的重构。HelloGitHub.com 是我在 2017 年的时候用 FlaskjQuery 开发的网站&#xff0c;最初的想法很简单就是为了…

MySQL34道练习题

1、取得每个部门最高薪水的人员名称&#xff08;要求显示部门编号、人员名称和薪资&#xff09; 第一步&#xff1a;查询每个部门的最高薪资 SELECT deptno,max(sal) as maxsal FROM emp GROUP BY deptno; 第二步&#xff1a;把上面查询结果当做临时表t&#xff0…

Pagoda panel宝塔面板

宝塔面板是干什么的&#xff1f;有哪些典型的功能作用?宝塔面板是开发的服务器管理工具&#xff0c;是一家专业从事服务器相关软件和服务研发的公司。宝塔的愿景是让用户更容易使用服务器。宝塔面板是一款服务器管理软件&#xff0c;支持Windows和Linux系统&#xff0c;服务器…

【SpringCloud18】SpringCloud Alibaba Nacos服务注册和配置中心

1.Nacos简介 1.1为什么叫Nacos 前四个字母分别为Naming和Configuration的前两个字母&#xff0c;最后的s为Service 1.2 是什么 一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台Nacos:Dynamic Naming and Configuration ServiceNacos就是注册中心 配置中心…

简单双向LSTM模型实战项目

前言 数据来自于kaggle比赛Ventilator Pressure Prediction&#xff0c;数据背景介绍请看官方说明代码来自于当前排名第一的团队Shujun, Kha, Zidmie, Gilles, B&#xff0c;他们在获得第一名的成绩以后发了一篇博客&#xff0c;提供了他们在比赛中使用的模型&#xff0c;包括…

[C/C++]对象指针

对象指针 1.对象指针变量 和基本类型变量一样&#xff0c;对象在初始化之后也会在内存中占有若干字节的内存空间。因此在程序中&#xff0c;我们可以通过对象名或对象的地址来访问该对象。对象指针变量就是一个用于保存对象在内存中存储空间首地址的指针变量&#xff0c;它与普…

Python Matplotlib 中如何用 plt.savefig 存储图片

目录前言正文前言 plt.show()展示图片的时候&#xff0c;截图进行保存&#xff0c;图片不是多么清晰 如何保存高清图也是一知识点 函数包名&#xff1a;import matplotlib.pyplot as plt 正文 主要功能&#xff1a;保存绘制数据后创建的图形。使用此方法可以将创建的图形保…

MobaXterm的安装与使用

安装: 分为开箱即用和逐步配置版本,这里选择开箱即用的版本,网址如下: MobaXterm Xserver with SSH, telnet, RDP, VNC and X11 - Home EditionFree X server for Windows with tabbed SSH terminal, telnet, RDP, VNC and X11-forwarding - Home Editionhttps://mobaxterm.m…

Java面试题(2),这是一篇会一直更新的博客!欢迎大家留言~

Java面试题第二篇1. 并发的三大特性2、线程池、解释线程池参数3、BeanFactory和ApplicationContext有什么区别&#xff1f;4、描述一下Spring Bean的生命周期5、Spring的几种Bean的作用域6、单例Bean是线程安全的吗&#xff1f;7、Spring框架用到了哪些设计模式8、Spring事务的…

Magic Leap CEO:我们的目标是再回到消费级AR市场

相比于几年前&#xff0c;Magic Leap在CEO换人后经历了一系列变化&#xff0c;其中更明显的变化&#xff0c;就是这家公司似乎变得更低调、更务实。此前&#xff0c;Magic Leap的内容库包括了大量趣味、娱乐的AR应用&#xff0c;而现在则主要改成B端解决方案&#xff0c;随着ML…

小程序商城源码代码分析

微信平台开发直播插件后&#xff0c;许多传统的商家开始加入小程序商城源码开发的队伍中&#xff0c;以此来自主管理自己的产品。下文&#xff0c;小编将为大家盘点一下小程序商城源码的优势&#xff0c;以及代码分析。 一、小程序商城源码有什么优势&#xff1f; 1、拓展私域…