Vuex入门简单示例(三)

前言

我想写一系列关于Vuex的入门文章,我是看着vuex官网文档,结合自己从零搭建的vue项目来实践vuex的知识。

Vuex入门系列:

 

本文涉及知识点:

  1. vuex之mapState
  2. 独立store.js文件
  3. vuex之getter
  4. 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官方中文文档 

 

posted on 2019-08-15 13:41  独自去流浪  阅读(949)  评论(0编辑  收藏  举报