Vue 之 mixins 和 provide/inject

news2025/7/19 15:50:35

一、mixins

1、简介

mixins 又称 混入,是指将一些可复用的代码(JS、生命周期钩子函数等等)抽离出来,定义成mixins模块,然后混入到多个组件中,从而实现组件间的逻辑代码共享,减少重复代码。当组件使用mixins模块时,mixins模块内部的代码将会被“混合”进组件的代码,代码“混合”逻辑与Vue.extend()相同,具体逻辑下面有讲解。

2、基础使用

mixins模块在组件内是通过与datamountedmethods等钩子函数同级的mixins钩子调用的,该钩子的值是一个数组,里面包含要混入当前组件的mixins模块,这些模块的混入顺序按照mixins钩子数组的排列顺序执行,而且mixins模块内如果包含生命周期钩子函数,则模块内的钩子函数执行顺序先于组件本身的钩子函数。

案例代码:
// 定义一个mixin模块 mixin.js
export default {
  created: function () { console.log('这里是mixin1模块的created') }
}

// 在组件实例中引入并使用定义的mixin模块
// 引入mixin模块
import mixin from "../mixins/mixin";
export default {
  created: function () { console.log('这里是组件本身的created') },
  // 使用mixin模块
  mixins: [mixin]
}
执行结果:

在这里插入图片描述

3、选项合并

​ 当组件与引入的mixins模块含有同名选项时,这些钩子将依照下面的规则进行合并( Vue.extend()的合并逻辑相同):

​ ① mixins模块中的data数据对象,将与组件的data数据进行递归合并,如果存在同名数据,则取组件内的数据值。

​ 当组件引入多个mixins模块时,将按照mixins钩子数组的排列顺序进行合并,如果mixins模块之间存在同名数据,则取排序最后的那个mixins模块的数据值,当然,如果该数据在组件内也存在,则还是取组件内的数据值。

案例代码:
// 定义一个mixin1.js模块
export default {
  data() {
    return {
      a: 1, // 第一个混入模块中的变量a
      b: 11 // 第一个混入模块中的变量b
    }
  },
}
// 定义一个mixin2.js模块
export default {
  data() {
    return {
      a: 2, // 第二个混入模块中的变量a
      b: 22 // 第二个混入模块中的变量b
    }
  },
}

// 在组件实例中引入并使用定义的mixin模块
// 引入两个mixin模块
import mixin1 from "../mixins/mixin1";
import mixin2 from "../mixins/mixin2";
export default {
  data() {
    return {
      b: 0, // 组件内的变量b
    };
  },
  mounted() {
    // 组件内不存在这个变量 两个混入模块存在同名变量 最终值取决于模块引用顺序
    console.log("经过合并后变量a的值是-----", this.a);
    // 组件内存在这个变量 最终值取组件内的值
    console.log("经过合并后变量b的值是-----", this.b);
  },
  // 使用两个mixin模块 注意先后顺序 决定同名变量的最终取值
  mixins: [mixin1, mixin2],
}
执行结果:

在这里插入图片描述

​ ② mixins模块中的钩子函数,将与组件内的同名钩子函数合并成一个数组,依次执行,且mixins模块中的钩子函数在组件同名钩子函数之前调用执行。不同名的钩子函数,依旧按照钩子函数的先后顺序执行。

​ 当组件引入多个mixins模块时,如果mixins模块之间存在同名钩子函数,则会按照mixins钩子数组的排列顺序合并成一个数组,排列顺序与执行顺序相同,排序靠前的mixins模块中的同名钩子函数先执行,排序靠后的后执行,最终才会执行组件本身的同名钩子函数。

案例代码:
// 定义一个mixin1.js模块
export default {
  created: function () { console.log('这里是mixin1模块的created') },
}
// 定义一个mixin2.js模块
export default {
  created: function () { console.log('这里是mixin2模块的created') },
  mounted: function () { console.log('这里是mixin2模块的mounted') },
}

// 在组件实例中引入并使用定义的mixin模块
// 引入两个mixin模块
import mixin1 from "../mixins/mixin1";
import mixin2 from "../mixins/mixin2";
export default {
  created() {
    console.log("这里是组件本身的created");
  },
  // 使用两个mixin模块 注意先后顺序 决定同名钩子函数的执行顺序
  mixins: [mixin1, mixin2],
}
执行结果:

在这里插入图片描述

​ ③ mixins模块中的methodscomponents等值为对象的选项,将会与组件内部的对应选项合并为一个对象,当对象中的键名发生冲突时,则取组件内的键名对应的值。

​ 当组件引入多个mixins模块时,如果mixins模块之间的选项存在同名冲突时,则会按照mixins钩子数组的排列顺序进行覆盖,后面的mixins会覆盖前面的mixins,键名对应的值将取排在最后的mixins模块中对应的值,当然,如果该键名在组件内也存在,则最终还是取组件内的对应的值。

案例代码:
// 定义一个mixin1.js模块
export default {
    methods: {
    test() {
      console.log('这里是mixin1模块methods中的test函数')
    },
    test1() {
      console.log('这里是mixin1模块methods中的test1函数')
    }
  },
}
// 定义一个mixin2.js模块
export default {
  methods: {
    // 如果mixin模块之间存在键名冲突 则以组件中mixin数组的引用顺序为准
    // 取排序最后的键名对应的值
    test1() {
      console.log('这里是mixin2模块methods中的test1函数')
    }
  },
}

// 在组件实例中引入并使用定义的mixin模块
// 引入两个mixin模块
import mixin1 from "../mixins/mixin1";
import mixin2 from "../mixins/mixin2";
export default {
  mounted() {
    this.test();
    this.test1();
  },
  // 使用两个mixin模块 注意先后顺序 决定键名冲突的最终结果
  mixins: [mixin1, mixin2],
  methods: {
    // 如果mixin模块与组件本身键名冲突 则以组件为最终结果
    test() {
      console.log("这里是组件本身methods中的test函数");
    },
  },

}
执行结果:

在这里插入图片描述

总结:

​ 当一个组件使用了多个mixins时,它们的顺序很重要。因为当mixins之间的选项存在冲突时,后面的mixins会覆盖前面的mixins。而且同名钩子函数的执行顺序也取决于多个mixins的顺序。

​ 当组件与mixins的选项存在冲突时,一切以组件为准。

​ 在使用多个mixins时,记得注意命名冲突问题。

4、全局混入

​ 上面我们举的例子都是在组件中依次引入mixins模块,如果我们想要在多个组件中,甚至是所有组件中都引入某个mixins模块,如果在每个组件中都引入一次,就太过繁琐。此时我们可以使用全局混入特性。

​ 全局混入是指将声明的mixins模块,在main.js文件中的全局Vue实例创建之前,通过Vue.mixin()方法,挂载到Vue上。其作用相当于全局引入了mixins模块,会影响到每一个单独创建的Vue页面实例和组件,因此请慎用该特性!!!

​ 如果全局混入的mixins模块中包含生命周期钩子函数,那么该钩子函数将会根据当前页面做包含的Vue实例数量来决定执行的次数,main.js文件的中的 全局Vue实例也算。

案例代码:
// 定义一个allmixin.js 模块
export default {
  created: function () { console.log('这里是全局mixin模块的created') },
  methods: {
    test() {
      console.log('这里是全局mixin模块methods中的test函数')
    }
  },
}

// 在main.js中引入并进行全局混入
import Vue from 'vue'
import App from './App.vue'
import allMixin from './mixins/allMixin'
// 一定要在 new Vue 之前  否则不起作用
Vue.mixin(allMixin)
// 创建全局Vue实例
new Vue({
  render: h => h(App),
}).$mount('#app')

// 此时的页面结构
// mian.js的new Vue -> APP.vue -> test.vue
执行结果:

在这里插入图片描述

5、自定义选项

​ 我们该可以结合this.$options自定义选项来使用全局混入,只有使用自定义选项的组件才会触发相关逻辑,从而局限mixins模块中部分代码的作用范围:

案例代码:
// 定义一个allmixin.js 模块
export default {
  created: function () {
    // 自定义选项 并接收传递的值
    const myOptionValue = this.$options.myOption
    // 输出传递的值
    if (myOptionValue) {
      console.log('-*------', myOptionValue)
    }
  },
}

// 在main.js中引入并进行全局混入
import Vue from 'vue'
import App from './App.vue'
import allMixin from './mixins/allMixin'
// 一定要在 new Vue 之前  否则不起作用
Vue.mixin(allMixin)
// 创建全局Vue实例
new Vue({
  render: h => h(App),
}).$mount('#app')

// 在组件使用全局混入中自定义的选项
export default {
    data() {
        return {}
    },
    // 使用自定义选项
    myOption: "这是我向自定义选项传递的字符串",
}
执行结果:

在这里插入图片描述

mixins模块中的自定义选项在合并时,使用的是默认策略,即简单的覆盖已有的值。当然我们也可以通过Vue.config.optionMergeStrategies来自定义合并逻辑:

// 自定义合并逻辑
Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) {
  // 返回合并后的值
}
// 或者
// 采用现有逻辑 与methods相同
Vue.config.optionMergeStrategies.myOption = Vue.config.optionMergeStrategies.methods

二、provide/inject

1、简介

provide/inject 是Vue在2.2.0版本新增的特性,该特性可以实现祖先组件向其后代组件跨层级传递数据,无论两者中间相隔多少组件层级,相对于传统的props传递方式,减少了传递数据的繁琐操作,提升了代码的可读性和可维护性。该特性与React 框架的上下文特性很相似。

​ 但是过多的使用provide/inject 特性,会增加组件之间的耦合性,降低组件的可复用性,因此使用该特性时要谨慎,不可滥用。而且provideinject的绑定并不是响应式的,传递的数据并不会自动响应数据变化,如果想要响应数据变化,请借助datacomputed

2、provide

provide选项需要在祖先组件中使用,作用是向后代组件传递数据,其选项值为一个对象或一个返回值为对象的函数,对象的属性即为要向其后代组件传递的数据。传递的数据可以是任意类型的数据,如基本类型、对象、函数等等。

provide选项中支持使用 ES2015 Symbols 作为 key,但是只在原生支持 SymbolReflect.ownKeys 的环境下可工作

案例代码:
// provide 的选项值为一个对象
export default {
    data() {
        return {}
    },
    provide: {
        a: "这是祖先组件向后代组件传递的字符串数据",
    	b: {
      		c: "这是祖先组件向后代组件传递的对象数据",
    	},
    	f: function () {
      		console.log("这是祖先组件向后代组件传递的函数数据");
    	},
    },
}

// provide 的选项值为一个返回值为对象的函数
export default {
    data() {
        return {}
    },
    provide() {
    	return {
      		 a: "这是祖先组件向后代组件传递的字符串数据",
        	 b: {
          	 	c: "这是祖先组件向后代组件传递的对象数据",
        	 },
        	 f: function () {
          	 	console.log("这是祖先组件向后代组件传递的函数数据");
        	 },
    	  };
  	  },
}

3、inject

inject选项是在后代组件中使用,作用是接收祖先组件传递的数据,其选项值为一个字符串数组(推荐)或一个对象。更推荐使用字符串数组的形式,其中数组元素对应的是provide对象中的key,通过this.数组字符串元素的形式来访问祖先组件传递的对应数据;如果使用对象形式,则需要通过键值对来接收数据,键名表示当前组件内的访问名称,value为字符串,对应的是provide对象中的key,通过this.键名的形式来访问祖先组件传递的对应数据。

案例代码:
// inject 的选项值为一个字符串数组(推荐)
export default {
    data() {
        return {}
    },
	inject: ["a", "b", "f"],
}

// inject 的选项值为一个对象(不推荐)
export default {
    data() {
        return {}
    },
	inject: {
    	a: "a",
    	b: "b",
    	f: "f",
  	},
}

// 在后代组件中通过inject接收传递的数据之后 调用传递的数据
mounted() {
    console.log("inject接收的祖先组件传递过来的字符串数据-----", this.a);
    console.log("inject接收的祖先组件传递过来的对象数据-----", this.b);
    console.log("inject接收的祖先组件传递过来的函数数据-----", this.f);
},
执行结果:

在这里插入图片描述

4、进阶知识

① 在Vue的2.2.1版本之后,后代组件中通过inject接收传递的数据,会在propsdata初始化之前得到,因此我们可以使用inject中的数据给propsdata中的数据设置默认值。

export default {
    inject: ['foo'],
    props: {
      a: {
          default() {
              return this.foo
          }
      }
    },
    data() {
        return {
            b: this.foo
        }
    },
}

② 在Vue的2.5.0 版本之后,我们可以给选项值为对象形式的inject中的数据设置默认值,使其在祖先组件中变成可选项,即在不传递数据时,后代组件依旧能正常工作。from属性设置数据源,default属性设置默认值。

props设置默认值类似,如果直接将非原始值(复杂数据类型)作为默认值,那么它将成为所有子组件实例之间共享的引用,相互之间会产生影响。因此我们需要对非原始值(复杂数据类型)使用一个工厂方法,以便于每次使用默认值时都获得一个新的副本,而不是共享同一个引用。

export default {
    inject: {
        // 当组件内名称与祖先组件的key相同时,可省略form属性
    	foo: { default: 'foo' },
        // 当组件内名称与祖先组件的key不同时,需要通过form属性指定对应的数据
        bar: {
            from: 'barFather',
            default: 'bar'
        },
        // 对复杂数据类型使用一个工厂方法
        arr: {
            from: 'arr',
            default: () => [1, 2, 3]
        },
  	},
    data() {
        return {}
    },
}

三、参考资料

Vue官方文档

ChatGPT

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

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

相关文章

Nginx正向代理、反向代理,动静分离

目录 正向代理 反向代理 动静分离 正向代理 正向代理最大的特点是客户端非常明确要访问的服务器地址;服务器只清楚请求来自哪个代理服务器,而不清楚来自哪个具体的客户端;正向代理模式屏蔽或者隐藏了真实客户端信息。 反向代理 客户端给服…

C#(四十六)之基于流的文件操作(FileStream)

FileStream类属性和方法 属性 CanRead 指示当前文件流是否支持读取 CanWrite 指示当前文件流是否支持写入 CanSeek 指示当前文件流是否支持查找 IsAsync FileStream是同步打开还是异步打开 Length 流的长度(字节数) CanTimeOut 当前文件流是否可以…

【C++进阶】哈希表

文章目录 前言哈希概念哈希冲突哈希函数哈希冲突解决一.闭散列解决哈希冲突1. 线性探测2.二次探测 二、闭散列的实现1.准备2.闭散列插入3.闭散列查找4.闭散列删除 三、闭散列完整源码四、开散列解决哈希冲突1.开散列概念2.哈希桶的结构3.哈希桶的插入4.哈希桶的查找5.哈希桶的删…

学习多线程

1、学习线程池 1.1jdk中的线程池 推荐使用ThreadPoolExecutor 1.1.1先了解参数的意义 首先看下ThreadPoolExecutor的构造函数 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueu…

对话e签宝,解读 SaaS圈“头部玩家”的增长故事

可以肯定的是&#xff0c;未来3年&#xff0c;整个中国SaaS市场会逐步确认船票分配。穿越周期&#xff0c;告别不确定的三年&#xff0c;给幸存下来的企业上了淋漓生动的一课。在新的起点上&#xff0c;SaaS也需要一种新的打开方式。谁扎得牢、想得透、做的好、跑得快&#xff…

Docker数据卷和数据卷容器应用

学习目的 掌握Docker数据卷和数据卷容器使用方法。 学习准备 要求实验主机能够连接外网&#xff0c;已经正确安装Docker&#xff0c;并关闭防火墙和selinux。 学习步骤 步骤1&#xff1a;创建数据卷 &#xff08;1&#xff09;创建容器&#xff0c;为容器添加一个数据卷&…

基于树莓派4B与STM32的智能门禁系统项目(代码开源)

前言&#xff1a;本文为手把手教学嵌入式经典项目——智能门禁项目&#xff0c;本次项目采用 树莓派4B 与 STM32F103C8T6 进行联合开发。项目充分发挥各自 CPU 的优势与长处&#xff0c;将人脸识别的大计算量任务给树莓派4B&#xff0c;将门禁系统的控制部分交给 STM32 进行处理…

外包干了2个月,技术退步明显...

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入湖南某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

举个栗子!Quick BI 技巧(1):创建趋势折线图

自 2016 年以来&#xff0c;优阅达《举个栗子》内容专栏已陪伴众多企业用户高效工作&#xff0c;解决真实业务场景中的用数问题。 2023 年&#xff0c;全新系列《举个栗子&#xff01;Quick BI 技巧》出炉啦~ 优阅达期待能够持续分享经验&#xff0c;帮助用户发现更多 Quick …

剑指 Offer. 二叉树中和为某一值的路径

给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum 22 输出&…

ATA3000系列功率放大器——水声信号中的典型应用

ATA3000系列功率放大器——水声信号中的典型应用 水声领域介绍&#xff1a; 主动声纳是通过声纳本身发射声波&#xff0c;根据声信号反射回波来发现目标&#xff0c;并测定目标的位置和运动参数。水声信号发射机在主动声纳设备中是非常重要的组成部分&#xff0c;其产生特定形式…

YoloV5/YoloV7改进---注意力机制:引入瓶颈注意力模块BAM,对标CBAM

目录 1.BAM介绍 2.BAM引入到yolov5 2.1 加入common.py中&#xff1a; 2.2 加入yolo.py中&#xff1a; 2.3 yolov5s_BAM.yaml 1.BAM介绍 论文&#xff1a;https://arxiv.org/pdf/1807.06514.pdf 摘要&#xff1a;提出了一种简单有效的注意力模块&#xff0c;称为瓶颈注意力模块…

【数据仓库】Apache Doris介绍

Apache Doris介绍 Apache Doris应用场景 Apache Doris核心特性 Apache Doris架构 Doris数据模型三种 Aggregate模型介绍 Uniq模型介绍 在某些多维分析场景下,用户更关注的是如何保证Key的唯一性Key 唯一性约束。因此&#xff0c;我们引入了 Unig 的数据模型。该模型本质上是聚…

微信如何创建自己的小程序?

微信如何创建自己的小程序&#xff1f;微信小程序成为了很多商家、企业甚至是个人在互联网中的营销工具&#xff0c;微信小程序基本上可以说是属于必备工具。那么微信如何创建自己的小程序呢&#xff1f;下面一起来给大家说说。 一、注册小程序账号 微信如何创建自己的小程序…

12.JavaWeb-Node.js

1.Node.js的概念 传统的Web服务器中&#xff0c;每个请求都会创建一个线程&#xff0c;这会导致线程数的增加&#xff0c;从而影响服务器的性能和扩展性&#xff0c;Ryan Dahl借助Chrome的V8引擎提供的能力实现了Node.js——可以在服务端运行的JavaScript&#xff08;可以把Nod…

win下实现Linux的tab自动补全

声明 &#xff1a;如果不是确定的话 注册表这个东西不建议更改 如果更改的话建议先备份系统 以防意外 1.找到注册表编辑器 2. 展开HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor 3.找到Completion Char 双击 把橙色的数值改成9 4.重新打开cmd 就可以了 参考文章…

【Python数据处理】-Pandas笔记

Python数据处理-Pandas笔记 &#x1f4dd; 基本概念 Pandas是一个强大的Python数据处理库&#xff0c;它提供了高效的数据结构和数据分析工具&#xff0c;使数据处理变得简单而快速。本篇笔记将介绍Pandas中最常用的数据结构——Series和DataFrame&#xff0c;以及数据处理的…

48. Compose自定义绘制日历-2

这次的实现方式完全改了&#xff0c;感觉最初的想法对切换周历模式比较难实现&#xff0c; 现在是把月历和周历 同时生成&#xff0c;动态切换。 待优化的&#xff1a;切换的时候 闪动没那么丝滑。 还有另一种实现方案 &#xff1a; 只生成当前月份 和前后月份三组数据&#x…

HTML5网页设计小案例:逸仙园茶馆招聘启事网页的设计

前言&#xff1a; 今天分享的逸仙园茶馆招聘启事网页的设计是本专栏的第一篇博客&#xff0c;也是我学习了几个小时知识点后写的实战小案例。我有个想法&#xff0c;想以逸仙园茶馆为灵感不断优化改进代码与想法设计一套与茶叶有关的的精美网页 逸仙园茶馆招聘启事网页的设计案…

【动态规划上分复盘】这是你熟悉的地下城游戏吗?

欢迎 前言一、动态规划五步曲二、地下城游戏题目分析思路&#xff1a;动态规划具体代码如下 总结 前言 本文讲解关于动态规划思路的两道题目。 一、动态规划五步曲 1.确定状态表示&#xff08;确定dp数组的含义&#xff09;2.确定状态转移方程&#xff08;确定dp的递推公式&a…