vue中vuex的使用練習


 

Vuex 是什么?

Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。它采用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。訪問官網,點擊這里

每一個 Vuex 應用的核心就是 store(倉庫)。“store”基本上就是一個容器,它包含着你的應用中大部分的狀態 (state)。Vuex 和單純的全局對象有以下兩點不同:

  1. Vuex 的狀態存儲是響應式的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那么相應的組件也會相應地得到高效更新。

  2. 你不能直接改變 store 中的狀態。改變 store 中的狀態的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個狀態的變化,從而讓我們能夠實現一些工具幫助我們更好地了解我們的應用。

 

vuex中,有默認的五種基本的對象:

  • state:存儲狀態(變量)

  • getters:對數據獲取之前的再次編譯,可以理解為state的計算屬性。我們在組件中使用 $sotre.getters.fun()

  • mutations:修改狀態,並且是同步的。在組件中使用$store.commit('',params)。這個和我們組件中的自定義事件類似。

  • actions:異步操作。在組件中使用是$store.dispath('')

  • modules:store的子模塊,為了開發大型項目,方便狀態管理而使用的。這里我們就不解釋了,用起來和上面的一樣。

0、在 main.js里面,引入vuex相關的js文件

import store from './store'
// store.js

import Vue from 'vue'
import Vuex from 'vuex'

import user from './module/user'
import app from './module/app'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    //
    number: 0
  },
  mutations: {
  
  },
  getters: {
   
  },
  actions: {
    
  },
  modules: {

  }
})

1、在 Vue 組件中獲得 Vuex 狀態(state)

(1)直接在組件father.vue中調用:

<div style="border: 2px solid #007606;padding: 5px;">
      VUEX的數據【number】:
      <span style="font-size: 30px;font-weight: bolder;">
        {{this.$store.state.number}}
      </span>
</div>

 

 (2)或者在計算屬性中處理后使用

<div style="border: 2px solid #007606;padding: 5px;">
  VUEX的數據【number】:
  <span style="font-size: 30px;font-weight: bolder;">
    {{count}}
  </span>
</div>
computed: {
  count () {
    return '==這是VUEX中定義的Number==:'+this.$store.state.number
  }
},

 

 

2、Getter

有時候我們需要從 store 中的 state 中派生出一些狀態,

如果有多個組件需要用到此屬性,我們要么復制這個函數,或者抽取到一個共享函數然后在多處導入它——無論哪種方式都不是很理想。

Vuex 允許我們在 store 中定義“getter”(可以認為是 store 的計算屬性)。就像計算屬性一樣,getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變才會被重新計算。

(1)實例:孫子級組件F中,處理【number】的值,*2

<div style="border: 2px solid #2700ff;padding: 5px;">
  Vuex的數值【number*2】:
  <span style="font-size: 30px;font-weight: bolder;">
  {{$store.getters.doneTodosCount}}
  </span>
</div>

(2)store.js代碼:

getters: {
  doneTodosCount: (state, getters) => {
    return state.number * 2
  }
},

 

 

 

 

 

 

3、Mutation(Mutation 必須是同步函數)

更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。Vuex 中的 mutation 非常類似於事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是我們實際進行狀態更改的地方,並且它會接受 state 作為第一個參數

(1)實例:祖父級組件parent,點擊按鈕,對number進行++或者--的操作

<div style="border: 2px solid #007606;padding: 5px;">
  VUEX的數據【number】:
  <span style="font-size: 30px;font-weight: bolder;">
    {{count}}
  </span>

  <br>
  <el-button @click="vuexClick1">點擊++</el-button>
  <el-button @click="vuexClick2">點擊--</el-button>
</div>
methods: {
  vuexClick1 () {
    this.$store.commit('vuexClick1')
  },
  vuexClick2 () {
    this.$store.commit('vuexClick2')
  }
},

(2)store.js代碼:

mutations: {
  //
  vuexClick1 (state) {
    return state.number++
  },
  vuexClick2 (state) {
    return state.number--
  }
},

 

 

 

4、Ation

Action 類似於 mutation,不同在於:

  • Action 提交的是 mutation,而不是直接變更狀態。

  • Action 可以包含任意異步操作。

(1)實例:祖父級組件parent,點擊按鈕"number延時3s加10",對number進行延時3s加10的操作

<el-button @click="vuexClick3">number延時3s加10</el-button>
vuexClick3 () {
  this.$store.dispatch('vuexClick3')
}

(2)store.js代碼:

actions: {
  //
  vuexClick3 (state) {
    setTimeout(() => {
      return state.commit('NumberChange3')
    }, 3000)
  }
},
NumberChange3 (state) {
  return state.number += 10
}

 

 

 

5、Module

官方文檔

由於使用單一狀態樹,應用的所有狀態會集中到一個比較大的對象。當應用變得非常復雜時,store 對象就有可能變得相當臃腫。

為了解決以上問題,Vuex 允許我們將 store 分割成模塊(module)。每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行同樣方式的分割:

image.png

modules: {
  user,
  app
}

命名空間

默認情況下,模塊內部的 action、mutation 和 getter 是注冊在全局命名空間的——這樣使得多個模塊能夠對同一 mutation 或 action 作出響應。

如果希望你的模塊具有更高的封裝度和復用性,你可以通過添加 namespaced: true 的方式使其成為帶命名空間的模塊。當模塊被注冊后,它的所有 getter、action 及 mutation 都會自動根據模塊注冊的路徑調整命名。

const store = new Vuex.Store({
  modules: {
    account: {
      namespaced: true,

      // 模塊內容(module assets)
      state: { ... }, // 模塊內的狀態已經是嵌套的了,使用 `namespaced` 屬性不會對其產生影響
      getters: {
        isAdmin () { ... } // -> getters['account/isAdmin']
      },
      actions: {
        login () { ... } // -> dispatch('account/login')
      },
      mutations: {
        login () { ... } // -> commit('account/login')
      },

      // 嵌套模塊
      modules: {
        // 繼承父模塊的命名空間
        myPage: {
          state: { ... },
          getters: {
            profile () { ... } // -> getters['account/profile']
          }
        },

        // 進一步嵌套命名空間
        posts: {
          namespaced: true,

          state: { ... },
          getters: {
            popular () { ... } // -> getters['account/posts/popular']
          }
        }
      }
    }
  }})

【官網實例】

完整代碼:

1.store.js

import Vue from 'vue'
import Vuex from 'vuex'

import user from './module/user'
import app from './module/app'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    //
    number: 0
  },
  mutations: {
    //
    vuexClick1 (state) {
      return state.number++
    },
    vuexClick2 (state) {
      return state.number--
    },
    NumberChange3 (state) {
      return state.number += 10
    }
  },
  getters: {
    doneTodosCount: (state, getters) => {
      return state.number * 2
    }
  },
  actions: {
    //
    vuexClick3 (state) {
      setTimeout(() => {
        return state.commit('NumberChange3')
      }, 3000)
    }
  },
  modules: {
    user,
    app
  }
})

 

2.father.vue

<!--
  文件描述:祖父級組件
  創建時間:2019/12/28 15:30
-->
<template>
  <div class="" style="width: 900px;height: 700px;background-color: #d6e7ff;padding: 20px;">
    <span style="font-size: 20px;font-weight: bold;">祖父級組件:father</span>
    <div style="border: 2px solid #007606;padding: 5px;">
      VUEX的數據【number】:
      <span style="font-size: 30px;font-weight: bolder;">
        {{count}}
      </span>

      <br>
      <el-button @click="vuexClick1">點擊++</el-button>
      <el-button @click="vuexClick2">點擊--</el-button>
      <el-button @click="vuexClick3">number延時3s加10</el-button>
    </div>
    <br>

    <div>
      <childrenA></childrenA>
      <childrenB></childrenB>
      <childrenC></childrenC>
    </div>
  </div>
</template>

<script>
// 這里可以導入其他文件(比如:組件,工具js,第三方插件js,json文件,圖片文件等等)
// 例如:import 《組件名稱》 from '《組件路徑》';
import childrenA from './childrenA'
import childrenB from './childrenB'
import childrenC from './childrenC'

export default {
  name: 'father',
  // import引入的組件需要注入到對象中才能使用
  components: {
    childrenA,
    childrenB,
    childrenC
  },
  computed: {
    count () {
      return this.$store.state.number
    }
  },
  methods: {
    vuexClick1 () {
      this.$store.commit('vuexClick1')
    },
    vuexClick2 () {
      this.$store.commit('vuexClick2')
    },
    vuexClick3 () {
      this.$store.dispatch('vuexClick3')
    }
  },
  data () {
    // 這里存放數據
    return {
    }
  }
}
</script>

 

3.childrenG.vue

<!--文件描述:孫子級組件G
  創建時間:2019/12/28 15:37-->
<template>
  <div class="" style="float: left;margin: 10px;width: 200px;height: 200px;background-color: #ff00de;padding: 20px;color:#fff;">
    <span style="font-size: 14px;font-weight: bold;color:#fff;">孫子級組件G:childrenG</span>
    <div style="border: 2px solid #00ff09;padding: 5px;">
      VUEX的數據【number】:
      <span style="font-size: 30px;font-weight: bolder;">
        {{this.$store.state.number}}
      </span>
    </div>
  </div>
</template>

<script>
export default {
  name: 'childrenA',
  // import引入的組件需要注入到對象中才能使用
  components: {},
  data () {
    // 這里存放數據
    return {
    }
  }
}
</script>

 

4.childrenF.vue

<!--
  文件描述:孫子級組件F
  創建時間:2019/12/28 15:37
  創建人:
-->
<template>
  <div class="" style="float: left;margin: 10px;width: 200px;height: 200px;background-color: #aaff1a;padding: 20px;">
    <span style="font-size: 14px;font-weight: bold;color:#000000;">孫子級組件F:childrenF</span>
    <div style="border: 2px solid #2700ff;padding: 5px;">
      Vuex的數值【number*2】:
      <span style="font-size: 30px;font-weight: bolder;">
      {{$store.getters.doneTodosCount}}
      </span>
    </div>
  </div>
</template>

<script>
// 這里可以導入其他文件(比如:組件,工具js,第三方插件js,json文件,圖片文件等等)
// 例如:import 《組件名稱》 from '《組件路徑》';
// 例如:import uploadFile from '@/components/uploadFile/uploadFile'

export default {
  name: 'childrenA',
  // import引入的組件需要注入到對象中才能使用
  components: {},
  data () {
    // 這里存放數據
    return {

    }
  },
  // 監聽屬性 類似於data概念
  computed: {},
  // 方法集合
  methods: {},
  // 監控data中的數據變化
  watch: {},
  // 生命周期 - 創建完成(可以訪問當前this實例)
  created () {

  },
  // 生命周期 - 掛載完成(可以訪問DOM元素)
  mounted () {

  },
  beforeCreate () {
  }, // 生命周期 - 創建之前
  beforeMount () {
  }, // 生命周期 - 掛載之前
  beforeUpdate () {
  }, // 生命周期 - 更新之前
  updated () {
  }, // 生命周期 - 更新之后
  beforeDestroy () {
  }, // 生命周期 - 銷毀之前
  destroyed () {
  }, // 生命周期 - 銷毀完成
  activated () {
  } // 如果頁面有keep-alive緩存功能,這個函數會觸發
}
</script>

<style scoped  lang="scss">
  //@import url(); 引入公共css類
</style>

 5.user.js

import {
  login,
  logout,
  getUserInfo,
  getMessage,
  getContentByMsgId,
  hasRead,
  removeReaded,
  restoreTrash,
  getUnreadCount
} from '@/api/user'
import { setToken, getToken } from '@/libs/util'

export default {
  state: {
    userName: '',
    userId: '',
    avatorImgPath: '',
    token: getToken(),
    access: '',
    hasGetInfo: false,
    unreadCount: 0,
    messageUnreadList: [],
    messageReadedList: [],
    messageTrashList: [],
    messageContentStore: {}
  },
  mutations: {
    setAvator (state, avatorPath) {
      state.avatorImgPath = avatorPath
    },
    setUserId (state, id) {
      state.userId = id
    },
    setUserName (state, name) {
      state.userName = name
    },
    setAccess (state, access) {
      state.access = access
    },
    setToken (state, token) {
      state.token = token
      setToken(token)
    },
    setHasGetInfo (state, status) {
      state.hasGetInfo = status
    },
    setMessageCount (state, count) {
      state.unreadCount = count
    },
    setMessageUnreadList (state, list) {
      state.messageUnreadList = list
    },
    setMessageReadedList (state, list) {
      state.messageReadedList = list
    },
    setMessageTrashList (state, list) {
      state.messageTrashList = list
    },
    updateMessageContentStore (state, { msg_id, content }) {
      state.messageContentStore[msg_id] = content
    },
    moveMsg (state, { from, to, msg_id }) {
      const index = state[from].findIndex(_ => _.msg_id === msg_id)
      const msgItem = state[from].splice(index, 1)[0]
      msgItem.loading = false
      state[to].unshift(msgItem)
    }
  },
  getters: {
    messageUnreadCount: state => state.messageUnreadList.length,
    messageReadedCount: state => state.messageReadedList.length,
    messageTrashCount: state => state.messageTrashList.length
  },
  actions: {
    // 登錄
    handleLogin ({ commit }, { userName, password }) {
      userName = userName.trim()
      return new Promise((resolve, reject) => {
        login({
          userName,
          password
        }).then(res => {
          const data = res.data
          commit('setToken', data.token)
          resolve()
        }).catch(err => {
          reject(err)
        })
      })
    },
    // 退出登錄
    handleLogOut ({ state, commit }) {
      return new Promise((resolve, reject) => {
        logout(state.token).then(() => {
          commit('setToken', '')
          commit('setAccess', [])
          resolve()
        }).catch(err => {
          reject(err)
        })
        // 如果你的退出登錄無需請求接口,則可以直接使用下面三行代碼而無需使用logout調用接口
        // commit('setToken', '')
        // commit('setAccess', [])
        // resolve()
      })
    },
    // 獲取用戶相關信息
    getUserInfo ({ state, commit }) {
      return new Promise((resolve, reject) => {
        try {
          getUserInfo(state.token).then(res => {
            const data = res.data
            commit('setAvator', data.avator)
            commit('setUserName', data.name)
            commit('setUserId', data.user_id)
            commit('setAccess', data.access)
            commit('setHasGetInfo', true)
            resolve(data)
          }).catch(err => {
            reject(err)
          })
        } catch (error) {
          reject(error)
        }
      })
    },
    // 此方法用來獲取未讀消息條數,接口只返回數值,不返回消息列表
    getUnreadMessageCount ({ state, commit }) {
      getUnreadCount().then(res => {
        const { data } = res
        commit('setMessageCount', data)
      })
    },
    // 獲取消息列表,其中包含未讀、已讀、回收站三個列表
    getMessageList ({ state, commit }) {
      return new Promise((resolve, reject) => {
        getMessage().then(res => {
          const { unread, readed, trash } = res.data
          commit('setMessageUnreadList', unread.sort((a, b) => new Date(b.create_time) - new Date(a.create_time)))
          commit('setMessageReadedList', readed.map(_ => {
            _.loading = false
            return _
          }).sort((a, b) => new Date(b.create_time) - new Date(a.create_time)))
          commit('setMessageTrashList', trash.map(_ => {
            _.loading = false
            return _
          }).sort((a, b) => new Date(b.create_time) - new Date(a.create_time)))
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },
    // 根據當前點擊的消息的id獲取內容
    getContentByMsgId ({ state, commit }, { msg_id }) {
      return new Promise((resolve, reject) => {
        let contentItem = state.messageContentStore[msg_id]
        if (contentItem) {
          resolve(contentItem)
        } else {
          getContentByMsgId(msg_id).then(res => {
            const content = res.data
            commit('updateMessageContentStore', { msg_id, content })
            resolve(content)
          })
        }
      })
    },
    // 把一個未讀消息標記為已讀
    hasRead ({ state, commit }, { msg_id }) {
      return new Promise((resolve, reject) => {
        hasRead(msg_id).then(() => {
          commit('moveMsg', {
            from: 'messageUnreadList',
            to: 'messageReadedList',
            msg_id
          })
          commit('setMessageCount', state.unreadCount - 1)
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },
    // 刪除一個已讀消息到回收站
    removeReaded ({ commit }, { msg_id }) {
      return new Promise((resolve, reject) => {
        removeReaded(msg_id).then(() => {
          commit('moveMsg', {
            from: 'messageReadedList',
            to: 'messageTrashList',
            msg_id
          })
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },
    // 還原一個已刪除消息到已讀消息
    restoreTrash ({ commit }, { msg_id }) {
      return new Promise((resolve, reject) => {
        restoreTrash(msg_id).then(() => {
          commit('moveMsg', {
            from: 'messageTrashList',
            to: 'messageReadedList',
            msg_id
          })
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    }
  }
}

 


免責聲明!

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



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