1.1 Props
components
├── Parent.vue // 父
└── Son1.vue // 子1
父組件中使用子組件。
// Parent.vue
<template>
<div>
父組件: {{num}}
<Son1 :parentNum="num"></Son1>
</div>
</template>
<script>
import Son1 from "./Son1"
export default {
name: 'Parent',
components: {
Son1
},
data () {
return {
num: 100
}
}
}
</script>
子組件通過 props
接收父組件數(shù)據(jù)。
// Son1.vue
<template>
<div>
子組件1: {{parentNum}}
</div>
</template>
<script>
export default {
name: 'Son1',
props: {
parentNum: { // 接收父組件數(shù)據(jù)
type: Number,
default: 0
}
}
}
</script>
1.2 $emit 使用
components
├── Parent.vue // 父
└── Son1.vue // 子1
父組件通過 v-on
將事件綁定在子組件身上。
// Parent.vue
<template>
<div>
父組件: {{num}}
<Son1 :parentNum="num" @changeNum="change"></Son1>
</div>
</template>
<script>
import Son1 from "./Son1"
export default {
name: 'Parent',
components: {
Son1
},
data () {
return {
num: 100
}
},
methods: {
change (newNum) {
this.num = newNum
}
}
}
</script>
子組件通過 $emit
觸發(fā)父組件綁定在自己身上的事件,調(diào)用父組件相應(yīng)方法,由父組件自行修改數(shù)據(jù)(遵循單向數(shù)據(jù)流)。
// Son1.vue
<template>
<div>
子組件1: {{parentNum}}
<button @click="change">修改父組件數(shù)據(jù)</button>
</div>
</template>
<script>
export default {
name: 'Son1',
props: {
parentNum: {
type: Number,
default: 0
}
},
methods: {
change () {
this.$emit('changeNum', 200)
}
}
}
</script>
1.2.1 .sync 語法糖
改寫:對以上綁定事件方法進行改寫。
父組件向子組件傳遞值與方法,此時綁定的事件為 changeNum:xxx
。
// Parent.vue
<template>
<div>
父組件: {{num}}
<Son1 :parentNum="num" @changeNum:xxx="change"></Son1>
</div>
</template>
<script>
import Son1 from "./Son1"
export default {
name: 'Parent',
components: {
Son1
},
data () {
return {
num: 100
}
},
methods: {
change (newNum) {
this.num = newNum
}
}
}
</script>
同樣,子組件觸發(fā)自身 changeNum:xxx
事件,調(diào)用父組件方法修改數(shù)據(jù)。
// Son1.vue
<template>
<div>
子組件1: {{parentNum}}
<button @click="change">修改父組件數(shù)據(jù)</button>
</div>
</template>
<script>
export default {
name: 'Son1',
props: {
parentNum: {
type: Number,
default: 0
}
},
methods: {
change () {
this.$emit('changeNum:xxx', 200) // 修改調(diào)用方法
}
}
}
</script>
進一步改寫:將事件 changeNum:xxx
改寫為 update:parentNum
。
// Parent.vue
<template>
<div>
父組件: {{num}}
<Son1 :parentNum="num" @update:parentNum="change"></Son1>
</div>
</template>
<script>
import Son1 from "./Son1"
export default {
name: 'Parent',
components: {
Son1
},
data () {
return {
num: 100
}
},
methods: {
change (newNum) {
this.num = newNum
}
}
}
</script>
同理,子組件調(diào)用方法改為 update:parentNum
。
// Son1.vue
<template>
<div>
子組件1: {{parentNum}}
<button @click="change">修改父組件數(shù)據(jù)</button>
</div>
</template>
<script>
export default {
name: 'Son1',
props: {
parentNum: {
type: Number,
default: 0
}
},
methods: {
change () {
this.$emit('update:parentNum', 200) // 修改調(diào)用方法
}
}
}
</script>
語法糖 .sync
誕生:同步父子組件數(shù)據(jù)。
父組件使用 :parentNum.sync="num"
語法糖簡寫,省略 @update:parentNum="change"
。
因為語法糖
:parentNum.sync="num"
是
:parentNum="num" @update:parentNum="change"
即
:parentNum="num" @update:parentNum="newNum => num = newNum"
的簡寫,已經(jīng)包含數(shù)據(jù)修改方法
newNum => num = newNum
,故不再需要change
事件。
// Parent.vue
<template>
<div>
父組件: {{num}}
<Son1 :parentNum.sync="num"></Son1>
</div>
</template>
<script>
import Son1 from "./Son1"
export default {
name: 'Parent',
components: {
Son1
},
data () {
return {
num: 100
}
}
}
</script>
子組件同上改進,觸發(fā) update:parentNum
事件,調(diào)用父組件方法修改數(shù)據(jù)。
注意,使用語法糖
.sync
時,子組件必須使用update
。
// Son1.vue
<template>
<div>
子組件1: {{parentNum}}
<button @click="change">修改父組件數(shù)據(jù)</button>
</div>
</template>
<script>
export default {
name: 'Son1',
props: {
parentNum: {
type: Number,
default: 0
}
},
methods: {
change () {
this.$emit('update:parentNum', 200) // 修改調(diào)用方法
}
}
}
</script>
1.2.2 v-model 語法糖
同上改寫,此時 父組件傳值綁定為 value
,方法修改為 input
。
// Parent.vue
<template>
<div>
父組件: {{num}}
<Son1 :value="num" @input="change"></Son1>
</div>
</template>
<script>
import Son1 from "./Son1"
export default {
name: 'Parent',
components: {
Son1
},
data () {
return {
num: 100
}
},
methods: {
change (newNum) {
this.num = newNum
}
}
}
</script>
此時子組件值使用 value
,觸發(fā)方法使用 input
。
<template>
<div>
子組件1: {{value}}
<button @click="change">修改父組件數(shù)據(jù)</button>
</div>
</template>
<script>
export default {
name: 'Son1',
props: {
value: {
type: Number,
default: 0
}
},
methods: {
change () {
this.$emit('input', 200)
}
}
}
</script>
語法糖 v-model
誕生。
父組件使用 v-model="num"
語法糖簡寫,省略 :value="num" @input="change"
。
因為語法糖
v-model="num"
是
:value="num" @input="change"
即
:value="num" @input="newNum => num = newNum"
的簡寫,已經(jīng)包含數(shù)據(jù)修改方法
newNum => num = newNum
,故不再需要change
事件。
// Parent.vue
<template>
<div>
父組件: {{num}}
<Son1 v-model="num" />
</div>
</template>
<script>
import Son1 from "./Son1"
export default {
name: 'Parent',
components: {
Son1
},
data () {
return {
num: 100
}
}
}
</script>
同上,子組件接收屬性名只能是 value
,觸發(fā)回調(diào)事件名只能是input
。
// Son1.vue
<template>
<div>
子組件1: {{value}}
<button @click="change">修改父組件數(shù)據(jù)</button>
</div>
</template>
<script>
export default {
name: 'Son1',
props: {
value: { // 接收屬性名只能是 value
type: Number,
default: 0
}
},
methods: {
change () {
this.$emit('input', 200) // 觸發(fā)事件只能是 input
}
}
}
</script>
1.3
children
components
├── Parent.vue // 父
├── Son1.vue // 子1
└── GrandSon.vue // 孫1
父組件數(shù)據(jù)傳遞到子組件,將事件 changeNum
綁定到子組件上。
// Parent.vue
<template>
<div>
父組件: {{num}}
<Son1 :parentNum="num" @changeNum="change"></Son1>
</div>
</template>
<script>
import Son1 from "./Son1"
export default {
name: 'Parent',
components: {
Son1
},
data () {
return {
num: 100
}
},
methods: {
change (newNum) {
this.num = newNum
}
}
}
</script>
子組件接收數(shù)據(jù),并將數(shù)據(jù)傳遞到孫組件。
// Son1.vue
<template>
<div>
子組件1: {{parentNum}}
<button @click="change">修改父組件數(shù)據(jù)</button>
<br />
<Grandson1 :parentNum="parentNum" />
</div>
</template>
<script>
import Grandson1 from './Grandson1'
export default {
name: 'Son1',
components: {
Grandson1
},
props: {
parentNum: {
type: Number,
default: 0
}
},
methods: {
change () {
this.$emit('changeNum', 200)
}
}
}
</script>
孫組件接收數(shù)據(jù),并通過 $parent
調(diào)用其父組件(即子組件),由于子組件上綁定有爺組件(即父組件)傳遞的方法 changeNum
,故可進行調(diào)用修改數(shù)據(jù)。
// Grandson1.vue
<template>
<div>
孫組件1:{{parentNum}}
<button @click="change">修改爺組件數(shù)據(jù)</button>
</div>
</template>
<script>
export default {
name: 'Grandson1',
props: {
parentNum: {
type: Number,
default: 0
}
},
methods: {
change () {
this.$parent.$emit('changeNum', 300)
}
},
}
</script>
1.3.1 $dispatch 向上派發(fā)
如上,如果層級很深,那么就會出現(xiàn) $parent.$parent.....
的情況,可以封裝一個 $dispatch
方法向上進行派發(fā)。
在 main.js
中 為 Vue
原型添加 $dispatch
方法,以便每個 vue
實例均可使用。
// main.js
import Vue from 'vue'
import App from './App.vue'
// 向上派發(fā)
Vue.prototype.$dispatch = function (eventName, value) {
let parent = this.$parent
while (parent) {
parent.$emit(eventName, value)
parent = parent.$parent
}
}
new Vue({
render: h => h(App)
}).$mount('#app')
后代組件調(diào)用原型上的 $dispatch
方法。
// Grandson1.vue
<template>
<div>
孫組件1:{{parentNum}}
<button @click="change">修改爺組件數(shù)據(jù)</button>
</div>
</template>
<script>
export default {
name: 'Grandson1',
props: {
parentNum: {
type: Number,
default: 0
}
},
methods: {
change () {
this.$dispatch('changeNum', 300)
}
},
}
</script>
1.3.2 $broadcast 向下通知
同理,如果層級很深,調(diào)用后代方法就會出現(xiàn)多個 $children
循環(huán)遍歷的情況,可以封裝一個 $broadcast
方法向下進行通知。
在 main.js
中 為 Vue
原型添加 $dispatch
方法,以便每個 vue
實例均可使用。
// main.js
import Vue from 'vue'
import App from './App.vue'
// 向下通知
Vue.prototype.$broadcast = function (eventName, value) {
const broadcast = children => {
children.forEach(child => {
child.$emit(eventName, value)
if (child.$children) broadcast(child.$children)
})
}
broadcast(this.$children)
}
new Vue({
render: h => h(App)
}).$mount('#app')
為長輩組件添加調(diào)用后代組件方法的方法。
// Parent.vue
<template>
<div>
父組件: {{num}}
<button @click="triggerChild">觸發(fā)后代方法</button>
<Son1 :parentNum="num" @changeNum="change"></Son1>
</div>
</template>
<script>
import Son1 from "./Son1"
export default {
name: 'Parent',
components: {
Son1
},
data () {
return {
num: 100
}
},
methods: {
change (newNum) {
this.num = newNum
},
triggerChild () {
this.$broadcast('changeChild') // 調(diào)用后代中的 changeChild 方法
}
}
}
</script>
在后代組件中綁定供長輩組件調(diào)用的方法。
// Son1.vue
<template>
<div>
子組件1: {{parentNum}}
<button @click="change">修改父組件數(shù)據(jù)</button>
<br />
<!-- 綁定方法 -->
<Grandson1 :parentNum="parentNum" @changeChild="change" />
</div>
</template>
<script>
import Grandson1 from './Grandson1'
export default {
name: 'Son1',
components: {
Grandson1
},
props: {
parentNum: {
type: Number,
default: 0
}
},
methods: {
change () {
this.$emit('changeNum', 200)
}
}
}
</script>
1.4
listeners
components
├── Parent.vue // 父
├── Son1.vue // 子1
└── GrandSon.vue // 孫1
1.4.1 $attrrs 屬性集合
父組件定義多個屬性,傳遞給孫組件。
需先由父組件傳遞給子組件。
<template>
<div>
父組件
<Son1 :name="name" :age=" 12" :sex="sex" />
</div>
</template>
<script>
import Son1 from "./Son1"
export default {
name: 'Parent',
components: {
Son1
},
data () {
return {
name: '張三',
age: '12',
sex: '男'
}
}
}
</script>
子組件可以使用 props
依次接收,然后在傳遞給孫組件。但是,每個組件向下傳遞時都寫在 props
中很麻煩,所以,可以使用 $attrs
統(tǒng)一接收,并傳遞給后代組件傳遞。
接收的
$attrs
是屬性對象,可以傳遞單個屬性,也可使用v-bind="{name: $attrs.name, age: '$attrs.age'}"
傳遞部分屬性,也可以使用v-bind="$attrs"
傳遞全部屬性。
<template>
<div>
子組件1: {{$attrs}}
<!-- <Grandson1 :name="$attrs.name" :age="$attrs.age" :sex="$attrs.sex" /> -->
<!-- <Grandson1 v-bind="{name: $attrs.name, age: '$attrs.age'}" /> -->
<Grandson1 v-bind='$attrs' />
</div>
</template>
<script>
import Grandson1 from './Grandson1'
export default {
name: 'Son1',
components: {
Grandson1
}
}
</script>
孫組件使用父組件傳遞的屬性。
//Grandson1.vue
<template>
<div>
孫組件1:姓名:{{$attrs.name}} 年齡:{{$attrs.age}} 性別:{{$attrs.sex}}
</div>
</template>
<script>
export default {
name: 'Grandson1'
}
</script>
此時可以看到,傳遞的屬性同樣添加到了 DOM
屬性上。
若不想將屬性添加到 DOM
上,可聲明不繼承。
//Grandson1.vue
<template>
<div>
孫組件1:姓名:{{$attrs.name}} 年齡:{{$attrs.age}} 性別:{{$attrs.sex}}
</div>
</template>
<script>
export default {
name: 'Grandson1',
inheritAttrs: false // 聲明不繼承
}
</script>
1.4.2 $listeners 方法集合
父組件定義多個屬性,傳遞給孫組件。
需先由父組件傳遞給子組件。
// Parent.vue
<template>
<div>
父組件 {{num}}
<Son1 @changeNum="change" />
</div>
</template>
<script>
import Son1 from "./Son1"
export default {
name: 'Parent',
components: {
Son1
},
data () {
return {
num: 100
}
},
methods: {
change (newNum) {
this.num = newNum
}
}
}
</script>
子組件使用 $listener
接收父組件傳入的全部方法,并使用 v-on
傳遞給孫組件。
// Son1.vue
<template>
<div>
子組件1
<Grandson1 v-on='$listeners' />
</div>
</template>
<script>
import Grandson1 from './Grandson1'
export default {
name: 'Son1',
components: {
Grandson1
}
}
</script>
孫組件調(diào)用 $listeners
中的某個方法。
// Grandson1.vue
<template>
<div>
孫組件1:<button @click="change">調(diào)用前輩方法</button>
</div>
</template>
<script>
export default {
name: 'Grandson1',
methods: {
change () {
this.$listeners.changeNum(300)
}
}
}
</script>
1.5 provide inject
如上,后代使用前輩方法需要依次傳遞,但希望能夠?qū)崿F(xiàn)前輩組件定義方法,后代均可直接共享(類似于 react
中的 context
)。
后代組件可以注入數(shù)據(jù),也可以注入前輩組件(前輩組件先提供)。
components
├── Parent.vue // 父
├── Son1.vue // 子1
└── GrandSon.vue // 孫1
1.5.1 Provide
父組件使用 provide
提供自身。
// Parent.vue
<template>
<div>
父組件 {{num}}
<Son1 />
</div>
</template>
<script>
import Son1 from "./Son1"
export default {
name: 'Parent',
components: {
Son1
},
provide () {
return {
parent: this // 提供自己
}
},
data () {
return {
num: 100
}
},
methods: {
change (newNum) {
this.num = newNum
}
}
}
</script>
1.5.2 inject
孫組件使用 inject
注入其爺組件(父組件)。
// Grandson1.vue
<template>
<div>
孫組件1:<button @click="change">調(diào)用前輩方法</button>
</div>
</template>
<script>
export default {
name: 'Grandson1',
inject: [ 'parent'],
methods: {
change () {
this.parent.change(300)
}
}
}
</script>
1.6 ref
ref
放在 DOM
元素上,獲取的是 DOM
節(jié)點。放在組件上,獲取的是當(dāng)前組件。故可以使用 ref
調(diào)用子組件屬性與方法。
components
├── Parent.vue // 父
└── Son1.vue // 子1
子組件定義屬性與方法。
// Son1.vue
<template>
<div>
子組件1:{{num}}
</div>
</template>
<script>
export default {
name: 'Son1',
data () {
return {
num: 200
}
},
methods: {
changeNum (newNum) {
this.num = newNum
}
}
}
</script>
父組件調(diào)用子組件屬性與方法。
// Parent.vue
<template>
<div>
父組件 {{num}}
<button @click="useChild">調(diào)用子組件方法</button>
<Son1 ref="Son1" />
</div>
</template>
<script>
import Son1 from "./Son1"
export default {
name: 'Parent',
components: {
Son1
},
data () {
return {
num: 100
}
},
mounted () {
this.num = this.$refs.Son1.num // 調(diào)用子組件屬性
},
methods: {
useChild () {
this.$refs.Son1.changeNum(201) // 調(diào)用子組件方法
}
}
}
</script>
1.7 EventBus
components
├── Parent.vue // 父
├── Son1.vue // 子1
├── Grandson1.vue // 子1
└── Son2.vue // 子2
EventBus
通常用于兄弟組件及其他非間隔組件之間的數(shù)據(jù)通信。
在 main.js
中在 Vue
原型上添加 $bus
,其是一個 vue
實例。
為什么使用
vue
實例?因為vue
實例上有$on
和$emit
方法,可用于收集和觸發(fā)事件。
import Vue from 'vue'
import App from './App.vue'
Vue.prototype.$bus = new Vue()
new Vue({
render: h => h(App)
}).$mount('#app')
父組件引入兩個子組件。
// Parent.vue
<template>
<div>
父組件
<Son1 />
<Son2 />
</div>
</template>
<script>
import Son1 from "./Son1"
import Son2 from "./Son2"
export default {
name: 'Parent',
components: {
Son1,
Son2
}
}
</script>
子組件1在 $bus
上綁定事件。
// Son1.vue
<template>
<div>
子組件1
<Grandson1 />
</div>
</template>
<script>
import Grandson1 from './Grandson1'
export default {
name: 'Son1',
components: {
Grandson1
},
mounted () {
this.$bus.$on('change', () =>{
console.log("Son1被觸發(fā)")
})
},
}
</script>
子組件2在 $bus
上綁定事件。
// Son2.vue
<template>
<div>
子組件2
</div>
</template>
<script>
export default {
name: 'Son2',
mounted () {
this.$bus.$on('change', () =>{
console.log("Son2被觸發(fā)")
})
},
}
</script>
孫組件1觸發(fā) $bus
上綁定的指定事件。
用方法</button>
</div>
</template>
<script>
export default {
name: 'Grandson1',
methods: {
change () {
this.$bus.$emit('change')
}
}
}
</script>
如上,此 EventBus
方法缺陷是如果綁定了相同的事件名,在一次觸發(fā)時所有相同事件名事件都將被觸發(fā)。
1.8 Vuex
待添加。