記得去年公司招聘前端工程師的時候,我要負責准備面試題去考驗面試者,讓我記憶深刻的有一件事,我看大多數面試者簡歷上都寫了熟練掌握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是什么,還在支支吾吾就不好了,還有什么問題或者不理解的地方歡迎在下方留言,我有什么做錯的地方也可以提出,我會進行修改,謝謝大家了。