前言
我想寫一系列關於Vuex的入門文章,我是看着vuex官網文檔,結合自己從零搭建的vue項目來實踐vuex的知識。
Vuex入門系列:
-
Vuex入門簡單示例(三)
- Vuex入門簡單示例(五)
本文涉及知識點:
- vuex之mapState
- 獨立store.js文件
- vuex之getter
- vuex之mapGetters
這一篇我們學習下如何使用mapState
給首頁(Home.vue)添加一些內容,顯示登錄狀態、用戶名和密碼這三個狀態。
先看下vuex文檔對mapState的說明:
mapState輔助函數
當一個組件需要獲取多個狀態時,將這些狀態都聲明為計算屬性會有些重復和冗余。為了解決這個問題,我們可以使用mapState輔助函數幫助我們生成計算屬性。
把這個知識點結合到本示例中
首先,在需要的頁面導入mapState
src/components/Home.vue
import { mapState } from 'vuex'
在vue計算屬性computed里面使用mapState函數
computed: mapState({ isLogin: state => state.isLogin, username: state => state.username, password: state => state.password })
還要修改下<template>模板的代碼
src/components/Home.vue完整代碼如下:
<template> <div class="home"> 登錄狀態:{{isLogin}} <br> 用戶名:{{username}}<br> 密碼:{{password}} <br> 首頁 </div> </template> <script> import { mapState } from 'vuex' export default { name: 'Home', computed: mapState({ isLogin: state => state.isLogin, username: state => state.username, password: state => state.password }) } </script>
刷新瀏覽器回到登錄頁(登錄頁內容不變)
點登錄按鈕進入首頁后,效果如下:
等等,我發現一個問題,這里的computed好像被mapState獨用了,如果我們想寫一個普通的(非mapState里面的)計算屬性怎么辦?
假設data里面有兩個變量,我們需要計算出它們的相加的結果並顯示出來。
// ... data () { return { a: 10, b: 20 } }, // ...
這時computed需要改變一下,這就要用到對象展開運算符[...]
// ... computed: { sum () { return this.a + this.b }, ...mapState({ isLogin: state => state.isLogin, username: state => state.username, password: state => state.password }) } // ...
src/components/Home.vue完整代碼如下:
<template> <div class="home"> 登錄狀態:{{isLogin}} <br> 用戶名:{{username}}<br> 密碼:{{password}} <br> a + b = {{sum}} <br> 首頁 </div> </template> <script> import { mapState } from 'vuex' export default { name: 'Home', data () { return { a: 10, b: 20 } }, computed: { sum () { return this.a + this.b }, ...mapState({ isLogin: state => state.isLogin, username: state => state.username, password: state => state.password }) } } </script>
獨立store.js
隨着示例內容的增加,store的代碼會越來越多,導致main.js太長,不利於維護。現在把store單獨放一個js文件。
在src目錄下新建一個js文件store.js
src/store.js
// 導入vue和vuex import Vue from 'vue' import Vuex from 'vuex' // 引用Vuex Vue.use(Vuex) // 從main.js拷貝過來即可 const store = new Vuex.Store({ state: { isLogin: false, //登錄狀態 username: '', //用戶名 password: '' //密碼 }, mutations: { // 修改登錄狀態 changeLogin(state, data) { state.isLogin = data }, // 修改用戶名狀態 changeUsername(state, data) { state.username = data }, // 修改密碼狀態 changePassword(state, data) { state.password = data } } }) // 暴露(導出)store export default store
main.js需要刪掉一些代碼
src/main.js
import Vue from 'vue' import App from './App.vue' import VueRouter from 'vue-router' // import Vuex from 'vuex' (-) import store from './store' // (+) Vue.config.productionTip = false Vue.use(VueRouter) //- Vue.use(Vuex) (-) // 頁面組件 import Home from '@/components/Home' import Login from '@/components/Login' const router = new VueRouter({ routes: [ { path: '/', name: 'home', component: Home, meta: { auth: true } }, { path: '/login', name: 'login', component: Login, meta: { auth: true } } ] }) // 一下store對象剪切到store.js // const store = new Vuex.Store({ // state: { // isLogin: false, //登錄狀態 // username: '', //用戶名 // password: '' //密碼 // }, // mutations: { // // 修改登錄狀態 // changeLogin(state, data) { // state.isLogin = data // }, // // 修改用戶名狀態 // changeUsername(state, data) { // state.username = data // }, // // 修改密碼狀態 // changePassword(state, data) { // state.password = data // } // } // }) /* 路由攔截:檢查是否登錄,未登錄則跳到登錄頁 */ router.beforeEach((to, _, next) => { console.log(to); if (to.matched.some( m => m.meta.auth)) { if (to.name == 'login') { next() } else { if (store.state.isLogin == true) { next() } else { next('/login') } } } else { next() } }) new Vue({ router, store, render: h => h(App), }).$mount('#app')
運行刷新一下,看下效果應該和之前一樣。
Vuex之Getter
為了學習Getter,我們做一個簽到列表頁。
在store.js的store對象的state里面的password下面添加一個表示簽到列表的狀態字段list
// ... state: { isLogin: false, //登錄狀態 username: '', //用戶名 password: '', //密碼 list: [ { name: '張三', checked: true }, { name: '李四', checked: false }, { name: '哪吒', checked: true }, { name: '敖丙', checked: false }, { name: '申公豹', checked: true }, { name: '太乙真人', checked: true }, ] }, // ...
目的是顯示checked為true的人員
給store對象添加getters屬性
// ... getters: { showChecked: state => { return state.list.filter(item => item.checked) } }, // ...
src/store.js的完整代碼如下:
// 導入vue和vuex import Vue from 'vue' import Vuex from 'vuex' // 引用Vuex Vue.use(Vuex) // 從main.js拷貝過來即可 const store = new Vuex.Store({ state: { isLogin: false, //登錄狀態 username: '', //用戶名 password: '', //密碼 list: [ { name: '張三', checked: true }, { name: '李四', checked: false }, { name: '哪吒', checked: true }, { name: '敖丙', checked: false }, { name: '申公豹', checked: true }, { name: '太乙真人', checked: true }, ] }, getters: { showChecked: state => { return state.list.filter(item => item.checked) } }, mutations: { // 修改登錄狀態 changeLogin(state, data) { state.isLogin = data }, // 修改用戶名狀態 changeUsername(state, data) { state.username = data }, // 修改密碼狀態 changePassword(state, data) { state.password = data } } }) // 暴露(導出)store export default store
回到Home.vue,看看如何使用getters
在vue實例的計算屬性中增加一個屬性
computed: { // 其他代碼省略... showChecked () { return this.$store.getters.showChecked } }
在template模板里面增加一段代碼顯示簽到人員
<hr> <div> 已簽到人員:<br> <ul> <li v-for="(item, index) in showChecked" :key="index"> {{item.name}} </li> </ul> </div>
src/components/Home.vue完整代碼如下:
<template> <div class="home"> 登錄狀態:{{isLogin}} <br> <hr> 用戶名:{{username}}<br> 密碼:{{password}} <br> <hr> a + b = {{sum}} <br> <hr> <div> 已簽到人員:<br> <ul> <li v-for="(item, index) in showChecked" :key="index"> {{item.name}} </li> </ul> </div> 首頁 </div> </template> <script> import { mapState } from 'vuex' export default { name: 'Home', data () { return { a: 10, b: 20 } }, computed: { sum () { return this.a + this.b }, ...mapState({ isLogin: state => state.isLogin, username: state => state.username, password: state => state.password }), showChecked () { return this.$store.getters.showChecked } } } </script>
此時首頁效果如下:
說明:
- 加了一些<hr>分割內容
- 教程中所有.vue文件都省略了<style></style>,這個自行加上
通過方法訪問
通過讓getter返回一個函數,來實現給getter傳參。在你對store里的數組進行查詢時非常有用
為了練習這一知識點,我們實現一個已簽到和未簽到的切換。
點擊已簽到顯示已簽到的人員,點擊未簽到顯示未簽到的人員。
改造一下store.js里的getters里的showChecked,根據傳入的參數checked來返回數據。
getters: { showChecked: (state) => (checked) => { return state.list.filter(item => item.checked === checked) } },
回到Home.vue頁面,這里我們不在計算屬性中獲取數據了。在methods中寫一個方法來獲取。
現在data里添加一個空數組,來存放簽到列表的初始值。再利用方法修改這個數組的值。
src/components/Home.vue
data () { return { a: 10, b: 20, checkList: [] } },
methods: { getChecked (checked) { console.log(this.$store.getters.showChecked(checked)) this.checkList = this.$store.getters.showChecked(checked); } }
修改template代碼,原來的‘已簽到人員:’改成兩個切換標簽,渲染列表的showChecked改成checkList:
<div> <div><a href="javascript:;" @click="getChecked(true)">已簽到</a> | <a href="javascript:;" @click="getChecked(false)">未簽到</a></div> <ul> <li v-for="(item, index) in checkList" :key="index"> {{item.name}} </li> </ul> </div>
在vue實例created鈎子函數里調用一次this.getChecked(true),為了默認顯示已簽到數據。否則一打開就是空空的。
src/components/Home.vue完整代碼:
<template> <div class="home"> 登錄狀態:{{isLogin}} <br> <hr> 用戶名:{{username}}<br> 密碼:{{password}} <br> <hr> a + b = {{sum}} <br> <hr> <div> <div><a href="javascript:;" @click="getChecked(true)">已簽到</a> | <a href="javascript:;" @click="getChecked(false)">未簽到</a></div> <ul> <li v-for="(item, index) in checkList" :key="index"> {{item.name}} </li> </ul> </div> 首頁 </div> </template> <script> import { mapState } from 'vuex' export default { name: 'Home', data () { return { a: 10, b: 20, checkList: [] } }, computed: { sum () { return this.a + this.b }, ...mapState({ isLogin: state => state.isLogin, username: state => state.username, password: state => state.password }), // showChecked () { // return this.$store.getters.showChecked(0) // } }, created () { this.getChecked(true); }, methods: { getChecked (checked) { console.log(this.$store.getters.showChecked(checked)) this.checkList = this.$store.getters.showChecked(checked); } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> </style>
mapGetters輔助函數
看下vuex官方文檔的描述:
mapGetters輔助函數僅僅是將store中的getter映射到局部計算屬性
為了應用mapGetters,我們得改變一下之前的代碼。
在store.js添加一個歌單(songs)狀態,目的是根據條件顯示其中一部分歌名。
// ... songs: [ { name: '黑色毛衣', singer: '周傑倫' }, { name: '煙花易冷', singer: '周傑倫' }, { name: '愛笑的眼睛', singer: '林俊傑' }, { name: '美人魚', singer: '林俊傑' }, { name: '不能說的秘密', singer: '周傑倫' }, { name: '一千年以后', singer: '林俊傑' }, { name: '七里香', singer: '周傑倫' }, { name: '修煉愛情', singer: '林俊傑' }, ] // ...
把getters里的showChecked改成簡單版的,只返回checked=true的人員;再添加一個方法showSongs,返回周傑倫的歌。
// ... getters: { showChecked: state => { return state.list.filter(item => item.checked) }, showSongs: state => { return state.songs.filter(item => item.singer == '周傑倫') }, }, // ...
回到Home.vue,改動蠻大的。
首先,導入mapGetters:
import { mapState, mapGetters } from 'vuex'
計算屬性調用mapGetters:
...mapGetters([ 'showChecked', 'showSongs' ])
template模板修改:
<div> <div>已簽到</div> <ul> <li v-for="(item, index) in showChecked" :key="index"> {{item.name}} </li> </ul> </div> <hr> <div> <div>周傑倫的歌</div> <ul> <li v-for="(item, index) in showSongs" :key="index"> {{item.name}} - {{item.singer}} </li> </ul> </div>
src/components/Home.vue完整代碼:
<template> <div class="home"> 登錄狀態:{{isLogin}} <br> <hr> 用戶名:{{username}}<br> 密碼:{{password}} <br> <hr> a + b = {{sum}} <br> <hr> <div> <div>已簽到</div> <ul> <li v-for="(item, index) in showChecked" :key="index"> {{item.name}} </li> </ul> </div> <hr> <div> <div>周傑倫的歌</div> <ul> <li v-for="(item, index) in showSongs" :key="index"> {{item.name}} - {{item.singer}} </li> </ul> </div> 首頁 </div> </template> <script> import { mapState, mapGetters } from 'vuex' export default { name: 'Home', data () { return { a: 10, b: 20 } }, computed: { sum () { return this.a + this.b }, ...mapState({ isLogin: state => state.isLogin, username: state => state.username, password: state => state.password }), ...mapGetters([ 'showChecked', 'showSongs' ]) }, methods: { } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> </style>
效果如下:
參考文檔:Vuex官方中文文檔