【Vue全解7】options之组合(mixin、extends、provide/inject)

26 篇文章 0 订阅
11 篇文章 0 订阅

目录

  • 系列文章
  • mixin混入
  • extends继承
  • provide提供/inject注入

一、系列文章

【Vue 全解 0】Vue 实例
【Vue 全解 1】构造选项 options 之 data
【Vue 全解 2】Vue 模板语法摘要
【Vue 全解 3】Vue 的 data 代理和数据响应式
【Vue 全解 4】options 之生命周期钩子(created、mounted、updated、destroyed)
【Vue 全解 5】options 之 DOM(el、template、render)
【Vue 全解 6】options 之资源(directive、filter、components)和修饰符
【Vue 全解 7】options 之组合(mixin、extends、provide/inject)
【Vue 全解 8】Vue 表单输入绑定 v-model

二、mixin混入

mixins 选项接收一个混入对象的数组。这些混入对象可以像正常的实例对象一样包含实例选项,这些选项将会被合并到最终的选项中,使用的是和 Vue.extend() 一样的选项合并逻辑。也就是说,如果你的混入包含一个 created 钩子,而创建组件本身也有一个,那么两个函数都会被调用。

Mixin 钩子按照传入顺序依次调用,并在调用组件自身的钩子之前被调用。

1、作用
  • 混入就是复制
  • 减少data、methods、钩子的重复
2、场景描述

假设我们需要在每个组件上添加name和time。在created、destroyed时,打出提示,并给出存活时间。

  • 一共有五个组件,请问怎么做?
  1. 做法1:给每个组件添加data和钩子,重复5次
  2. 做法2:使用mixin减少重复。
  • 做法1:重复5次

子组件1:child1.vue


<template>
	<div>child1</div>
</template>
<script>
	export default{
		data(){
			return {
				name:'child1',
				time_birth:undefined,
				time_dead:undefined
			}
		},
		created(){
			this.time_birth=new Date();
			console.log(this.name+'出生了');
		},
		beforeDestroy(){
			this.time_dead=new Date();
			console.log(`${this.name}死了,共存活了${this.time_dead-this.time_birth}ms`);
		}
	}
</script>

子组件2:child2.vue

<template>
	<div>child2</div>
</template>
<script>
	export default{
		data(){
			return {
				name:'child2',
				time_birth:undefined,
				time_dead:undefined
			}
		},
		created(){
			this.time_birth=new Date();
			console.log(this.name+'出生了');
		},
		beforeDestroy(){
			this.time_dead=new Date();
			console.log(`${this.name}死了,共存活了${this.time_dead-this.time_birth}ms`);
		}
	}
</script>

子组件3:child3.vue

<template>
	<div>child3</div>
</template>
<script>
	export default{
		data(){
			return {
				name:'child3',
				time_birth:undefined,
				time_dead:undefined
			}
		},
		created(){
			this.time_birth=new Date();
			console.log(this.name+'出生了');
		},
		beforeDestroy(){
			this.time_dead=new Date();
			console.log(`${this.name}死了,共存活了${this.time_dead-this.time_birth}ms`);
		}
	}
</script>

子组件4:child4.vue

<template>
	<div>child4</div>
</template>
<script>
	export default{
		data(){
			return {
				name:'child4',
				time_birth:undefined,
				time_dead:undefined
			}
		},
		created(){
			this.time_birth=new Date();
			console.log(this.name+'出生了');
		},
		beforeDestroy(){
			this.time_dead=new Date();
			console.log(`${this.name}死了,共存活了${this.time_dead-this.time_birth}ms`);
		}
	}
</script>

子组件5:child5.vue

<template>
	<div>child5</div>
</template>
<script>
	export default{
		data(){
			return {
				name:'child5',
				time_birth:undefined,
				time_dead:undefined
			}
		},
		created(){
			this.time_birth=new Date();
			console.log(this.name+'出生了');
		},
		beforeDestroy(){
			this.time_dead=new Date();
			console.log(`${this.name}死了,共存活了${this.time_dead-this.time_birth}ms`);
		}
	}
</script>

主组件:App.vue

<template>
	<div id="app">
		<child1 v-if='child1visible'/>
		<button v-on:click='!child1visible'>toggle1</button>
		<hr>
		<child2 v-if='child2visible'/>
		<button v-on:click='!child2visible'>toggle2</button>
		<hr>
		<child3 v-if='child3visible'/>
		<button v-on:click='!child3visible'>toggle3</button>
		<hr>
		<child4 v-if='child4visible'/>
		<button v-on:click='!child4visible'>toggle4</button>
		<hr>
		<child5 v-if='child5visible'/>
		<button v-on:click='!child5visible'>toggle5</button>
	</div>
</template>
<script>
import child1 from './child1.vue';
import child2 from './child2.vue';
import child3 from './child3.vue';
import child4 from './child4.vue';
import child5 from './child5.vue';
export default {
	name:'App',
	data(){
		return {
			child1visible:true,
			child2visible:true,
			child3visible:true,
			child4visible:true,
			child5visible:true,
		}
	},
	compoents:{
		child1,
		child2,
		child3,
		child4,
		child5
	}
}
</script>

做法1小结:代码重复太多,基本上每个组件都在复刻第一个组件的样式,这样下来,代码的维护性是十分低的。万一有一天要改需求了怎么办?又倒回去重新修改5次吗?5次并不是真正的5次,万一是10次,100次呢?很显然,这种做法并不可取。接下来来看另外一种做法。

  • 做法2:使用mixin减少重复

1、先将每个子组件的输出部分(相似的代码)提出来放在一个公共的文件中,我们将它放在Mixins文件夹中吧,取名为public.js。注意要将其导出

const public = {
	data(){
			return {
				/* 每个组件的名字不同,用另外的方法赋值。即每个组件自己带上自己的名字即可。 */
				name:undefined,
				time_birth:undefined,
				time_dead:undefined
			}
		},
		created(){
			if(!this.name){
				throw new Error('need name');
			}
			this.time_birth=new Date();
			console.log(this.name+'出生了');
		},
		beforeDestroy(){
			this.time_dead=new Date();
			console.log(`${this.name}死了,共存活了${this.time_dead-this.time_birth}ms`);
		}
}
export default public;

2、子组件1:child1.vue – 其他4个组件也是类似的操作。这里就不展示了,最主要的是突出重复5次的操作是真的很傻,而且代码维护也很难。

<template>
	<div>child1</div>
</template>
<script>
	/* 首先导入公共部分的js内容 */
	import public from './Mixins/public.js';
	export default{
		data(){
			return {
				/* 其他组件就写自己组件的名字即可 */
				name:'child1',
			}
		}
		/* 使用导入的public模块 */
		mixins:['public'],
	}
</script>

主组件:App.vue – 主组件的内容全都是必要的,没有办法再精简了。

<template>
	<div id="app">
		<child1 v-if='child1visible'/>
		<button v-on:click='!child1visible'>toggle1</button>
		<hr>
		<child2 v-if='child2visible'/>
		<button v-on:click='!child2visible'>toggle2</button>
		<hr>
		<child3 v-if='child3visible'/>
		<button v-on:click='!child3visible'>toggle3</button>
		<hr>
		<child4 v-if='child4visible'/>
		<button v-on:click='!child4visible'>toggle4</button>
		<hr>
		<child5 v-if='child5visible'/>
		<button v-on:click='!child5visible'>toggle5</button>
	</div>
</template>
<script>
import child1 from './child1.vue';
import child2 from './child2.vue';
import child3 from './child3.vue';
import child4 from './child4.vue';
import child5 from './child5.vue';
export default {
	name:'App',
	data(){
		return {
			child1visible:true,
			child2visible:true,
			child3visible:true,
			child4visible:true,
			child5visible:true,
		}
	},
	compoents:{
		child1,
		child2,
		child3,
		child4,
		child5
	}
}
</script>

mixin小结:

  • 选项会智能合并就是当公共部分有created函数同时主组件也有created函数时的情况。
  • Vue.mixin用到时看文档,不推荐使用

三、extend继承

允许声明扩展另一个组件 (可以是一个简单的选项对象或构造函数),而无需使用 Vue.extend。这主要是为了便于扩展单文件组件。

这和 mixins 类似。

  • 借用上面的例子

创建一个公共的 js 文件,用于组件使用它来进行继承

import Vue from 'vue';	//导入vue
const Myvue = Vue.extend({
	data(){
			return {
				/* 每个组件的名字不同,用另外的方法赋值。即每个组件自己带上自己的名字即可。 */
				name:undefined,
				time_birth:undefined,
				time_dead:undefined
			}
		},
		created(){
			if(!this.name){
				throw new Error('need name');
			}
			this.time_birth=new Date();
			console.log(this.name+'出生了');
		},
		beforeDestroy(){
			this.time_dead=new Date();
			console.log(`${this.name}死了,共存活了${this.time_dead-this.time_birth}ms`);
		}
});
export default Myvue;

子组件:其他的操作都是类似的,主组件的内容也没有改变

<template>
	<div>child1</div>
</template>
<script>
	/* 首先导入公共部分的js内容 */
	import Myvue from './Myvue.js';
	export default{
		extends: Myvue,
		data(){
			return {
				/* 其他组件就写自己组件的名字即可 */
				name:'child1',
			}
		}		
	}
</script>

extend继承小结:extends是比mixins更抽象一点的封装。如果你嫌写mixins烦,可以考虑使用extend。但是实际上用mixins就够了。


四、provide/inject

祖先栽树(provide),后人乘凉(inject)

需求 1 :一键换肤功能,默认为蓝色,可以切换为红色。文字大小,默认正常,可以改大或者改小。

  • 代码演示

主组件

<template>
	<div v-bind:class="`app theme-${themeName} fontSize-${fontSizeName}`">
		<child1/>
		<button>toggle1</button>
		<hr>
		<child2/>
		<button>toggle2</button>
		<hr>
		<child3/>
		<button >toggle3</button>
		<hr>
		<child4/>
		<button>toggle4</button>
		<hr>
		<child5/>
		<button>toggle5</button>
	</div>
</template>
<script>
import child1 from './child1.vue';
import child2 from './child2.vue';
import child3 from './child3.vue';
import child4 from './child4.vue';
import child5 from './child5.vue';
export default {
	name:'App',
	provide(){
		return {
			themeName:this.themeName,
			changeTheme:this.changeTheme,
			changeFontSize:this.changeFontSize
		}
	},
	data(){
		return {
			themeName:'blue',	//'red'
			fontSizeName:'normal'	//'big' | 'small'
		}
	},
	methods:{
		changeTheme(){
			if(this.themeName==='blue'){
				this.themeName='red';
			}
			else{
				this.themeName='blue';
			}
		},
		changeFontSize(name){
			if(name in ['normal','big','small']){
				this.fontSizeName=name;
			}
		}
	}
	compoents:{
		child1,
		child2,
		child3,
		child4,
		child5
	}
}
</script>
<style>
	.app.theme-blue button{
		background:blue;
		color:white;
	}
	.app.theme-blue{
		color:darkblue;
	}
	.app.theme-red button{
		background:red;
		color:white;
	}
	.app.theme-red{
		color:darkred;
	}
	.app.fontSize-normal {
		font-size: 16px;
	}
	.app.fontSize-big {
		font-size: 24px;
	}
	.app.fontSize-small{
		font-size: 8px;
	}
	.app button {
		/* 让按钮继承字体的大小 */
		font-size:inherit;
	}
</style>

子组件:切换主题的按钮–文件名为:ChangeThemeButton.vue

<template>
	<div>
		<button v-on:click='x'>当前主题色:{{themeName}}换肤</button>
		<hr>
		<button v-on:click='changeFontSize("big")'>大字</button>
		<hr>
		<button v-on:click='changeFontSize("small")'>小字</button>
		<hr>
		<button v-on:click='changeFontSize("normal")'>正常字</button>
	</div>
</template>
<script>
	export default {
		inject:['themeName','changeTheme','changeFontSize'],//注意themeName这里只是拿到一个字符串的复制品,并不能修改到主组件的内容。需要传送一个主组件能修改的函数changeTheme。
		methods:{
			x(){
				this.changeTheme;
			}
		}
	}
</script>

子组件:其他5个都是这样类似

<template>
	<div>
	child1
	/* <change-theme-button> 也可以*/
	<ChangeThemeButton/>
	</div>
</template>
<script>
	import ChangeThemeButton from './ChangeThemeButton.vue';
	export default {
		compoents:{ChangeThemeButton}
	}
</script>

provide/inject总结:

  • 适用于大范围的data和methods共用
  • 注意:不能只传themeName不传changeTheme,因为App.vue里面的themeName的值是被复制给provide的。
  • 传引用可以(就是传一个对象,改对象里面的数据)。但是不推荐,容易失控。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue中的provide/inject实现了跨组件的通信,但是默认情况下,提供的数据并不是响应式的。这是因为provide/inject的设计初衷并不是为了实现响应式数据的传递。引用 然而,如果你传入一个可监听的对象作为provide的值,那么该对象的属性仍然是可响应的。这意味着当对象的属性发生变化时,所有使用该属性的组件也会更新。引用 例如,在Vue组件A中使用provide提供一个响应式对象,然后在组件B中使用inject获取该对象。如果在组件A中修改了该对象的属性,组件B将会自动更新。引用 需要注意的是,只有provide提供的对象本身是响应式的,而不是该对象的嵌套属性。也就是说,如果在provide中传入的对象中的属性发生变化,那么只有直接使用这些属性的组件才会更新,而不是使用了整个对象的组件。引用<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Vue中的Provide/Inject 实现响应式数据](https://blog.csdn.net/weixin_43459866/article/details/114691818)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [Vue Provide / Inject 详细介绍(跨组件通信、响应式变化、版本变化)](https://blog.csdn.net/qq_41809113/article/details/122071958)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值