父子通信
props 和 emit
父 -> 子
子 -> 父
props: {
校验的属性名: {
type: 类型, // Number String Boolean ...
required: true, // 是否必填
default: 默认值, // 默认值
validator (value) {
// 自定义校验逻辑
return 是否通过校验
}
}
},
- data 的数据是自己的 → 随便改
- prop 的数据是外部的 → 不能直接改,要遵循 单向数据流
- 子组件不能直接修改父组件的数据
style 和 class
子组件的根元素可以合并自身的和父元素传递过来的 class 和 style
<div class="son" style="color: chartreuse">
<h1>{{msg}}</h1>
<button @click="changeMsg">我是子组件,我要修改父组件的内容</button>
</div>
<Son style="margin-top: 20px" class="father" :msg="msg" @changeMsg="handleChange"/>
attribute
父组件传递一些属性到子组件中,但是子组件并没有声明这些属性,则它们成为 attribute ,这些属性直接附在子元素的根元素上。
不包括 class 和 style ,处理机制不同
<Son
data-a="123"
b-c="234"
/>
然后在子组件中通过 this.$attrs
获取父组件传递过来的属性。
当然,子组件可以通过 inheritAttrs: false 进行配置,禁止将 attribute 附在子组件的根元素上,但仍然可以通过 $attrs
获取。
native 修饰符
<Son
@click.native="handleClick"
/>
handleClick(){
console.log(1)
}
此时父组件的事件会直接挂载到子组件的根元素上,所以点击子组件根div 会打印 1 。
listeners
子组件可以通过 $listeners
获取父组件传递过来的所有事件处理函数。
v-model
双向数据绑定。
sync 修饰符
类似于 v-model。 那么区别是什么呢?先看一个例子:
<template>
<div>
<button @click="$emit(`update:num1`, num1 - 1)"> - </button>
{{num1}}
<button @click="$emit(`update:num1`, num1 + 1)"> + </button>
<button @click="$emit(`update:num2`, num2 - 1)"> - </button>
{{num2}}
<button @click="$emit(`update:num2`, num2 + 1)"> + </button>
</div>
</template>
<script>
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: 'Son',
props: ['num1', 'num2'],
methods: {
}
}
</script>
<style scoped>
</style>
<template>
<Son
:num1="n1"
:num2="n2"
@update:num1="n1 = $event"
@update:num2="n2 = $event"
/>
</template>
<script>
import Son from './components/Son.vue'
export default {
name: 'App',
components: {
Son
},
data(){
return {
n1: 1,
n2: 2
}
},
created() {
console.log(this.$listeners)
},
methods: {
handleClick(){
console.log(1)
}
}
}
</script>
<style>
</style>
通过父组件的两个事件控制子组件数据和视图变化。
而 sync 的作用就是:
<Son
:num1.sync="n1"
:num2.sync="n2"
/>
相当于简化书写,但格式有规定。
$parent
和 $children
用于获取当前组件的父组件和子组件。
注意:子组件挂载之后才可以访问到。
子组件可以直接修改父组件的数据。
$slots
和 $scopedSlots
ref
父组件获取子组件的实例。
子组件挂载之后才可以访问(获取)到。
<template>
<Son
:num1.sync="n1"
:num2.sync="n2"
ref="son"
/>
</template>
<script>
import Son from './components/Son.vue'
export default {
name: 'App',
components: {
Son
},
data(){
return {
n1: 1,
n2: 2
}
},
mounted() {
console.log(this.$refs.son)
},
methods: {
}
}
</script>
<style>
</style>
跨组件通信
事件总线 event bus
-
创建一个都能访问的事件总线 (空Vue实例)
import Vue from 'vue' const Bus = new Vue() export default Bus
-
A组件(接受方),监听Bus的 $on事件
created () { Bus.$on('sendMsg', (msg) => { this.msg = msg }) }
-
B组件(发送方),触发Bus的$emit事件
Bus.$emit('sendMsg', '这是一个消息')
provide 和 inject
- 父组件 provide提供数据
export default {
provide () {
return {
// 普通类型【非响应式】
color: this.color,
// 复杂类型【响应式】
userInfo: this.userInfo,
}
}
}
2.子/孙组件 inject获取数据
export default {
inject: ['color','userInfo'],
created () {
console.log(this.color, this.userInfo)
}
}
- provide提供的简单类型的数据不是响应式的,复杂类型数据是响应式。(推荐提供复杂类型数据)
- 子/孙组件通过inject获取的数据,不能在自身组件内修改
router
一个组件改变了地址栏,其他监听地址栏的组件都会做出反应。
比如点击 router-link 组件改变了地址,router-view 组件就渲染其他内容。
vuex
数据仓库。
store 模式
缺点就是难以跟踪数据变化。
// store.js
const store = {
loginUser: ...,
settings: ...
}
// compA
const compA = {
data(){
return {
store.loginUser
}
}
}
data 里面的数据都是响应式(递归响应)