Vue組件間通信的實(shí)現(xiàn)方法講解
前言
前面介紹過(guò)幾種可以實(shí)現(xiàn)組件間通信的方式props、ref、自定義事件綁定、全局事件總線、插槽…,這些要么就是實(shí)現(xiàn)組件間通信只能在特定條件下使用,要么就是實(shí)現(xiàn)起來(lái)太復(fù)雜,今天介紹到的vuex是Vue中一款強(qiáng)大的插件,使用vuex后可以實(shí)現(xiàn)任意組件間通信,并且支持模塊化管理。接下來(lái)將會(huì)圍繞一個(gè)計(jì)數(shù)案例進(jìn)行展開,逐步引入vuex如何使用。

核心代碼:
<template>
<div>
<h2>目前的數(shù)值結(jié)果是:{{n}}</h2>
<div class="container1">
<select v-model.number="step">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="addNum">增加</button>
<button @click="subNum">減少</button>
<button @click="oddNum">偶數(shù)再加</button>
<button @click="longTimeAdd">延遲增加</button>
</div>
</div>
</template>
<script>
export default {
// eslint-disable-next-line vue/multi-word-component-names
name:"Counts",
data(){
return {
n:0,
step:1
}
},
methods:{
addNum(){
this.n+=this.step
},
subNum(){
this.n-=this.step
},
oddNum(){
if (this.n%2==0){
this.n+=this.step
}
},
longTimeAdd(){
setTimeout(() => {
this.n+=this.step
}, 1000);
}
}
}
</script>
<style>
.container1>*{
margin-left: 10px;
}
</style>
一、vuex是什么
簡(jiǎn)單點(diǎn)說(shuō):是一個(gè)數(shù)據(jù)倉(cāng)庫(kù),用于數(shù)據(jù)存儲(chǔ),數(shù)據(jù)共享,可以進(jìn)行數(shù)據(jù)存放與獲取,并全程監(jiān)視數(shù)據(jù)的動(dòng)向
使用官方語(yǔ)言入下:
Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式 + 庫(kù)。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化。
這個(gè)狀態(tài)自管理應(yīng)用包含以下幾個(gè)部分:
- 狀態(tài),驅(qū)動(dòng)應(yīng)用的數(shù)據(jù)源;
- 視圖,以聲明方式將狀態(tài)映射到視圖;
- 操作,響應(yīng)在視圖上的用戶輸入導(dǎo)致的狀態(tài)變化。

但是,當(dāng)我們的應(yīng)用遇到多個(gè)組件共享狀態(tài)時(shí),單向數(shù)據(jù)流的簡(jiǎn)潔性很容易被破壞:
多個(gè)視圖依賴于同一狀態(tài)。
來(lái)自不同視圖的行為需要變更同一狀態(tài)。
對(duì)于問(wèn)題一,傳參的方法對(duì)于多層嵌套的組件將會(huì)非常繁瑣,并且對(duì)于兄弟組件間的狀態(tài)傳遞無(wú)能為力。對(duì)于問(wèn)題二,我們經(jīng)常會(huì)采用父子組件直接引用或者通過(guò)事件來(lái)變更和同步狀態(tài)的多份拷貝。以上的這些模式非常脆弱,通常會(huì)導(dǎo)致無(wú)法維護(hù)的代碼。
因此,我們?yōu)槭裁床话呀M件的共享狀態(tài)抽取出來(lái),以一個(gè)全局單例模式管理呢?在這種模式下,我們的組件樹構(gòu)成了一個(gè)巨大的“視圖”,不管在樹的哪個(gè)位置,任何組件都能獲取狀態(tài)或者觸發(fā)行為!
通過(guò)定義和隔離狀態(tài)管理中的各種概念并通過(guò)強(qiáng)制規(guī)則維持視圖和狀態(tài)間的獨(dú)立性,我們的代碼將會(huì)變得更結(jié)構(gòu)化且易維護(hù)。
這就是 Vuex 背后的基本思想,借鑒了 Flux、Redux 和 The Elm Architecture。與其他模式不同的是,Vuex 是專門為 Vue.js 設(shè)計(jì)的狀態(tài)管理庫(kù),以利用 Vue.js 的細(xì)粒度數(shù)據(jù)響應(yīng)機(jī)制來(lái)進(jìn)行高效的狀態(tài)更新。

vuex適用于大型單頁(yè)應(yīng)用的開發(fā),如果是小型的不推薦使用。
其中vuex3適配vue2,vuex4適配vue3。
vue工作原理:
vuex中主要包含三個(gè)部分
- Actions:相當(dāng)于對(duì)數(shù)據(jù)的預(yù)處理(可以在這里使用異步加載進(jìn)行數(shù)據(jù)校驗(yàn)等)
- Mutations (跟開發(fā)者工具關(guān)系密切)
- State (存放數(shù)據(jù)的地方,經(jīng)過(guò)校驗(yàn)與變換的數(shù)據(jù)都存放在這里)
vuex中的兩種數(shù)據(jù)操作
- dispatch:將數(shù)據(jù)交給Actions
- commit:將數(shù)據(jù)交給Mutations
vuex在項(xiàng)目中使用
大致有兩種(實(shí)質(zhì)上一樣):
- 1.vuex/store.js 創(chuàng)建vuex文件夾,將代碼放進(jìn)store.js中
- 2.store/index.js 創(chuàng)建store文件夾,將代碼放進(jìn)index.js中
安裝vuex
npm i vuex
二、vuex的使用
繼續(xù)從以下三個(gè)模塊與兩個(gè)方法講起:
三個(gè)模塊:
- Actions:相當(dāng)于對(duì)數(shù)據(jù)的預(yù)處理(可以在這里使用異步加載進(jìn)行數(shù)據(jù)校驗(yàn)等)
- Mutations (跟開發(fā)者工具關(guān)系密切)
- State (存放數(shù)據(jù)的地方,經(jīng)過(guò)校驗(yàn)與變換的數(shù)據(jù)都存放在這里)
兩個(gè)方法:
- dispatch:將數(shù)據(jù)交給Actions
- commit:將數(shù)據(jù)交給Mutations
如果調(diào)用vuex的dispatch方法會(huì)將數(shù)據(jù)提交到Actions中的函數(shù),此時(shí)數(shù)據(jù)并不是最終的結(jié)果,在Actions中可以對(duì)數(shù)據(jù)進(jìn)行進(jìn)一步加工,然后調(diào)用commit方法提交,在Actions與Mutations中定義的函數(shù)可以獲取到上下文找到state對(duì)象中存儲(chǔ)的數(shù)據(jù)。
接下來(lái)結(jié)合代碼看一下vuex如何使用(這一段代碼暴露出去一個(gè)可以使用的vuex對(duì)象):
import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex)
const actions={
odd_Num(comtext,value){
// 可以通過(guò)上下文comtext找到commit方法,dispatch方法
// 可以找到state對(duì)象,并找到其中的屬性
if (comtext.state.n%2===0){
comtext.commit("add_Num",value)
}
},
long_Time_Add(comtext,value){
setTimeout(() => {
comtext.commit("add_Num",value)
}, 1000);
}
}
const mutations={
add_Num(state,value){
// 這里的state被稱為上下文,這里可以拿到state中的數(shù)據(jù)
// console.log(state,value)
state.n+=value
},
sub_Num(state,value){
// console.log(state,value)
state.n-=value
},
}
//數(shù)據(jù)源,源數(shù)據(jù)
const state={
n:0
}
//創(chuàng)建一個(gè)vuex對(duì)象
export default new Vuex.Store({
actions,
mutations,
state
})
以下代碼是使用vuex:
main.js
import App from "./App"
//這個(gè)便是剛才封裝的vuex對(duì)象
import store from "./store"
import Vue from "vue"
new Vue({
el:"#App",
store,
render:h=>h(App)
})
功能性組件:
export default {
// eslint-disable-next-line vue/multi-word-component-names
name:"Counts",
data(){
return {
step:1
}
},
computed:{
n(){
return this.$store.state.n
}
}
,
methods:{
addNum(){
this.$store.commit("add_Num",this.step)
},
subNum(){
this.$store.commit("sub_Num",this.step)
},
oddNum(){
this.$store.dispatch("odd_Num",this.step)
},
longTimeAdd(){
this.$store.dispatch("long_Time_Add",this.step)
}
}
}
除此之外vuex還有一個(gè)重要的模塊供我們使用getters,這里面的方法通常用于獲取定制后的state中的屬性
比如下面一個(gè)實(shí)例,就是將state中的n擴(kuò)大10倍后返回了出去。
const getters={
numBig(state){
return state.n*10
}
}
三、四個(gè)重要的映射函數(shù)
如果vuex中的函數(shù)較多時(shí),一個(gè)一個(gè)引入會(huì)相當(dāng)麻煩,官方給出了四個(gè)映射函數(shù),可以根據(jù)自己的需求將想要的函數(shù)一下引進(jìn)來(lái),分別是:mapState,mapGetters,mapActions,mapMutations。
以下兩種寫法,在使用時(shí)效果是一樣的
不使用映射函數(shù):
<script>
export default {
// eslint-disable-next-line vue/multi-word-component-names
name:"Counts",
data(){
return {
step:1
}
},
computed:{
n(){
return this.$store.state.n
},
bigNum(){
// 以下兩種寫法均可以
// return this.$store.getters["numBig"]
return this.$store.getters.numBig
}
}
,
methods:{
addNum(){
this.$store.commit("add_Num",this.step)
},
subNum(){
this.$store.commit("sub_Num",this.step)
},
oddNum(){
this.$store.dispatch("odd_Num",this.step)
},
longTimeAdd(){
this.$store.dispatch("long_Time_Add",this.step)
}
}
}
</script>
使用映射函數(shù):
<script>
import {mapState,mapGetters,mapActions,mapMutations} from "vuex"
export default {
// eslint-disable-next-line vue/multi-word-component-names
name:"Counts",
data(){
return {
step:1
}
},
computed:{
...mapState(["n"]),
...mapGetters({bigNum:"numBig"})
}
,
methods:{
// 傳入對(duì)象是常規(guī)寫法,當(dāng)對(duì)象中的鍵值相等時(shí),可以用傳入列表的方式進(jìn)行傳參
// 例如上面的mapState(["n"])
...mapMutations({addNum:"add_Num",subNum:"sub_Num"}),
...mapActions({oddNum:"odd_Num",longTimeAdd:"long_Time_Add"})
}
}
</script>
四、多組件數(shù)據(jù)共享
經(jīng)過(guò)將多個(gè)組件使用到的數(shù)據(jù)提取到vuex,可以實(shí)現(xiàn)多組件間的數(shù)據(jù)共享,也就是說(shuō)在a組件修改共享數(shù)據(jù)之后,在b組件可以渲染出數(shù)據(jù)改變后的結(jié)果,同樣b組件中修改a組件也可以渲染。
以下案例一個(gè)計(jì)數(shù)組件負(fù)責(zé)計(jì)數(shù),一個(gè)person組件,負(fù)責(zé)將人員列表渲染出來(lái),person組件支持增加用戶,我們要做的就是person組件中進(jìn)行人員添加后,計(jì)數(shù)組件感應(yīng)到人數(shù)變動(dòng),重新渲染列表,將總?cè)藬?shù)進(jìn)行改變。

vuex插件
import Vue from "vue"
import Vuex from "vuex"
import {nanoid} from "nanoid"
Vue.use(Vuex)
const actions={
odd_Num(comtext,value){
// 可以通過(guò)上下文comtext找到commit方法,dispatch方法
// 可以找到state對(duì)象,并找到其中的屬性
if (comtext.state.n%2===0){
comtext.commit("add_Num",value)
}
},
long_Time_Add(comtext,value){
setTimeout(() => {
comtext.commit("add_Num",value)
}, 1000);
},
add_Person(context,value){
var someone={id:nanoid(),name:value}
context.commit("add_Person",someone)
}
}
const mutations={
add_Num(state,value){
// 這里的state被稱為上下文,這里可以拿到state中的數(shù)據(jù)
// console.log(state,value)
state.n+=value
},
sub_Num(state,value){
// console.log(state,value)
state.n-=value
},
add_Person(state,value){
state.personList.unshift(value)
}
}
const state={
n:0,
personList:[
{
id:"0001",
name:"張三"
}
]
}
const getters={
numBig(state){
return state.n*10
},
personNum(state){
return state.personList.length
// console.log("index"+state.personList.length)
}
}
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
計(jì)數(shù)組件
<template>
<div>
<h2>目前的數(shù)值結(jié)果是:{{n}}</h2>
<h2>放大后的數(shù)值結(jié)果是:{{bigNum}}</h2>
<div class="container1">
<select v-model.number="step">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="addNum(step)">增加</button>
<button @click="subNum(step)">減少</button>
<button @click="oddNum(step)">偶數(shù)再加</button>
<button @click="longTimeAdd(step)">延遲增加</button>
</div>
<h2>當(dāng)前的總?cè)藬?shù)為{{personNum}}</h2>
</div>
</template>
<script>
import {mapState,mapGetters,mapActions,mapMutations} from "vuex"
export default {
// eslint-disable-next-line vue/multi-word-component-names
name:"Counts",
data(){
return {
step:1
}
},
computed:{
...mapState(["n"]),
...mapGetters({bigNum:"numBig",personNum:"personNum"})
}
,
methods:{
// 傳入對(duì)象是常規(guī)寫法,當(dāng)對(duì)象中的鍵值相等時(shí),可以用傳入列表的方式進(jìn)行傳參
// 例如上面的mapState(["n"])
...mapMutations({addNum:"add_Num",subNum:"sub_Num"}),
...mapActions({oddNum:"odd_Num",longTimeAdd:"long_Time_Add"})
}
}
</script>
<style>
.container1>*{
margin-left: 10px;
}
</style>
person組件
<template>
<div>
<h2>目前的計(jì)算結(jié)果為{{n}}</h2>
//監(jiān)聽的鍵盤事件,當(dāng)輸入enter時(shí)添加用戶
<input v-model="tempname" @keydown.enter="add_Person(tempname)">
<ul>
<li v-for="p in personList" :key="p.id">
學(xué)生id:{{p.id}} 學(xué)生姓名:{{p.name}}
</li>
</ul>
</div>
</template>
<script>
import {mapState,mapActions} from "vuex"
export default {
// eslint-disable-next-line vue/multi-word-component-names
name:"Persons",
data(){
return {
tempname:""
}
},
computed:{
...mapState(["n","personList"])
},
methods:{
...mapActions(["add_Person"])
}
}
</script>
<style>
</style>
五、模塊化使用vuex
如果不按照數(shù)據(jù)功能對(duì)數(shù)據(jù)進(jìn)行模塊劃分,那么vuex將會(huì)顯得非常雜亂,我們希望vuex可以根據(jù)數(shù)據(jù)的不同作用將其進(jìn)行模塊化,便于我們管理,對(duì)于接下來(lái)的代碼大家需要熟悉的是如何使用劃分后的函數(shù)、函數(shù)映射。
劃分后的代碼大概長(zhǎng)這個(gè)樣子,其中store中的index會(huì)將count.js與person.js引入并使用。vue代碼中使用vuex依舊是引入index.js,值得注意的是進(jìn)行模塊劃分后的vuex,在使用時(shí)跟以往有了不同。詳細(xì)看代碼

index.js
import Vue from "vue"
import Vuex from "vuex"
import counts from "./count"
import person from "./person"
Vue.use(Vuex)
export default new Vuex.Store({
modules:{
counts,
person
}
})
count.js
// import axios from "axios"
export default{
namespaced:true,
actions:{
/* ... */
},
mutations:{
/* ... */
},
state:{
n:0,
},
getters:{
numBig(state){
return state.n*10
}
}
}
person.js
import {nanoid} from "nanoid"
export default{
namespaced:true,
actions:{
/* ... */
},
mutations:{
/* ... */
},
state:{
personList:[
{
id:"0001",
name:"張三"
}
]
},
getters:{
personNum(state){
return state.personList.length
// console.log("index"+state.personList.length)
}
}
}
count.vue
<template>
<div>
<h2>目前的數(shù)值結(jié)果是:{{n}}</h2>
<h2>放大后的數(shù)值結(jié)果是:{{bigNum}}</h2>
<div class="container1">
<select v-model.number="step">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="addNum(step)">增加</button>
<button @click="subNum(step)">減少</button>
<button @click="oddNum(step)">偶數(shù)再加</button>
<button @click="longTimeAdd(step)">延遲增加</button>
</div>
<h2>當(dāng)前的總?cè)藬?shù)為{{personNum}}</h2>
</div>
</template>
<script>
import {mapState,mapGetters,mapActions,mapMutations} from "vuex"
export default {
// eslint-disable-next-line vue/multi-word-component-names
name:"Counts",
data(){
return {
step:1
}
},
computed:{
...mapState("counts",["n"]),
...mapGetters("counts",{bigNum:"numBig"}),
...mapGetters("person",{personNum:"personNum"})
}
,
methods:{
// 傳入對(duì)象是常規(guī)寫法,當(dāng)對(duì)象中的鍵值相等時(shí),可以用傳入列表的方式進(jìn)行傳參
// 例如上面的mapState(["n"])
...mapMutations("counts",{addNum:"add_Num",subNum:"sub_Num"}),
...mapActions("counts",{oddNum:"odd_Num",longTimeAdd:"long_Time_Add"})
}
}
</script>
<style>
.container1>*{
margin-left: 10px;
}
</style>
persons.vue
<template>
<div>
<h2>目前的計(jì)算結(jié)果為{{n}}</h2>
<input v-model="tempname" @keydown.enter="addPerson(tempname)">
<button v-if="tag" @click="tag=!tag">目前可增加任意姓氏</button>
<button v-if="!tag" @click="tag=!tag">目前只可以添加王氏</button>
<ul>
<li v-for="p in personList" :key="p.id">
學(xué)生id:{{p.id}} 學(xué)生姓名:{{p.name}}
</li>
</ul>
</div>
</template>
<script>
import {mapState,mapActions} from "vuex"
export default {
// eslint-disable-next-line vue/multi-word-component-names
name:"Persons",
data(){
return {
tag:true,
tempname:""
}
},
computed:{
...mapState("counts",["n"]),
...mapState("person",["personList"])
},
methods:{
...mapActions("person",["add_Person"]),
addPerson(){
if (this.tempname===""){
return ""
}
if(this.tag){
// 映射出來(lái)的函數(shù)
this.add_Person(this.tempname)
}else{
//[Vue warn]: Error in v-on handler: "TypeError: this.addPersonWang is not a function"
// 如果沒有使用映射的話,不能這樣使用
// this.addPersonWang(this.tempname)
// 正確的使用方式(使用模塊命名空間進(jìn)行限定)
this.$store.dispatch("person/addPersonWang",this.tempname)
}
this.tempname=""
},
}
}
</script>
<style>
</style>
除此之外還可以通過(guò)下面方法獲取到vuex中的state中的屬性。
this.$store.state.模塊名.屬性
this.$store.state.loginModel.userInfo
到此這篇關(guān)于Vue組件間通信的實(shí)現(xiàn)方法講解的文章就介紹到這了,更多相關(guān)Vue組件間通信內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vuex+axios+element-ui實(shí)現(xiàn)頁(yè)面請(qǐng)求loading操作示例
這篇文章主要介紹了vuex+axios+element-ui實(shí)現(xiàn)頁(yè)面請(qǐng)求loading操作,結(jié)合實(shí)例形式分析了vuex+axios+element-ui實(shí)現(xiàn)頁(yè)面請(qǐng)求過(guò)程中l(wèi)oading遮罩層相關(guān)操作技巧與使用注意事項(xiàng),需要的朋友可以參考下2020-02-02
vue路由攔截及頁(yè)面跳轉(zhuǎn)的設(shè)置方法
這篇文章主要介紹了vue路由攔截及頁(yè)面跳轉(zhuǎn)的設(shè)置方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-05-05
關(guān)于element-ui?單選框默認(rèn)值不選中的解決
這篇文章主要介紹了關(guān)于element-ui?單選框默認(rèn)值不選中的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09

