标签:lock block 结束 设计理念 主题 基础 rev ble function
> props(常用)
> props和$emit(常用)
> .sync(语法糖)
> model(单选框和复选框场景可以使用)
> $attr和$listeners(组件封装用的比较多)
> provide和inject(高阶组件/组件库使用比较多)
> eventBus(小项目中使用就好)
> Vuex(中大型项目推荐使用)
> $parent和$children(推荐少用)
> $root(组件树的根,用的少)
> 其他通信方式
当前组件接收到的 props 对象。Vue 实例代理了对其 props 对象属性的访问。
在使用prop传参时需要注意:
// Father组件
<template>
<div class="father">
<Child
:msg=‘msg‘
:changeMsg=‘handleChangeMsg‘
/>
</div>
</template>
<script>
import Child from ‘./Child.vue‘
export default {
name: ‘father‘,
data() {
return {
msg: ‘msg‘
}
},
methods: {
handleChangeMsg(value) {
this.msg = value
}
},
components: {
Child
}
}
</script>
// Child组件
<template>
<div class="child">
<h3>String使用:</h3>
<div>
{{msg}}
</div>
<button @click="handleChangeMsg">修改父组件的msg</button>
</div>
</template>
<script>
export default {
name: ‘Child‘,
props: {
msg: {
type: String,
default: ‘‘
},
changeMsg: {
type: Function,
default: () => {}
}
},
methods: {
handleChangeMsg() {
// this.msg = ‘a‘ // 控制台会报错
// 可以使用父组件给的方法来改数据
this.changeMsg(‘hello world‘)
}
}
}
</script>
触发当前实例上的事件。附加参数都会传给监听器回调。
emit的使用场景主要是在子组件要传参数给父组件,通过$emit来触发父组件给的监听器。
// Father组件
<template>
<div class="father">
{{value}}
<Child v-on:change="handleChange" :value=‘value‘ />
</div>
</template>
<script>
import Child from ‘./Child.vue‘
export default {
name: ‘father‘,
data() {
return {
value: ‘‘
}
},
methods: {
handleChange(value) {
this.value = value
}
},
components: {
Child
}
}
</script>
// Child组件
<template>
<div class="child">
<input type="text" :value="value" @change="_change">
</div>
</template>
<script>
export default {
name: ‘Child‘,
props: {
value: String
},
methods: {
_change(e) {
this.$emit(‘change‘, e.target.value)
}
}
}
</script>
在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以修改父组件,且在父组件和子组件都没有明显的改动来源。因此以 update:myPropName 的模式触发事件取而代之。
<Child :value.sync=‘value‘ v-bind.sync=‘obj‘ />
,这样会把 obj 对象中的每一个属性 (如 title) 都作为一个独立的 prop 传进去,然后各自添加用于更新的 v-on 监听器。 // Father组件
<template>
<div class="father">
{{value}}
<br/>
{{obj}}
<br/>
<!-- <Child v-on:update:value=‘value = $event‘ /> -->
<!-- sync是上面的语法糖 -->
<!-- <Child :value.sync=‘value‘ /> -->
<Child :value.sync=‘value‘ v-bind.sync=‘obj‘ />
</div>
</template>
<script>
import Child from ‘./Child.vue‘
export default {
name: ‘father‘,
data() {
return {
value: ‘hello‘,
obj: {
title: ‘主题‘,
content: ‘文本‘
}
}
},
components: {
Child
}
}
</script>
// Child组件
<template>
<div class="child">
<input type="text" :value="value" @change="_change">
<br/>
<button @click="_changeObj">改变obj对象</button>
</div>
</template>
<script>
export default {
name: ‘Child‘,
props: {
value: String
},
methods: {
_change(e) {
this.$emit(‘update:value‘, e.target.value)
},
_changeObj() {
this.$emit(‘update:title‘, ‘新主题‘)
}
}
}
</script>
允许一个自定义组件在使用 v-model 时定制 prop 和 event。默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。
// Father组件
<template>
<div class="father">
输入的值是:{{phoneInfo}}
<Child v-model="phoneInfo" />
</div>
</template>
<script>
import Child from ‘./Child.vue‘
export default {
name: ‘father‘,
data() {
return {
phoneInfo: {
areaCode: ‘+86‘,
phone: ‘‘
}
}
},
components: {
Child
}
}
</script>
// Child组件
<template>
<div class="child">
<select
:value="phoneInfo.areaCode"
placeholder="区号"
@change="_changeAreaCode"
>
<option value="+86">+86</option>
<option value="+60">+60</option>
</select>
<input
:value="phoneInfo.phone"
type="number"
placeholder="手机号"
@input="_changePhone"
/>
</div>
</template>
<script>
export default {
name: ‘Child‘,
model: {
prop: ‘phoneInfo‘, // 默认 value
event: ‘change‘ // 默认 input
},
props: {
phoneInfo: Object
},
methods: {
_changeAreaCode(e) {
this.$emit(‘change‘, {
...this.phoneInfo,
areaCode: e.target.value
})
},
_changePhone(e) {
this.$emit(‘change‘, {
...this.phoneInfo,
phone: e.target.value
})
}
}
}
</script>
- $attrs包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
- $listeners包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。
// 第一个组件
<template>
<div class="one">
第一个组件的value:{{value}}
<Two :value=‘value‘ @change="handleChange" @changeTwo.native=‘handleChange‘ :test=‘test‘ />
</div>
</template>
<script>
import Two from ‘./Two.vue‘
export default {
name: ‘one‘,
data() {
return {
value: 10,
test: ‘hello‘
}
},
methods: {
handleChange(value, msg) {
this.value = value
console.log(msg)
}
},
components: {
Two
}
}
</script>
// 第二个组件
<template>
<div class="two">
<button @click="_change">第二个组件</button>
<br/>
第二个组件的value:{{$attrs.value}}
<Three v-bind="$attrs" v-on="$listeners"/>
</div>
</template>
<script>
import Three from ‘./Three.vue‘
export default {
inheritAttrs: false, //
name: ‘two‘,
props: {
test: String
},
created() {
console.log(‘-----第二个组件-----‘)
console.log(this.$attrs) // 获取父级作用域中绑定在该组件上且没有在Prop声明的属性
// {value: 10}
console.log(this.$listeners) // 获取父作用域中的 (不含 .native 修饰器的) v-on 事件监听器
// {change: ƒ}
},
methods: {
_change() {
this.$emit(‘change‘, 2, ‘来自第二个组件的事件触发‘)
}
},
components: {
Three
}
}
</script>
// 第三个组件
<template>
<div class="three">
<button @click="_change">第三个组件</button>
<br/>
第三个组件中显示第一个组件的value:{{$attrs.value}}
</div>
</template>
<script>
export default {
name: ‘three‘,
created() {
console.log(‘-----第三个组件-----‘)
console.log(this.$attrs)
console.log(this.$listeners)
},
methods: {
_change() {
this.$emit(‘change‘, 3, ‘来自第三个组件的事件触发,感谢$listeners‘)
}
}
}
</script>
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
// one组件
<template>
<div class="one">
data中的b:{{b}}
<br>
可响应对象test:{{test}}
<br>
<button @click="_change">第一个组件的按钮</button>
<Two/>
</div>
</template>
<script>
import Vue from "vue"
import Two from ‘./Two.vue‘
import symbol from ‘./symbol‘
export default {
// provide: {
// a: ‘A‘
// },
provide () {
this.test = Vue.observable({ // 可响应对象的创建,建议传响应式对象
count: 0
})
return {
a: ‘A‘,
test: this.test, // 赋与对象指针
b: this.b, // 赋值操作
[symbol.KEY]: ‘C‘,
one: this,
onChange: this.handleChange
}
},
name: ‘one‘,
data () {
return {
b: ‘B‘
}
},
created () {
console.log(‘-----第一个组件-----‘)
console.log(‘data中b=‘ + this.b)
},
methods: {
handleChange (value, msg) {
this.b = value
console.log(msg)
},
_change () {
this.b = ‘one....b‘
this.test.count++
}
},
components: {
Two
}
}
</script>
// two组件
<template>
<div class="two">
inject中b的值:{{b}}
<br>
inject中test的值:{{test}}
<br>
<button @click="onChange">第二个组件的按钮</button>
<Three/>
</div>
</template>
<script>
import Three from ‘./Three.vue‘
import symbol from ‘./symbol‘
export default {
// inject: [‘a‘, ‘b‘],
inject: {
a: {
default: ‘AA‘ // 在 2.5.0+ 的注入可以通过设置默认值使其变成可选项
},
b: {
from: ‘b‘, // 如果它需要从一个不同名字的属性注入,则使用 from 来表示其源属性
default: ‘no value!‘
},
key: {
from: symbol.KEY,
default: () => [‘no‘, ‘value‘] // 与 prop 的默认值类似,你需要对非原始值使用一个工厂方法
},
one: {
default: () => ({})
},
_change: { // 命名与子组件冲突可以更改别名
from: ‘onChange‘
},
test: {
from: ‘test‘
}
},
name: ‘two‘,
props: {
two_p_b: {
default () {
return this.b
}
}
},
data () {
return {
two_d_b: this.b
}
},
created () {
console.log(‘-----第二个组件-----‘)
console.log(‘inject注入的a=‘ + this.a)
console.log(‘inject注入的b=‘ + this.b)
console.log(‘-------------------‘)
console.log(‘inject注入整个one组件‘)
console.log(this.one)
console.log(‘-------------------‘)
console.log(‘props中b=‘ + this.two_p_b)
console.log(‘data中b=‘ + this.two_d_b)
console.log(`inject注入的Symbol类型的key=${JSON.stringify(this.key)}`)
},
methods: {
onChange () {
if (this.one && this.one.handleChange)