Vue 组件通信
1、props
`props实现父向子通信:`
向下传递数据,子组件中通过props接收使用数据
`props实现子向父通信:`
给子组件传递一个函数,这个函数用于修改父组件中数据
`代码片段如下:`
1、组件关系如下:
App
Father
Son
2、实现父向子通信代码:
# 父组件中:
<template>
<div>
<h1>我今年{{age}}岁</h1>
<Son :age='age' />
</div>
</template>
<script>
import Son from './Son'
export default {
name: 'Father',
components: {
Son
},
data() {
return {
age: 22,
}
}
}
</script>
<style scoped>
</style>
# 子组件中:
<template>
<div>
<p>我爸爸今年{{age}}岁</p>
</div>
</template>
<script>
export default {
name: 'Son',
props: ['age'],
}
</script>
<style scoped>
</style>
3、实现子向父通信代码:
# 父组件中:
<template>
<div>
<Son :getSonAge='getSonAge' />
<p>我儿子今年{{newSonAge}}岁</p>
</div>
</template>
<script>
import Son from './Son'
export default {
name: 'Father',
components: {
Son
},
data() {
return {
newSonAge: 0
}
},
methods: {
getSonAge(age) {
this.newSonAge = age
}
}
}
</script>
<style scoped>
</style>
# 子组件中:
<template>
<div>
<p>我今年{{sonAge}}岁</p>
</div>
</template>
<script>
export default {
name: 'Son',
props: ['getSonAge'],
data() {
return {
sonAge: 18
}
},
mounted() {
this.getSonAge(this.sonAge)
}
}
</script>
<style scoped>
</style>
2、自定义事件
作用: `用来实现子向父传递数据(通信)`
使用方式:
在子组件中通过`this.$emit(eventName,message)`分发事件
在父组件中通过`this.$on(eventName,callback)`绑定事件
`代码片段如下:`
1、组件关系如下:
App
Father
Son
2、父组件中代码
<template>
<div>
<h1>我儿子今年{{sonAge}}岁</h1>
<Son @getAge='getAge' />
</div>
</template>
<script>
import Son from './Son'
export default {
name: 'Father',
components: {
Son
},
data() {
return {
sonAge: 0
}
},
methods: {
getAge(age) {
this.sonAge = age
}
}
}
3、子组件中代码
<template>
<button @click='$emit("getAge",age)'>click me</button>
</template>
<script>
export default {
name: 'Son',
data() {
return {
age: 18
}
}
}
</script>
<style scoped>
</style>
3、事件总线
作用:`利用自定义事件特性,在Vue构造函数的显示原型上面添加一个公共的对象,这个对象是Vue的实例对象,完成任意组件进行通信的效果`
代码片段:
在main.js中
方式一:
import Vue from 'vue'
import App from './App.vue'
Vue.prototype.$bus = new Vue()
new Vue({
render: h => h(App),
}).$mount('#app')
方式二:
import Vue from 'vue'
import App from './App.vue'
new Vue({
beforeCreate () {
Vue.prototype.$bus = this
},
render: h => h(App),
}).$mount('#app')
4、v-model本质
通过`动态的value属性和原生的input事件来实现`
## 动态的value属性 : 初始化显示
## 原生的input事件 : 表单输入的时候,一直触发
代码片段:
<template>
<div>
<h1>自己实现{{msg}}</h1>
<input type="text" :value="msg" @input="changeValue" />
</div>
</template>
<script>
export default {
data() {
return {
msg: ''
}
},
methods: {
changeValue(event) {
this.msg = event.target.value
}
}
}
</script>
<style scoped>
</style>
## 或者:
<template>
<div>
<h1>自己实现{{msg}}</h1>
<input type="text" :value="msg" @input="msg=$event.target.value" />
</div>
</template>
<script>
export default {
data() {
return {
msg: ''
}
},
}
</script>
<style scoped>
</style>
5、sync修饰符
作用:`子组件修改父组件数据`
.sync要求:
子组件中`$emit(update:propName)`
父组件中`:propName.sync=value`
代码片段:
## 父组件中:
<template>
<div>
<h1>爸爸还剩{{money}}元</h1>
<!-- <Son :money="money" @spendMoney='money=$event'></Son> -->
<Son :money.sync="money" ></Son>
</div>
</template>
<script>
import Son from './Son'
export default {
data() {
return {
money: 1000
}
},
components: {
Son
}
}
</script>
<style scoped>
</style>
## 子组件中:
<template>
<div>
<!-- <button @click="$emit('spendMoney',attrs.money-100)">点击花钱</button>-->
<button @click="$emit('update:money',$attrs.money-100)">点击花钱</button>
</div>
</template>
<script>
</script>
<style scoped>
</style>
6、
attrs和listeners
作用:`透传,向下传递不确定的数据,类似于react封装高阶组件中{...this.props}`
## $attrs:`当一个组件没有声明任何 prop 时,会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过v-bind="$attrs" 传入内部组件,常用于封装组件`
## $listeners:`包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v- on="$listeners" 传入内部组件`
使用:`$attrs一般和v-bind配合,$listeners一般和v-on配合`
代码片段:
组件结构:
App
MyComponent
MyButton
## 父组件中:
<template>
<div>
<MyButton title="添加" icon="xh-add" @click="addBtn" @dblclick="add2Btn" />
<MyButton title="删除" icon="xh-delete" @click="deleteBtn" @dblclick="delete2Btn" />
<MyButton title="修改" icon="xh-update" @click="updateBtn" @dblclick="update2Btn" />
</div>
</template>
<script>
import MyButton from './MyButton'
export default {
name: 'MyComponent',
components: {
MyButton
},
methods: {
addBtn() {
console.log('我是add')
},
add2Btn() {
console.log('我是双击的add')
},
deleteBtn() {
console.log('我是delete')
},
delete2Btn() {
console.log('我是双击的delete')
},
updateBtn() {
console.log('我是update')
},
update2Btn() {
console.log('我是双击的update')
}
}
}
</script>
<style scoped>
</style>
## 子组件中:
<template>
<a href="javascript:;" :title="title">
<svg
class="icon"
aria-hidden="true"
v-if="icon === 'xh-add'"
v-on="$listeners"
v-bind="$attrs"
>
<use xlink:href="#icon-add" />
</svg>
<svg
v-else-if="icon === 'xh-delete'"
v-on="$listeners"
v-bind="$attrs"
class="icon"
aria-hidden="true"
>
<use xlink:href="#icon-delete" />
</svg>
<svg v-else v-on="$listeners" v-bind="$attrs" class="icon" aria-hidden="true">
<use xlink:href="#icon-set_update_hov" />
</svg>
</a>
</template>
<script>
export default {
name: 'MyButton',
props: ['title', 'icon']
}
</script>
<style scoped>
.icon {
width: 60px;
height: 30px;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
background-color: transparent;
border-radius: 5px;
margin: 0 5px;
padding: 0 5px;
}
.icon:active {
background-color: powderblue;
}
</style>
7、
parent和children
## $parent: 父实例/祖辈实例,如果当前实例有的话。
## $children: 当子组件/子孙组件,注意:$children 并不保证顺序,也不是响应式的
8、作用域插槽
作用:`传递标签结构给子组件,父组件中可以访问子组件中的数据,父子互相通信`
代码片段:
## 父组件中:
<template>
<div>
<Son :todos="todos">
<template v-slot:xh="{todo,index}">
<span>序号:{{index + 1}}--</span>
<span :style="{color:index & 1 ? 'red' : 'green'}">{{todo.content}}</span>
</template>
</Son>
</div>
</template>
<script>
import Son from './Son'
export default {
name: 'Father',
components: {
Son
},
data() {
return {
todos: [
{ id: 0, content: '内容1' },
{ id: 1, content: '内容2' },
{ id: 2, content: '内容3' },
{ id: 3, content: '内容4' },
{ id: 4, content: '内容5' }
]
}
}
}
</script>
<style scoped>
</style>
## 子组件中:
<template>
<ul>
<li v-for="item in todos" :key="item.id">
<slot name="xh" :todo="item" :index="item.id"></slot>
</li>
</ul>
</template>
<script>
export default {
name: 'Son',
props: ['todos']
}
</script>
<style scoped>
ul {
list-style: none;
}
</style>
9、vuex
作用:`任意组件进行通信`
流程:
组件中通过`this.$store.dispatch('actionName',[data])`触发对应的action函数调用
action函数中通过`commit('mutationName',[data])`触发对应的mutation函数调用
mutation函数中通过`state[data]=data`修改state数据
state数据一旦被修改,视图更新,用户看到的就是最新的数据
#注意:
`mutations模块可用于直接操作状态数据
actions模块一般是用来触发对应mutation函数调用,从而修改状态数据
actions模块一般用来发送异步请求,间接修改状态数据`
#使用步骤:
1、`src目录下整一个store文件夹,创建一个index.js文件作为被引入文件`
2、目录结构如下:
src/
store/
index.js
state.js
mutations.js
actions.js
getters.js
3、入口文件main.js中引入store,并且`注册store,为了让所有组件都能访问store`
4、走vuex流程
5、组件中可以通过辅助函数访问相关数据
# 代码片段:
## index.js文件中:
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'
Vue.use(Vuex)
export default new Vuex.Store({
state,
mutations,
actions,
getters,
modules: {
}
})
## actions模块中:
export default {
test ({ commit }, testName) {
commit('TEST', testName)
}
}
## mutations模块中:
export default {
TEST (state, testName) {
state.testName = testName
}
}
## state模块中:
export default {
testName: ''
}
## 入口文件main.js中
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store
}).$mount('#app')
## 组件中:
<template>
<h1>{{testName}}</h1>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'App',
computed: {
...mapState(['testName'])
},
mounted() {
this.$store.dispatch('test', '测试数据~~')
}
}
</script>
<style scoped>
</style>
10、mixin
作用:`复用代码逻辑`,分为局部`混入mixin`和`全局混入mixin`,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先,同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。
## 代码片段:
## 登录组件:
<template>
<PublicCode type="登录" />
</template>
<script>
import PublicCode from './PublicCode'
export default {
name: 'Login',
components: {
PublicCode
}
}
</script>
<style scoped>
</style>
## 注册组件
<template>
<PublicCode type="注册" />
</template>
<script>
import PublicCode from './PublicCode'
export default {
name: 'Register',
components: {
PublicCode
}
}
</script>
<style scoped>
</style>
## PublicCode组件(抽取公共代码成单独组件)
<template>
<div>
<template v-if="$attrs.type==='登录'">
<h1>登录</h1>用户名:
<input type="text" v-model="loginData.username" />
<br />密码:
<input type="password" v-model="loginData.password" />
<br />
<button @click="login">登录</button>
</template>
<template v-else>
<h1>注册</h1>用户名:
<input type="text" v-model="registerData.username" />
<br />密码:
<input type="password" v-model="registerData.password" />
<br />确认密码:
<input type="password" v-model="registerData.repassword" />
<br />电话:
<input type="text" v-model="registerData.phone" />
<br />
<button @click="register">注册</button>
<br />
</template>
</div>
</template>
<script>
import { loginOrRegister } from './mixins'
export default {
name: 'PublicCode',
mixins: [loginOrRegister],
methods: {
login() {
this.publicCode('login')
},
register() {
this.publicCode('register')
}
}
}
</script>
<style scoped>
</style>
## mixins.js文件
export const loginOrRegister = {
data () {
return {
loginData: {
username: '',
password: ''
},
registerData: {
username: '',
password: '',
repassword: '',
phone: ''
}
}
},
methods: {
publicCode (type) {
if (type === 'login') {
const { username, password } = this.loginData
if (!username || !password) return
alert(`您的用户名是${username},密码是${password}`)
this.loginData = {}
} else {
const { username, password, repassword, phone } = this.registerData
if (!username || !password || !repassword || !phone) return
if (password !== repassword) return
alert(`您的用户名是${username},密码是${password},手机号为${phone}`)
this.registerData = {}
}
}
}
}
11、native修饰符
作用:`监听组件根元素的原生事件`,在没有`.native`前,如果监听的是非原生事件,那么内部必须要绑定一个原生事件并且分发一个自定义事件才能让外部使用,在有`.native`后,可直接在组件标签上监听原生事件,内部不需要做什么,可以通过`event.target`来判断当前触发的是哪一个元素(`冒泡原理`)
## 代码片段:
## App组件:
<template>
<MyButton @click.native="myBtn" />
</template>
<script>
import MyButton from './components/MyButton'
export default {
name: 'App',
components: {
MyButton
},
methods: {
myBtn() {
console.log(event.target)
}
}
}
</script>
<style scoped>
</style>
## MyButton组件:
<template>
<div>
<h1 @click="$emit('click')">1111</h1>
<h1>2222</h1>
</div>
</template>
<script>
export default {
name: 'MyButton'
}
</script>
<style scoped>
</style>