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
接收父组件数据。
// Son1.vue
<template>
<div>
子组件1: {{parentNum}}
</div>
</template>
<script>
export default {
name: 'Son1',
props: {
parentNum: { // 接收父组件数据
type: Number,
default: 0
}
}
}
</script>
1.1-1.png
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
触发父组件绑定在自己身上的事件,调用父组件相应方法,由父组件自行修改数据(遵循单向数据流)。
// Son1.vue
<template>
<div>
子组件1: {{parentNum}}
<button @click="change">修改父组件数据</button>
</div>
</template>
<script>
export default {
name: 'Son1',
props: {
parentNum: {
type: Number,
default: 0
}
},
methods: {
change () {
this.$emit('changeNum', 200)
}
}
}
</script>
1.2-1.gif
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>
同样,子组件触发自身 changeNum:xxx
事件,调用父组件方法修改数据。
// Son1.vue
<template>
<div>
子组件1: {{parentNum}}
<button @click="change">修改父组件数据</button>
</div>
</template>
<script>
export default {
name: 'Son1',
props: {
parentNum: {
type: Number,
default: 0
}
},
methods: {
change () {
this.$emit('changeNum:xxx', 200) // 修改调用方法
}
}
}
</script>
1.2.1-1.gif
进一步改写:将事件 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>
同理,子组件调用方法改为 update:parentNum
。
// Son1.vue
<template>
<div>
子组件1: {{parentNum}}
<button @click="change">修改父组件数据</button>
</div>
</template>
<script>
export default {
name: 'Son1',
props: {
parentNum: {
type: Number,
default: 0
}
},
methods: {
change () {
this.$emit('update:parentNum', 200) // 修改调用方法
}
}
}
</script>
1.2.1-2.gif
语法糖 .sync
诞生:同步父子组件数据。
父组件使用 :parentNum.sync="num"
语法糖简写,省略 @update:parentNum="change"
。
因为语法糖
:parentNum.sync="num"
是
:parentNum="num" @update:parentNum="change"
即
:parentNum="num" @update:parentNum="newNum => num = newNum"
的简写,已经包含数据修改方法
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>
子组件同上改进,触发 update:parentNum
事件,调用父组件方法修改数据。
注意,使用语法糖
.sync
时,子组件必须使用update
。
// Son1.vue
<template>
<div>
子组件1: {{parentNum}}
<button @click="change">修改父组件数据</button>
</div>
</template>
<script>
export default {
name: 'Son1',
props: {
parentNum: {
type: Number,
default: 0
}
},
methods: {
change () {
this.$emit('update:parentNum', 200) // 修改调用方法
}
}
}
</script>
1.2.1-3.gif
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
,触发方法使用 input
。
<template>
<div>
子组件1: {{value}}
<button @click="change">修改父组件数据</button>
</div>
</template>
<script>
export default {
name: 'Son1',
props: {
value: {
type: Number,
default: 0
}
},
methods: {
change () {
this.$emit('input', 200)
}
}
}
</script>
1.2.2-1.gif
语法糖 v-model
诞生。
父组件使用 v-model="num"
语法糖简写,省略 :value="num" @input="change"
。
因为语法糖
v-model="num"
是
:value="num" @input="change"
即
:value="num" @input="newNum => num = newNum"
的简写,已经包含数据修改方法
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
,触发回调事件名只能是input
。
// Son1.vue
<template>
<div>
子组件1: {{value}}
<button @click="change">修改父组件数据</button>
</div>
</template>
<script>
export default {
name: 'Son1',
props: {
value: { // 接收属性名只能是 value
type: Number,
default: 0
}
},
methods: {
change () {
this.$emit('input', 200) // 触发事件只能是 input
}
}
}
</script>
1.2.2-2.gif
1.3 children
components
├── Parent.vue // 父
├── Son1.vue // 子1
└── GrandSon.vue // 孙1
父组件数据传递到子组件,将事件 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>
子组件接收数据,并将数据传递到孙组件。
// Son1.vue
<template>
<div>
子组件1: {{parentNum}}
<button @click="change">修改父组件数据</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>
孙组件接收数据,并通过 $parent
调用其父组件(即子组件),由于子组件上绑定有爷组件(即父组件)传递的方法 changeNum
,故可进行调用修改数据。
// Grandson1.vue
<template>
<div>
孙组件1:{{parentNum}}
<button @click="change">修改爷组件数据</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.gif
1.3.1 $dispatch 向上派发
如上,如果层级很深,那么就会出现 $parent.$parent.....
的情况,可以封装一个 $dispatch
方法向上进行派发。
在 main.js
中 为 Vue
原型添加 $dispatch
方法,以便每个 vue
实例均可使用。
// main.js
import Vue from 'vue'
import App from './App.vue'
// 向上派发
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')
后代组件调用原型上的 $dispatch
方法。
// Grandson1.vue
<template>
<div>
孙组件1:{{parentNum}}
<button @click="change">修改爷组件数据</button>
</div>
</template>
<script>
export default {
name: 'Grandson1',
props: {
parentNum: {
type: Number,
default: 0
}
},
methods: {
change () {
this.$dispatch('changeNum', 300)
}
},
}
</script>
1.3.1-1.gif
1.3.2 $broadcast 向下通知
同理,如果层级很深,调用后代方法就会出现多个 $children
循环遍历的情况,可以封装一个 $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')
为长辈组件添加调用后代组件方法的方法。
// Parent.vue
<template>
<div>
父组件: {{num}}
<button @click="triggerChild">触发后代方法</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') // 调用后代中的 changeChild 方法
}
}
}
</script>
在后代组件中绑定供长辈组件调用的方法。
// Son1.vue
<template>
<div>
子组件1: {{parentNum}}
<button @click="change">修改父组件数据</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.3.2-1.gif
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
统一接收,并传递给后代组件传递。
接收的
$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>
1.4.1-1.png
此时可以看到,传递的属性同样添加到了 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.1-3.png
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>
孙组件调用 $listeners
中的某个方法。
// Grandson1.vue
<template>
<div>
孙组件1:<button @click="change">调用前辈方法</button>
</div>
</template>
<script>
export default {
name: 'Grandson1',
methods: {
change () {
this.$listeners.changeNum(300)
}
}
}
</script>
1.4.2-1.gif
1.5 provide inject
如上,后代使用前辈方法需要依次传递,但希望能够实现前辈组件定义方法,后代均可直接共享(类似于 react
中的 context
)。
后代组件可以注入数据,也可以注入前辈组件(前辈组件先提供)。
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">调用前辈方法</button>
</div>
</template>
<script>
export default {
name: 'Grandson1',
inject: [ 'parent'],
methods: {
change () {
this.parent.change(300)
}
}
}
</script>
1.5.2-1.gif
1.6 ref
ref
放在 DOM
元素上,获取的是 DOM
节点。放在组件上,获取的是当前组件。故可以使用 ref
调用子组件属性与方法。
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>
父组件调用子组件属性与方法。
// Parent.vue
<template>
<div>
父组件 {{num}}
<button @click="useChild">调用子组件方法</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 // 调用子组件属性
},
methods: {
useChild () {
this.$refs.Son1.changeNum(201) // 调用子组件方法
}
}
}
</script>
1.6-1.gif
1.7 EventBus
components
├── Parent.vue // 父
├── Son1.vue // 子1
├── Grandson1.vue // 子1
└── Son2.vue // 子2
EventBus
通常用于兄弟组件及其他非间隔组件之间的数据通信。
在 main.js
中在 Vue
原型上添加 $bus
,其是一个 vue
实例。
为什么使用
vue
实例?因为vue
实例上有$on
和$emit
方法,可用于收集和触发事件。
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被触发")
})
},
}
</script>
子组件2在 $bus
上绑定事件。
// Son2.vue
<template>
<div>
子组件2
</div>
</template>
<script>
export default {
name: 'Son2',
mounted () {
this.$bus.$on('change', () =>{
console.log("Son2被触发")
})
},
}
</script>
孙组件1触发 $bus
上绑定的指定事件。
![8](../../8.gif<template>
<div>
孙组件1:<button @click="change">调用方法</button>
</div>
</template>
<script>
export default {
name: 'Grandson1',
methods: {
change () {
this.$bus.$emit('change')
}
}
}
</script>
1.7-1.gif
如上,此 EventBus
方法缺陷是如果绑定了相同的事件名,在一次触发时所有相同事件名事件都将被触发。
1.8 Vuex
待添加。
网友评论