記得去年公司招聘前端工程師的時候,我要負責准備面試題去考驗面試者,讓我記憶深刻的有一件事,我看大多數面試者簡歷上都寫了熟練掌握Vuex,然而當我問起的時候,大部分回答都支支吾吾,解釋不清,而當我提起與Vuex書寫相似的Vux的時候,偶爾會被面試者反問到:“這不是一個東西嗎?”,和我一同負責面試的技術總監(python,負責后台)也會充滿疑惑,也會小聲問道:“不是一樣的嗎?”,我就只好解釋完全不是一個東西,一個是狀態管理模式,一個是移動端UI組件庫等等,說得我口干舌燥......

過了這么久,怎么才突然想起寫一篇這樣的博客呢,還是因為上上篇博客所提到的那個朋友,也問過我Vuex究竟是什么?正好趁公司項目上線后的這段修整期,有點無聊,寫點博客加深一下印象,也希望能幫助更多人理解通Vuex的用法,話不多說,開始步入正題吧......
一.Vuex是什么?
介紹:Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。
理解:核心就是 store(倉庫),倉庫是用來干什么的?你就當它用來儲存東西的。
二.上方介紹提到的狀態管理模式是什么?
首先我們先看一張圖:

圖中的狀態管理的各部分含義為:
- state,驅動應用的數據源;
- view,以聲明方式將 state 映射到視圖;
- actions,響應在 view 上的用戶輸入導致的狀態變化。
在代碼中的體現位置為:
new Vue({ // state data () { return { count: 0 } }, // view template: ` <div>{{ count }}</div> `, // actions methods: { increment () { this.count++ } } })
三.我們什么時候應該用到Vuex呢?
1.小應用不建議使用Vuex,因為小項目使用 Vuex 可能會比較繁瑣冗余;
2.中大型單頁應用,因為要考慮如何更好地在組件外部管理狀態,Vuex 將會成為自然而然的選擇;
四.對於使用Vuex的理解是什么?
由於Vue是單向數據流,子組件內部不能直接修改從父級傳遞過來的數據,子組件與子組件之間無法相互傳遞數據。如果我們想讓兩個子組件之間進行通信的話,可以借助子組件 A 向父組件傳值,父組件接收子組件 A 的數據后再傳給 B 組件這樣的方式進行通信。
但是這樣會有一個問題,就是如果子組件 A 的父組件上面還有一層爺爺組件,或者還有更多祖父類型的層級,那么是不是會很麻煩。
因此,我們會想到能不能我們將一個共有的數據存在一個特定的地方,用的時候自己去拿,這樣就不需要一層層傳值,於是乎 Vuex 就應運而生了。
五.創建項目,開始實戰
創建項目:我的第二篇博客介紹到了怎么創建項目,這里就不多說了,傳送門:https://www.cnblogs.com/hejun26/p/8540404.html;
安裝vuex:
npm install vuex --save
項目目錄(具體處已用箭頭標注):

目錄我們可以看到有一個父組件HelloWorld和兩個子組件JobList和JobTop,我們接下來就在這幾個父子組件里面來介紹傳值方法;
HelloWorld.vue
我們在父組件里面引入兩個子組件,並呈現出數據,方便接下來我們繼續傳值操作:
<template>
<div>
<job-top :job="job"></job-top>
<job-list :jobs="jobs"></job-list>
</div>
</template>
<script>
import JobTop from './component/JobTop';
import JobList from './component/JobList';
export default {
name: 'HelloWorld',
props: {},
data () {
return {
job:'',
jobs:[]
}
},
components:{
JobTop,
JobList,
},
methods: {
getListInfo() {
//這里模擬的接口數據
this.getListInfoSucc({
"code":true,
"job":"web",
"data":{
"jobs":[
{
"id":1,
"name":"web"
},
{
"id":2,
"name":"C++"
},
{
"id":3,
"name":"python"
},
{
"id":4,
"name":"java"
}
]
}
})
},
getListInfoSucc(res){
if(res.code){
this.job=res.job;
this.jobs=res.data.jobs;
}
},
},
mounted() {
this.getListInfo();
},
}
</script>
<style scoped>
</style>
JobTop.vue
這里我們繼續單個數據呈現,到時候方便顯示切換后的數據:
<template>
<p>{{job}}</p>
</template>
<script>
export default {
name:'JobTop',
props:{
job:String,
},
data(){
return{
}
},
}
</script>
<style>
</style>
JobList.vue
這里是父組件里面通過接口獲取的數組,進行遍歷所呈現出列表形式,我們之后再進行點擊傳值
<template>
<div>
<button v-for="item of jobs" :key="item.id">{{item.name}}</button>
</div>
</template>
<script>
export default {
name:'JobList',
props:{
jobs:Array,
},
data(){
return{
}
},
}
</script>
<style>
</style>
頁面呈現效果如下:

好,現在頁面呈現出來了,我接着要寫點擊事件來進行傳值,我們會想到三種傳值方式:
1.父子組件傳值
在 jobList.vue 中定義 jobClick 方法,將 changeJob 方法注入,並將所選中的 job 值傳入
<template>
<div>
<button v-for="item of jobs" :key="item.id" @click="jobClick(item.name)">{{item.name}}</button>
</div>
</template>
<script>
export default {
name:'JobList',
props:{
jobs:Array,
},
data(){
return{
}
},
methods:{
//父組件傳值
jobClick(name){
this.$emit("changeJob",name)
}
}
}
</script>
<style>
</style>
在父組件 HelloWorld.vue 中接收
<template>
<div>
<job-top :job="job"></job-top>
<job-list :jobs="jobs" @changeJob="ChangeJobClick"></job-list>
</div>
</template>
<script>
import JobTop from './component/JobTop';
import JobList from './component/JobList';
export default {
name: 'HelloWorld',
props: {},
data () {
return {
job:'',
jobs:[]
}
},
components:{
JobTop,
JobList,
},
methods: {
getListInfo() {
this.getListInfoSucc({
"code":true,
"job":"web",
"data":{
"jobs":[
{
"id":1,
"name":"web"
},
{
"id":2,
"name":"C++"
},
{
"id":3,
"name":"python"
},
{
"id":4,
"name":"java"
}
]
}
})
},
getListInfoSucc(res){
if(res.code){
this.job=res.job;
this.jobs=res.data.jobs;
}
},
ChangeJobClick(name){
console.log("選中的崗位",name);
this.job=name;
}
},
mounted() {
this.getListInfo();
},
}
</script>
<style scoped>
</style>
在父組件的的 job-list 標簽中寫上子組件 JobLict.vue注入的 changeJob方法,並指向 ChangeJobClick 方法,ChangeJobClick 方法中接收傳過來的選中的 job 值,並將該值傳遞給子組件 JobTop.vue,JobTop頁面無需改動,頁面效果如下:

2.原型鏈繼承
正好之前一篇博客講過 javascript 的原型鏈繼承,vue 其實就是 js 的一種框架,它也有 prototype 屬性,我們叫做 Bus總線方法。如下:
JobList.vue
<template>
<div>
<button v-for="item of jobs" :key="item.id" @click="jobClick(item.name)">{{item.name}}</button>
</div>
</template>
<script>
import Vue from 'vue'
Vue.prototype.bus=new Vue();
export default {
name:'JobList',
props:{
jobs:Array,
},
data(){
return{
}
},
methods:{
//原型鏈繼承
jobClick(name){
console.log("點擊某個崗位:",name)
this.bus.$emit("changeJob",name)
}
}
}
</script>
<style>
</style>
我們現在 Vue 的 prototype 屬性中引入 bus ,然后在 button 按鈕上綁定 jobClick 點擊事件,並傳入點擊的 job ,然后在 bus總線上注入 changeJob 方法,並將點擊的 job 值傳入,我們再來看一下
JobTop.vue
<template>
<p>{{jobself}}</p>
</template>
<script>
export default {
name:'JobTop',
props:{
job:String,
},
data(){
return{
jobself:this.job
}
},
mounted () {
//原型鏈繼承
var that=this;
this.bus.$on("changeJob",function(name){
console.log("點擊后傳過來的job",name)
that.jobself=name;
})
}
}
</script>
<style>
</style>
HelloWorld頁面無需更改,最終效果圖如下:

3.Vuex
接下來介紹今天的主角 Vuex ,Vuex 說白了就像是開辟了一個組件共有的內存空間,組件都可以去取去用;
我們在src創建 store 文件夾,里面添加一個 index.js 來存儲共享數據,在 main.js 中引入 store文件,並在 Vue 實例中注入 store 。
main.js
import Vue from 'vue' import App from './App' import router from './router' import Vuex from 'vuex' import store from './store' Vue.use(VueRouter) Vue.use(Vuex) Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, store, components: { App }, template: '<App/>' })
JobList.vue
<template>
<div>
<button v-for="item of jobs" :key="item.id" @click="jobClick(item.name)">{{item.name}}</button>
</div>
</template>
<script>
export default {
name:'JobList',
props:{
jobs:Array,
},
data(){
return{
}
},
methods:{
//vuex
jobClick(name){
this.$store.dispatch("changeJob",name)
},
}
}
</script>
<style>
</style>
在 JobList.vue 中添加 jobClick 點擊方法,根據上面說的流程將一個 changJob 事件通過 dispatch 方法注入,並將所選的 job 值傳入。
store文件夾下的index.js
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex); export default new Vuex.Store({ state: { job:"web" }, actions: { changeJob(ctx,name){ console.log("action",ctx,name); ctx.commit("changeJob",name); } }, mutations: { changeJob(state,name){ console.log("mutation",state,name); state.job=name; } } })
JobTop.vue
獲取到job的值,渲染出來
<template>
<p>{{this.$store.state.job}}</p>
</template>
<script>
export default {
name:'JobTop',
props:{
job:String,
},
data(){
return{
}
},
}
</script>
<style>
</style>
HelloWorld頁面不需要改動,頁面效果如下:

以上就是vue的幾種組件傳參方式,大家想了解的更加官方,請閱覽官方地址:https://vuex.vuejs.org/zh/
根據官方 Vuex 目錄,我們再深一步講解一下核心概念,對上面的代碼進行一下重構。
JobList.vue
<template>
<div>
<button v-for="item of jobs" :key="item.id" @click="jobClick(item.name)">{{item.name}}</button>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
name:'JobList',
props:{
jobs:Array,
},
data(){
return{
}
},
methods:{
//vuex
jobClick(name){
this.changeJob(name)
},
...mapMutations(['changeJob'])
}
}
</script>
<style>
</style>
尤大大為我們提供了封裝好的一些方法,如上面的 ...mapMutations() ,意思是 mutations 里有一個 changeJob ,我們將其映射到該組件的一個 changeJob的方法里,這樣就可以直接調用 changeJob 方法並傳值了
JobTop.vue
<template>
<p>{{this.job}}</p>
</template>
<script>
import { mapState } from 'vuex'
export default {
name:'JobTop',
props:{
},
data(){
return{
}
},
computed:{
...mapState(['job'])
},
}
</script>
<style>
</style>
在 mapState 里有一個 job 屬性,將該屬性映射到該組件的 job 屬性里,
效果圖如下:

總結:Vuex的介紹就先到這里了,希望大家真的理解了,不會在面試的時候被面試官問Vuex是什么,還在支支吾吾就不好了,還有什么問題或者不理解的地方歡迎在下方留言,我有什么做錯的地方也可以提出,我會進行修改,謝謝大家了。

