一起學習Vuex 4.0的狀態管理(Vite)

叫我詹躲躲發表於2022-02-12

本文目錄

  1. 簡單介紹vite,搭建vite專案。
  2. 安裝vuex
  3. Vuex基礎介紹
  4. 使用Vuex
  5. 外掛引入

    1.簡單介紹vite,搭建vite專案

    1.1什麼是vite?

    Vite是一種新型前端構建工具,能夠顯著提升前端開發體驗。它主要由兩部分組成:

  6. 一個開發伺服器,它基於 原生 ES 模組 提供了 豐富的內建功能,如速度快到驚人的 模組熱更新(HMR)。
  7. 一套構建指令,它使用 Rollup 打包你的程式碼,並且它是預配置的,可輸出用於生產環境的高度優化過的靜態資源。

Vite 意在提供開箱即用的配置,同時它的 外掛 API 和 JavaScript API 帶來了高度的可擴充套件性,並有完整的型別支援。
Vite 將會使用 esbuild 預構建依賴。Esbuild 使用 Go 編寫,並且比以 JavaScript 編寫的打包器預構建依賴快 10-100 倍。

1.2初始化vite

npm init vite@latest

1.3新建第一個vite專案

Vuex 是一個專為 Vue.js 應用程式開發的狀態管理模式 + 庫。它採用集中式儲存管理應用的所有元件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。

2.安裝vuex

npm install vuex@next --save

3.Vuex基礎介紹

3.1 vuex

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

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

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

主要包含五個部分:State、Getters、Mutations、Actions、Module。

3.1.1 State概念

Vuex 使用單一狀態樹——是的,用一個物件就包含了全部的應用層級狀態。至此它便作為一個“唯一資料來源 (SSOT)”而存在。這也意味著,每個應用將僅僅包含一個 store 例項。單一狀態樹讓我們能夠直接地定位任一特定的狀態片段,在除錯的過程中也能輕易地取得整個當前應用狀態的快照。

3.1.2 分割模組(module)

由於使用單一狀態樹,應用的所有狀態會集中到一個比較大的物件。當應用變得非常複雜時,store 物件就有可能變得相當臃腫。
為了解決以上問題,Vuex 允許我們將 store 分割成模組(module)。每個模組擁有自己的 state、mutation、action、getter、甚至是巢狀子模組——從上至下進行同樣方式的分割:由此此處分割為兩個狀態管理模組。(test和test1模組)。

//store/test.js
export const test = {
    namespaced: true,
    state: {
        name: '叫我詹躲躲',
        gender: '男',
        profession: '前端開發',
        age: 10
    }
}

//store/test1.js
export const test1 = {
    namespaced: true,
    state: {
        name: '二月',
        sport: '跑步、程式碼和音樂',
        publics:'叫我詹躲躲',
        amount:100
    },
}

3.1.3名稱空間(namespaced)

預設情況下,模組內部的 action 和 mutation 仍然是註冊在全域性名稱空間的——這樣使得多個模組能夠對同一個 action 或 mutation 作出響應。Getter 同樣也預設註冊在全域性名稱空間,但是目前這並非出於功能上的目的(僅僅是維持現狀來避免非相容性變更)。必須注意,不要在不同的、無名稱空間的模組中定義兩個相同的 getter 從而導致錯誤。
如果希望你的模組具有更高的封裝度和複用性,你可以通過新增 namespaced: true 的方式使其成為帶名稱空間的模組。

3.1.4 Getters定義

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

//store/test.js
export const test = {
    namespaced: true,
    state: {
        name: '叫我詹躲躲',
        gender: '男',
        profession: '前端開發',
        age: 10
    },
    //從state派生的一些狀態,可以將該部分抽離出來成函式方便呼叫
    getters: {
        getUserInfo: state => {
            return state.name + '的職業是' + state.profession
        },
        getUserSex: state => {
            return state.name + '的性別是' + state.gender
        }
    },
}

//store/test1.js
export const test1 = {
    namespaced: true,
    state: {
        name: '二月',
        sport: '跑步、程式碼和音樂',
        publics:'叫我詹躲躲',
        amount:100
    },
    getters: {
        getSport: state => {
            return state.name + '喜歡的執行是' + state.sport
        },
        getPublics: state => {
            return state.name + '的公眾號是' + state.publics
        }
    },
}

3.1.5.mutations定義

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

//store/test.js
export const test = {
    namespaced: true,
    state: {
        name: '叫我詹躲躲',
        gender: '男',
        profession: '前端開發',
        age: 10
    },
    //從state派生的一些狀態,可以將該部分抽離出來成函式方便呼叫
    getters: {
        getUserInfo: state => {
            return state.name + '的職業是' + state.profession
        },
        getUserSex: state => {
            return state.name + '的性別是' + state.gender
        }
    },
    mutations: {
        testMutation1(state) {
            // 變更狀態
            state.age++;
        },
        // 第二個引數是載荷
        testMutation2(state, payload) {
            state.age += payload.content;
        }
    },
}

//store/test1.js
export const test1 = {
    namespaced: true,
    state: {
        name: '二月',
        sport: '跑步、程式碼和音樂',
        publics:'叫我詹躲躲',
        amount:100
    },
    getters: {
        getSport: state => {
            return state.name + '喜歡的執行是' + state.sport
        },
        getPublics: state => {
            return state.name + '的公眾號是' + state.publics
        }
    },
    mutations: {
        test1Mutation1(state) {
            state.amount++;
        },
        // 第二個引數是載荷
        test1Mutation2(state, payload) {
            state.amount += payload.amount;
        }
    },
}

3.1.6.actions定義

Action 類似於 mutation,不同在於:
Action 提交的是 mutation,而不是直接變更狀態。
Action 可以包含任意非同步操作。

//store/test.js
export const test = {
    namespaced: true,
    state: {
        name: '叫我詹躲躲',
        gender: '男',
        profession: '前端開發',
        age: 10
    },
    //從state派生的一些狀態,可以將該部分抽離出來成函式方便呼叫
    getters: {
        getUserInfo: state => {
            return state.name + '的職業是' + state.profession
        },
        getUserSex: state => {
            return state.name + '的性別是' + state.gender
        }
    },
    mutations: {
        testMutation1(state) {
            // 變更狀態
            state.age++;
        },
        // 第二個引數是載荷
        testMutation2(state, payload) {
            state.age += payload.content;
        }
    },
    actions: {
        testAction1(context) {
            context.commit('testMutation1');
        },
        //通過引數解構來簡化程式碼,testAction1簡化為testAction2寫法
        testAction2({ commit }, payload) {
            commit({
                type: 'testMutation2',
                content: payload.content
            });
        }
    }
}

//store/test1.js
export const test1 = {
    namespaced: true,
    state: {
        name: '二月',
        sport: '跑步、程式碼和音樂',
        publics:'叫我詹躲躲',
        amount:100
    },
    getters: {
        getSport: state => {
            return state.name + '喜歡的執行是' + state.sport
        },
        getPublics: state => {
            return state.name + '的公眾號是' + state.publics
        }
    },
    mutations: {
        test1Mutation1(state) {
            state.amount++;
        },
        // 第二個引數是載荷
        test1Mutation2(state, payload) {
            state.amount += payload.amount;
        }
    },
    actions: {
        test1Action1(context) {
            context.commit('test1Mutation1');
        },
        test1Action2({ commit }, payload) {
            commit({
                type: 'test1Mutation1',
                content: payload.content
            });
        }
    }
}

已經維護了store倉庫,在元件中使用我們的狀態。

4.狀態使用

4.1 引入

//store/index.js 內容

import { createStore } from "vuex";
import { test } from "./modules/test";
import { test1 } from "./modules/test1";

export const store = createStore({
  // Vuex允許store分割成小的module,每個模組擁有自己的state、mutation、action、getter;
  // 訪問test的狀態:store.state.test
  modules: {
    test, //store模組1
    test1 //store模組2
  }
});

main.js使用

//main.js
import { createApp } from 'vue'
import App from './App.vue'
import { store } from './store'
let app = createApp(App)
app.use(store).mount('#app')

4.2 元件中使用狀態

4.2.1 使用state

test裡面的狀態

//test裡面的狀態
<h3>1.test模組state的狀態</h3>
<h5>{{userName}}</h5>
<h5>{{userInfo}}</h5>

<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
/* ------------test模組狀態-----------------*/
//獲取test1模組的狀態
const userName = computed(() => store.state.test.name)
const userInfo = computed(
  () => store.state.test.name + '的職業是:' + store.state.test.profession
)
</script>

test1裡面的狀態

//test1裡面的狀態
<h3>1.test1模組state的狀態</h3>
<h5>{{sport}}</h5>
<h5>公眾號:{{publics}}</h5>

//獲取test2模組的狀態
const publics = computed(() => store.state.test1.publics)
const sport = computed(
  () => store.state.test1.name + '喜歡的運動:' + store.state.test1.sport
)

4.2.2 使用getters

test模組getters的狀態

<h3>2.test模組getters的狀態</h3>
<h5>{{getUserInfo}}</h5>
<h5>{{getUserSex}}</h5>

//獲取getters
const getUserInfo = computed(() => store.getters['test/getUserInfo'])
const getUserSex = computed(() => store.getters['test/getUserSex'])

test1模組getters的狀態

<h3>2.test1模組getters的狀態</h3>
<h5>{{getSport}}</h5>
<h5>{{getPublics}}</h5>

//獲取getters
const getSport = computed(() => store.getters['test1/getSport'])
const getPublics = computed(() => store.getters['test1/getPublics'])

4.2.3 使用mutations

用mutations改變test模組age的狀態

<h3>3.用mutations改變test模組age的狀態</h3>
<button @click="testClick">改變test狀態(age)</button>
<h5>{{age}}</h5>

//通過mutations改變狀態,改變test模組的age
const age = computed(() => store.state.test.age)
const testClick = () => {
  store.commit('test/testMutation1')
}

用mutations改變test1模組amount的狀態

<h3>3.用mutations改變test1模組amount的狀態</h3>
<button @click="test1Click">改變test1狀態(amount)</button>
<h5>{{amount}}</h5>

//通過mutations改變狀態,改變test1模組的amount
const amount = computed(() => store.state.test1.amount)
const test1Click = () => {
  store.commit('test1/test1Mutation1')
}

4.2.4 使用actions

用actions改變test模組age的狀態

<h3>4.用actions改變test模組age的狀態</h3>
<button @click="changeActions">改變test狀態(age)</button>
<h5>{{age}}</h5>

//通過actions改變狀態,改變test模組的age
const changeActions = () => {
  store.dispatch('test/testAction1')
}

用actions改變test1模組amount的狀態

<h3>4.用actions改變test1模組amount的狀態</h3>
<button @click="changeActions1">改變test狀態(amount)</button>
<h5>{{amount}}</h5>

//通過actions改變狀態,改變test模組的amount
const changeActions1 = () => {
  store.dispatch('test1/test1Action1')
}

完整的Demo示例

<template>
  <div>
    <h2>Vuex狀態學習</h2>
    <div class="wrapper">
      <div class="left-box">
        <h3>1.test模組state的狀態</h3>
        <h5>{{userName}}</h5>
        <h5>{{userInfo}}</h5>
        ----------------------------------------------------------
        <h3>2.test模組getters的狀態</h3>
        <h5>{{getUserInfo}}</h5>
        <h5>{{getUserSex}}</h5>
        ----------------------------------------------------------
        <h3>3.用mutations改變test模組age的狀態</h3>
        <button @click="testClick">改變test狀態(age)</button>
        <h5>{{age}}</h5>
        ----------------------------------------------------------
        <h3>4.用actions改變test模組age的狀態</h3>
        <button @click="changeActions">改變test狀態(age)</button>
        <h5>{{age}}</h5>
      </div>

      <div class="line"></div>
      <div class="right-box">
        <h3>1.test1模組state的狀態</h3>
        <h5>{{sport}}</h5>
        <h5>公眾號:{{publics}}</h5>
        ----------------------------------------------------------
        <h3>2.test1模組getters的狀態</h3>
        <h5>{{getSport}}</h5>
        <h5>{{getPublics}}</h5>
        ----------------------------------------------------------
        <h3>3.用mutations改變test1模組amount的狀態</h3>
        <button @click="test1Click">改變test1狀態(amount)</button>
        <h5>{{amount}}</h5>
        ----------------------------------------------------------
        <h3>4.用actions改變test1模組amount的狀態</h3>
        <button @click="changeActions1">改變test狀態(amount)</button>
        <h5>{{amount}}</h5>
      </div>
    </div>

  </div>
</template>
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
/* ------------test模組狀態-----------------*/
//獲取test1模組的狀態
const userName = computed(() => store.state.test.name)
const userInfo = computed(
  () => store.state.test.name + '的職業是:' + store.state.test.profession
)
//獲取getters
const getUserInfo = computed(() => store.getters['test/getUserInfo'])
const getUserSex = computed(() => store.getters['test/getUserSex'])

//通過mutations改變狀態,改變test模組的age
const age = computed(() => store.state.test.age)
const testClick = () => {
  store.commit('test/testMutation1')
}

//通過actions改變狀態,改變test模組的age
const changeActions = () => {
  store.dispatch('test/testAction1')
}

/* -----------test1模組狀態------------------*/
//獲取test2模組的狀態
const publics = computed(() => store.state.test1.publics)
const sport = computed(
  () => store.state.test1.name + '喜歡的運動:' + store.state.test1.sport
)
//獲取getters
const getSport = computed(() => store.getters['test1/getSport'])
const getPublics = computed(() => store.getters['test1/getPublics'])

//通過mutations改變狀態,改變test1模組的amount
const amount = computed(() => store.state.test1.amount)
const test1Click = () => {
  store.commit('test1/test1Mutation1')
}

//通過actions改變狀態,改變test模組的amount
const changeActions1 = () => {
  store.dispatch('test1/test1Action1')
}
</script>

<style scoped>
h2 {
  text-align: center;
}
.wrapper {
  width:1200px;
  margin: 0 auto;
}
.left-box,
.right-box {
  width:calc(50% - 4px);
  display: inline-block;
  text-align: center;
  background: #c4bebf;
  border-radius: 5px;
}
.line {
  height: 100%;
  width: 4px;
  display: inline-block;
}
</style>

5.外掛

Vuex 的 store 接受 plugins 選項,這個選項暴露出每次 mutation 的鉤子。Vuex 外掛就是一個函式,它接收 store 作為唯一引數:

const myPlugin = (store) => {
  // 當 store 初始化後呼叫
  store.subscribe((mutation, state) => {
    // 每次 mutation 之後呼叫
    // mutation 的格式為 { type, payload }
  })
}

5.1 使用外掛

const store = createStore({
  plugins: [myPlugin]
})

以上是vuex的使用和部分引數的解釋,每天進步一點點,歡迎一起學習交流。我是叫我詹躲躲。文章不定期更新到 個人部落格(https://zhanhongzhu.top)、掘金、思否和微信公眾號【叫我詹躲躲】。一起精進,一起成長。

參考文章

vuex中文文件:https://vuex.vuejs.org/zh/

相關文章