前言
我想写一系列关于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官方中文文档