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官方中文文檔 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM