目录
系列文章
mixin混入
extends继承
provide提供/inject注入
一、系列文章
【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)
二、mixin混入
mixins 选项接收一个混入对象的数组。这些混入对象可以像正常的实例对象一样包含实例选项,这些选项将会被合并到最终的选项中,使用的是和 Vue.extend() 一样的选项合并逻辑。也就是说,如果你的混入包含一个 created 钩子,而创建组件本身也有一个,那么两个函数都会被调用。
Mixin 钩子按照传入顺序依次调用,并在调用组件自身的钩子之前被调用。
1、作用
混入就是复制
减少data、methods、钩子的重复
2、场景描述
假设我们需要在每个组件上添加name和time。在created、destroyed时,打出提示,并给出存活时间。
一共有五个组件,请问怎么做?
- 做法1:给每个组件添加data和钩子,重复5次
- 做法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小结:
三、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的。
传引用可以(就是传一个对象,改对象里面的数据)。但是不推荐,容易失控。