组件通信方式
父子组件通信
常用:props/$emit
父传子:
通过props向子组件传值。调用子组件时,可以用 静态传递 或者 使用 v-bind动态传递,子组件用props接受使用。
子传父:
通过$emit方法触发自定义事件。子组件通过this.$emit方法触发父组件上(调用子组件的那个语句)自定义事件,将值传递给自定义事件函数,通过$event获取。
其他:
v-model
子组件中,props中通过value字段来接收
子组件触发this.$emit(‘input’)事件,父组件改变v-model绑定的值
所以在组件中使用的时候,相当于下面的简写:
<custom v-bind:value="something" v-on:input="something = $event.target.value"></custom>
这通常用在子组件是表单元素的情况,例如调用elment-ui中的el-input等组件时。
.sync修饰符
允许props进行双向绑定,以this.$emit(update:PropName,newValue)的模式触发事件。
即:
<text-document v-bind:title.sync="doc.title"></text-document>
相当于:
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
this.$emit('update:title', newTitle)
通常用在父子组件都需要改变传入值的情况,比如:在调用el-dialog的时候,visible属性支持.sync修饰符是为了子组件中右上角叉号点击时能改变父组件中visible的绑定值,这样就能关闭dialog。
this.$refs(不推荐使用,容易造成状态混乱)
首先你的给子组件做标记。demo :
然后在父组件中,通过this.$refs.one就可以访问了这个自组件了,包括访问子组件的data里面的数据,调用它的函数。
this.refs如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
无直接关系组件间通信
常用:bus总线事件 原理可以参考:https://blog.csdn.net/bingqise5193/article/details/109406910
与this.$emit原理一致,都是在同一个vue实例内实行的,只不过放在了一个新的vue内,同样是利用了vm.$on/vm.$off/vm.$emit接口来实现的。
1、let bus = new Vue({})
2、Vue.prototype.Bus = bus
3、触发组件内 this.Bus.emit(‘userdifined’, ‘chufa’)
4、接受事件组件内
mounted () {
this.Bus.$on('userdifined', res => console.log(res))
},
destroyed () {
this.Bus.$off('userdifined')
}
简易集中式状态管理器
以上方法适合两个或不多的组件之间传值,如果有以下情况:
- 多视图依赖同一个状态
- 不同的视图需要变更同一个状态
用上面的方法虽然可以实现,但是会造成状态管理混乱,这种情况需要一个多组件共享的响应式数据管理中心,在此来实现一个简易版的:
数据共享中心:store.js
export default {
state: {
data: 0
},
change(r) {
this.state.data += r
}
}
调用:
<template>
<div>
<h1>componentA</h1>
<div>{{data.data}}</div>
<button @click="change(1)">click to change</button>
</div>
</template>
<script>
import store from '@/state/store'
export default {
data() {
return {
// 应该要给一个对象,不能store.state.data
// store.state.data为一个基础类型,赋值后,当store.state.data变更时与data并无关系
// 而store.state是一个对象,赋值为地址赋值,data与store.state指向同一个对象,才能触发响应式
data: store.state
}
},
methods: {
change(data) {
store.change(data)
}
}
}
</script>
建立一个状态管理中心,用于管理所有共用的状态,并且如果改变这些状态必须通过中心的事件来更改,这样就能记录更改状态的所有操作。
vuex的使用
vuex是什么
一个集中式存储管理所有组件的状态,当多个组件均需要使用或改变这个状态时,适合用vuex。Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地更新。例如用户信息、菜单信息、购物车信息很适合用vuex来实现
vuex的使用步骤
1. 安装插件: npm i vuex -S
2. 载入插件:import Vuex from 'vuex'
3. 注册插件:Vue.use(Vuex)
4. 实例化存储对象:const store = new Vuex.Store({
modules: {
count,
msg
}
})
5. 挂载:new Vue({
store,
render: h => h(App),
}).$mount('#app')
6. 使用存储对象
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
computed: {
...mapState({
msg: state => state.msg.msg,
count: state => state.count.count
}),
...mapGetters(['reversedMsg'])
},
methods: {
...mapMutations(['changeCount']),
...mapActions(['changeCountAsync'])
},
Tips:
- 具体的使用请参考vuex官方文档
- 更改state的操作必须通过commit mutations中的操作,mutations中只能有同步操作,这样dev tools能够记录回溯每次状态更改的情况,actions中可以有异步操作。
- actions中的第一个参数为context,但并不是实例,context对象包括以下:
vuex使用实战-购物车例子
具体的项目代码请查看:shop-cart
几个小TIPS:
- find/findIndex/filter/reduce/every的使用,reduce常用于求和
- import/export,具体参考:
- localStorage只能存储字符串,因此存储和取用对象时要注意
JSON.parse(window.localStorage.getItem('cart-goods'))
window.localStorage.setItem('cart-goods', JSON.stringify(state.goods))
- 作用域插槽:父组件只能访问父组件中的值,如果想要访问子组件中的值,则可以通过v-slot来实现:在父组件中引用子组件的行信息
<el-table-column prop="address" label="操作">
<template v-slot="scope">
<el-button @click="addProducts(scope.row)">加入购物车</el-button>
</template>
</el-table-column>
- 基于其他的值计算并响应式更改,请用vuex中的getters,另外,所有更改state的操作都要通过提交mutations。
- 如果某个操作需要在每次 mutation 之后调用,可以使用store的plugin,使用如下:
const myPlugin = store => {
// 当 store 初始化后调用
store.subscribe((mutation, state) => {
// 每次 mutation 之后调用
// mutation 的格式为 { type, payload }
// localStorage只能存储字符串
window.localStorage.setItem('cart-goods', JSON.stringify(state.goods))
})
}
export default new Vuex.Store({
plugins: [myPlugin],
state: {}
}
- 在表单元素中,如果绑定的是vuex中的值,要用:value去绑定,不能用v-model,更改vuex中的值需要通过mutations,因此采用:value+@change等事件去使用,实现双向绑定。对于change等事件,当你传递了自定义参数的时候,还想得到原来那个默认参数,就手动传递一个 $event
vuex原理
简易手写版:
let _Vue = null
class Store {
constructor(options) {
const {
state = {},
getters = {},
mutations = {},
actions = {}
} = options
this.state = _Vue.observable(state)
this.getters = Object.create(null)
Object.keys(getters).forEach(key => {
Object.defineProperty(this.getters, key, {
get: () => getters[key](state)
})
})
this._mutations = mutations,
this._actions = actions
}
commit (type, payload) {
this._mutations[type](this.state, payload)
}
dispatch (type, payload) {
this._actions[type](this, payload)
}
}
function install (Vue) {
_Vue = Vue
// 挂载$store
_Vue.mixin({
beforeCreate() {
if(this.$options.store){
// 如果是组件则不需要
_Vue.prototype.$store = this.$options.store
}
}
})
}
export default {
Store,
install
}