Vuex使用詳解

一、Vuex概述

引用官方文檔的話解釋什么是Vuex

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

設想每個Vue組件都只需要關心自己的數據和數據處理邏輯,組件之間完全獨立,沒有共享數據的需求,那么web應用會非常簡單。

但是實際情況是,一個web應用中不同組件常常會有數據共享的需求。例如文檔類型的應用,編輯的內容和大綱之間要共享數據,大綱列表組件需要根據編輯的內容生成文檔大綱、電商類型的應用,商品展示列表需要根據用戶選擇的分類展示相應的商品,那么商品列表組件需要知道分類選項組件中用戶選擇的是哪個類別。

我們知道Vue父組件可以和子組件之間通過props、事件、ref引用來進行通信,但是這種組件之間的通信方式在規模較大的應用中會有些力不從心。因此我們更傾向于將不同組件通用的數據提取出來統一管理。

我們知道Vue組件根據數據渲染視圖,不同的數據對應不同的視圖。我們也可以理解為web應用處于不同的“狀態”,每個狀態對應數據的一組取值。因此我們將數據抽出來統一管理也可以稱為“狀態管理”。

總之,Vuex就是專為Vue.js開發的狀態管理工具,用于解決組件之間數據共享的需求。

狀態管理工具都需要包含哪些要素?

  1. 初始狀態
  2. 改變狀態
  3. 監聽狀態的改變

首先我們看下Vuex都有哪些API,然后說明這些API的作用。

  1. state
  2. getters
  3. mutations
  4. actions
  5. module
  6. 輔助函數:mapState、mapGetters、mapMutations、mapActions
  7. createStore

其中state和getters用來保存狀態;mutations和actions用來改變狀態;監聽狀態用的是Vue組件中的computed屬性;module是用來組織整個應用的狀態管理代碼,使狀態劃分模塊,更易于管理;輔助函數用來在監聽狀態時候簡化代碼,createStore則用來創建狀態管理對象。

Vuex狀態管理就是創建一個對象(store),這個對象上面保存了應用中大部分數據,組件可以通過store獲取數據,也可以改變數據的值。上面說的這些能力一個普通的js對象也可以做到。store和普通的對象的區別是:

  1. Vuex 的狀態存儲是響應式的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那么相應的組件也會相應地得到高效更新。
  2. 你不能直接改變 store 中的狀態。改變 store 中的狀態的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個狀態的變化,從而讓我們能夠實現一些工具幫助我們更好地了解我們的應用

Vuex的數據流是組件中觸發Action,Action提交Mutations,Mutations修改State。 組件根據 States或Getters來渲染頁面。

參考官網的圖示Vuex官網

二、Vuex基本使用

前端面試刷題網站靈題庫,收集大廠面試真題,相關知識點詳細解析。】

注意這里使用Vuex版本為4


基本使用

首先我們來使用Vuex中的createStore方法創建一個store,我們注意到創建store的對象中目前有state和mutations兩個屬性。state中保存著我們需要使用的數據 count,mutations里面是改變state中數據的方法,方法接受一個state參數,通過改變這個state參數的屬性(count)的值就可以修改狀態了。

執行了store.commit('increase');之后,Vuex會執行mutations中的increase方法,讓count加一。


import {createStore} from 'vuex';

const store = createStore({
    state: {
        count: 0
    },
    mutations: {
        increase(state) {
            state.count++;
        }
    }
});
store.commit('increase');

console.log(store.state.count); // 1

mutations還可以接收參數

import {createStore} from 'vuex';

const store = createStore({
    state: {
        count: 0
    },
    mutations: {
        increase(state, {count}) {
            state.count = count;
        }
    }
});
store.commit('increase', {count: 2});

console.log(store.state.count); // 2

getters

getters和state的關系類似于Vue組件data屬性和computed屬性的關系,getters根據state或者其他getters計算出另一個變量的值,當其依賴的數據變化時候,它也會實時更新。

import {createStore} from 'vuex';

const store = createStore({
    state: {
        count: 0
    },
    getters: {
        msg(state) {
            return state.count % 2 === 0 ? '偶數': '奇數';
        }
    },
    mutations: {
        increase(state) {
            state.count++;
        }
    }
});
store.commit('increase');

console.log(store.state.count, store.getters.msg); // 1 "奇數"

actions


既然已經有了mutations可以改變state,為什么還需要actions呢?因為mutations不應該用于異步修改狀態。實際上mutations是可以異步修改狀態的,比如:

mutations: {
    increase(state) {
        setTimeout(() => {
            state.count++;
        }, 1000);
    }
}

但是這樣做的話,Vuex是無法知道修改state.count的時機的,因為它是在異步回調里面指定的,因此Vuex無法在調試工具中打印出我們實際改變state的操作。

因此Vuex中有actions API,在actions中可以進行異步操作,在actions中可以提交mutations,也可以觸發其他的action。

import {createStore} from 'vuex';

const store = createStore({
    state: {
        count: 0
    },
    mutations: {
        increase(state) {
            state.count++;
        }
    },
    actions: {
        increase(context) {
            setTimeout(() => {
                // contex.dispatch可以用于觸發其他action
                context.commit('increase');
            }, 1000);
        }
    }
});

store.dispatch('increase');

console.log(store.state.count); // 0

setTimeout(() => {
    console.log(store.state.count); // 1
}, 1000);

三、Vuex modules

通常在一個web應用中,會有很多數據,都放在一個store里面會讓數據很混亂,因此我們應該根據功能模塊將數據劃分成一個一個的模塊。

Vuex支持將store劃分成模塊,并且模塊還可以嵌套。

下面看官方文檔上的多個模塊的示例:

在調用createStore時候傳入的對象中,提供modules屬性,傳入兩個子模塊。

import {createStore} from 'vuex';
const moduleA = {
    state: {
        name: 'a'
    }
};

const moduleB = {
    state: {
        name: 'b'
    }
};

const store = createStore({
    modules: {
        a: moduleA,
        b: moduleB
    }
});

console.log(store.state.a.name); // a
console.log(store.state.b.name); // b

看下包含嵌套子模塊時候,訪問子模塊的getters和state的示例,

下面的示例中,有兩個子模塊a和b,其中a中還有嵌套的子模塊c,

(注意默認情況下,所有子模塊的getters、mutations和actions都是注冊在全局的)

import {createStore} from 'vuex';

const store = createStore({
    state: {
        counter: 0
    },
    getters: {
        counter10times(state) {
            return state.counter * 10;
        }
    },
    modules: {
        a: {
            state: {aName: 'A·a'},
            aGetters: {
                aFirstName(state) {
                    return state.aName.split('·')[0];
                }
            },
            modules: {
                c: {
                    state: {cName: 'C·c'},
                    getters: {
                        cFirstName(state) {
                            return state.cName.split('·')[0];
                        }
                    }
                }
            }
        },
        b: {
            state: {bName: 'B·b'},
            getters: {
                bFirstName(state) {
                    return state.bName.split('·')[0];
                },
                bNewName(state, getters, rootState, rootGetters) {
                    // 訪問局部state
                    const {bName} = state;
                    // 訪問全局state
                    const {a: {c: {cName}}} = rootState;
                    // 訪問局部getters
                    const {bFirstName} = getters;
                    // 訪問全局getters
                    const {cFirstName} = rootGetters;
                    return `${bName}  ${bFirstName}  ${cName}  ${cFirstName}`;
                }
            }
        }
    }
});

// 子模塊的state通過子模塊路徑訪問
console.log(store.state.a.c.cName);

// 子模塊的getters都注冊到了全局,在store.getters下面直接能訪問到
console.log(store.getters.bNewName);

下面是一個多模塊,commit mutation和dispatch action的示例,

模塊可以提交其他模塊的mutation,從而改變其他的模塊的狀態;模塊也可以觸發其他模塊的action

import {createStore} from 'vuex';

const store = createStore({
    state: {
        counter: 0
    },
    mutations: {
        increaseCounter(state) {
            state.counter++;
        }
    },
    modules: {
        a: {
            state: {aName: 'A·a'},
            mutations: {
                changeAName(state) {
                    state.aName = 'A-a';
                }
            },
            actions: {
                callChangeCNameAsync({dispatch}) {
                    // 觸發其他模塊的action
                    setTimeout(() => {
                        dispatch('changeCNameAsync');
                    }, 500);
                }
            },
            modules: {
                c: {
                    state: {cName: 'C·c'},
                    mutations: {
                        changeCName(state, payload) {
                            state.cName = `C-c-${payload.suffix}`;
                        }
                    },
                    actions: {
                        changeCNameAsync({commit, rootState}) {
                            setTimeout(() => {
                                // 提交其他模塊的mutation,mutation是全局的
                                commit('increaseCounter');
                                // 提交局部模塊的mutation
                                commit('changeCName', {
                                    suffix: rootState.counter
                                });
                            }, 500);
                        }
                    }
                },
            }
        },
        b: {
            state: {bName: 'B·b'},
            mutations: {
                changeBName(state) {
                    state.bName = 'B-b';
                }
            }
        }
    }
});

// 全局的commit
store.commit('increaseCounter');
console.log(store.state.counter); // 1

// 子模塊mutation注冊到全局了
store.commit('changeCName', {suffix: '123'});
console.log(store.state.a.c.cName); // C-c-123

// 子模塊commit其他模塊的mutation
store.dispatch('changeCNameAsync');
setTimeout(() => {
    console.log(store.state.a.c.cName); // C-c-2
}, 1000);

// 子模塊dispatch其它模塊的action
store.dispatch('callChangeCNameAsync');
setTimeout(() => {
    console.log(store.state.a.c.cName); // C-c-3
}, 1500);

之前提到默認情況下,所有模塊的getters、mutations和actions都是注冊到全局的,這樣如果多個子模塊的getters、mutations和actions中有同名時候,會導致覆蓋,引起問題。因此通常我們需要給子模塊加命名空間。

給子模塊加命名空間的方式是給子模塊加namespaced屬性并賦值為true。

加了命名空間后,訪問state的方式不變(因為默認state也不是注冊到全局的),訪問getters時候需要加命名空間前綴,如果訪問模塊自身子模塊的getters、提交mutations、觸發actions時候,只需要加相對路徑前綴,不需要加自身命名空間前綴,例如模塊a訪問其子模塊c時候,不需要加'a/c'前綴,只需要'c'就可以了。

看下多模塊(包含嵌套模塊情況)時候訪問state和getters的示例:

import {createStore} from 'vuex';

const store = createStore({
    state: {
        counter: 0
    },
    getters: {
        counter10times(state) {
            return state.counter * 10;
        }
    },
    modules: {
        a: {
            namespaced: true,
            state: {aName: 'A·a'},
            getters: {
                aFirstName(state) {
                    return state.aName.split('·')[0];
                }
            },
            modules: {
                c: {
                    namespaced: true,
                    state: {cName: 'C·c'},
                    getters: {
                        cFirstName(state) {
                            return state.cName.split('·')[0];
                        }
                    }
                }
            }
        },
        b: {
            namespaced: true,
            state: {bName: 'B·b'},
            getters: {
                bNewName(state, getters, rootState, rootGetters) {
                    // 局部state
                    const bName = state.bName.split('·')[0];
                    // 其他模塊的getter
                    const cFirstName = rootGetters['a/c/cFirstName'];
                    // 其他模塊的state
                    const aName = rootState.a.aName;
                    return `${bName} ${cFirstName} ${aName}`;
                }
            }
        }
    }
});

// getters命名空間
console.log(store.getters['b/bNewName']); // B C A·a

// 子節點state仍然是通過節點路徑訪問
console.log(store.state.a.c.cName); // C·c

看下在使用了命名空間的多模塊的提交mutations和觸發actions

import {createStore} from 'vuex';

const store = createStore({
    state: {
        counter: 0
    },
    mutations: {
        increaseCounter(state) {
            state.counter++;
        }
    },
    modules: {
        a: {
            namespaced: true,
            state: {aName: 'A·a'},
            mutations: {
                changeAName(state) {
                    state.aName = 'A-a';
                }
            },
            actions: {
                callChangeCNameAsync({dispatch}) {
                    // 觸發子模塊的action,是相對于自身的路徑,不需要加a前綴
                    setTimeout(() => {
                        dispatch('c/changeCNameAsync');
                    }, 500);
                }
            },
            modules: {
                c: {
                    namespaced: true,
                    state: {cName: 'C·c'},
                    mutations: {
                        changeCName(state, payload) {
                            state.cName = `C-c-${payload.suffix}`;
                        }
                    },
                    actions: {
                        changeCNameAsync({commit, rootState}) {
                            setTimeout(() => {
                                // 提交其他模塊的mutation,mutation是全局的
                                commit('increaseCounter', null, {root: true});
                                // 提交局部模塊的mutation,不需要加前綴
                                commit('changeCName', {
                                    suffix: rootState.counter
                                });
                            }, 500);
                        }
                    }
                },
            }
        },
        b: {
            namespaced: true,
            state: {bName: 'B·b'},
            mutations: {
                changeBName(state) {
                    state.bName = 'B-b';
                }
            }
        }
    }
});

// 全局的commit
// 注意加了命名空間之后,提交根模塊的mutation和觸發根模塊的action時候,都需要加上{root: true}的選項
store.commit('increaseCounter', null, {root: true});
console.log(store.state.counter); // 1

// 子模塊mutation注冊到全局了
store.commit('a/c/changeCName', {suffix: '123'});
console.log(store.state.a.c.cName); // C-c-123

// 子模塊commit其他模塊的mutation
store.dispatch('a/c/changeCNameAsync');
setTimeout(() => {
    console.log(store.state.a.c.cName); // C-c-2
}, 1000);

// 子模塊dispatch其它模塊的action
store.dispatch('a/callChangeCNameAsync');
setTimeout(() => {
    console.log(store.state.a.c.cName); // C-c-3
}, 1500);

四、在Vue組件中使用Vuex

1. 注入store

使用Vue3、Vuex4版本,通過如下方式向注入store,

import { createApp } from 'vue';
import App from './App.vue';
import {createStore} from 'vuex';

const store = createStore({
  state: {
    counter: 0
  },
  getters: {
    counter10times(state) {
      return state.counter * 10;
    }
  },
  mutations: {
    increaseCounter(state) {
      state.counter++;
    }
  },
  modules: {
    a: {
      namespaced: true,
      state: {aName: 'A·a'},
      getters: {
        aFirstName(state) {
          return state.aName.split('·')[0];
        }
      },
      mutations: {
        changeAName(state) {
          state.aName = 'A-a';
        }
      },
      actions: {
        callChangeCNameAsync({dispatch}) {
          // 觸發子模塊的action,相對與自身的路徑
          setTimeout(() => {
            dispatch('c/changeCNameAsync');
          }, 500);
        }
      },
      modules: {
        c: {
          namespaced: true,
          state: {cName: 'C·c'},
          getters: {
            cFirstName(state) {
              return state.cName.split('·')[0];
            }
          },
          mutations: {
            changeCName(state, payload) {
              state.cName = `C-c-${payload.suffix}`;
            }
          },
          actions: {
            changeCNameAsync({commit, rootState}) {
              setTimeout(() => {
                // 提交其他模塊的mutation,mutation是全局的
                commit('increaseCounter', null, {root: true});
                // 提交局部模塊的mutation,不需要加前綴
                commit('changeCName', {
                  suffix: rootState.counter
                });
              }, 500);
            }
          }
        }
      }
    },
    b: {
      namespaced: true,
      state: {bName: 'B·b'},
      getters: {
        bNewName(state, getters, rootState, rootGetters) {
          // 局部state
          const bName = state.bName.split('·')[0];
          // 其他模塊的getter
          const cFirstName = rootGetters['a/c/cFirstName'];
          // 其他模塊的state
          const aName = rootState.a.aName;
          return `${bName} ${cFirstName} ${aName}`;
        }
      },
      mutations: {
        changeBName(state) {
          state.bName = 'B-b';
        }
      }
    }
  }
});

createApp(App).use(store).mount('#app');

將剛才的加了命名空間的store注入到Vue組件樹中。這樣在所有的Vue組件中,都能夠通過this.$store方式訪問store。

Vue組件通過computed屬性來監聽store的數據變化。

看下面的示例,computed依賴了this.$store里面的一些模塊的state和getters,并將計算結果展示在界面上。

<template>
  <div>
    {{counter}}
    {{bName}}
    {{cFirstName}}
  </div>
</template>

<script>
  export default {
    computed: {
      counter() {
        return this.$store.state.counter;
      },
      bName() {
        return this.$store.state.b.bName;
      },
      cFirstName() {
        return this.$store.getters['a/c/cFirstName'];
      }
    }
  }
</script>

<style>
  #app {
    margin-top: 60px;
  }
</style>

下面看下Vue組件中改變狀態的示例,

可以看到Vue組件中通過methods方法調用this.$store的commit和dispatch方法來提交修改和觸發action。

/**
* @file main.js
*/
import { createApp } from 'vue';
import App from './App.vue';
import {createStore} from 'vuex';

const store = createStore({
    state: {
        counter: 0
    },
    getters: {
        counter10times(state) {
            return state.counter * 10;
        }
    },
    mutations: {
        increaseCounter(state) {
            state.counter++;
        }
    },
    modules: {
        a: {
            namespaced: true,
            state: {aName: 'A·a'},
            getters: {
                aFirstName(state) {
                    return state.aName.split('·')[0];
                }
            },
            mutations: {
                changeAName(state) {
                    state.aName = 'A-a';
                }
            },
            actions: {
                callChangeCNameAsync({dispatch}) {
                    // 觸發子模塊的action,相對于自身的路徑
                    setTimeout(() => {
                        dispatch('c/changeCNameAsync');
                    }, 500);
                }
            },
            modules: {
                c: {
                    namespaced: true,
                    state: {cName: 'C·c'},
                    getters: {
                        cFirstName(state) {
                            return state.cName.split('·')[0];
                        }
                    },
                    mutations: {
                        changeCName(state, payload) {
                            state.cName = `C-c-${payload.suffix}`;
                        }
                    },
                    actions: {
                        changeCNameAsync({commit, rootState}) {
                            setTimeout(() => {
                                // 提交其他模塊的mutation,mutation是全局的
                                commit('increaseCounter', null, {root: true});
                                // 提交局部模塊的mutation,不需要加前綴
                                commit('changeCName', {
                                    suffix: rootState.counter
                                });
                            }, 500);
                        }
                    }
                }
            }
        },
        b: {
            namespaced: true,
            state: {bName: 'B·b'},
            getters: {
                bNewName(state, getters, rootState, rootGetters) {
                    // 局部state
                    const bName = state.bName.split('·')[0];
                    // 其他模塊的getter
                    const cFirstName = rootGetters['a/c/cFirstName'];
                    // 其他模塊的state
                    const aName = rootState.a.aName;
                    return `${bName} ${cFirstName} ${aName}`;
                }
            },
            mutations: {
                changeBName(state) {
                    state.bName = 'B-b';
                }
            }
        }
    }
});

createApp(App).use(store).mount('#app');
/**
* @file App.vue
*/
<template>
    <div>
        {{counter}}
        {{bName}}
        {{cFirstName}}
        <button @click="modifyBName">修改b的name</button>
        <button @click="modifyCName">修改c的name</button>
    </div>
</template>

<script>
export default {
    computed: {
        counter() {
            return this.$store.state.counter;
        },
        bName() {
            return this.$store.state.b.bName;
        },
        cFirstName() {
            return this.$store.getters['a/c/cFirstName'];
        }
    },
    methods: {
        modifyBName() {
            this.$store.commit('b/changeBName');
        },
        modifyCName() {
            this.$store.dispatch('a/callChangeCNameAsync');
        }
    }
}
</script>

<style>
#app {
    margin-top: 60px;
}
</style>

2. 輔助函數

我們知道我們使用Vuex時候,通過computed綁定store的state和getters的數據,通過methods中調用this.$store的commit和dispatch方法來改變狀態。

但是每次都要寫this.$store,當需要綁定的數據多的時候會比較繁雜,因此Vuex提供了輔助函數來簡化代碼。輔助函數包括

  1. mapState
  2. mapGetters
  3. mapMutations
  4. mapActions

其中mapState和mapGetters將映射到computed屬性中,mapMutations和mapActions映射到methods屬性中。

用法見下面示例

<template>
    <div>
        {{counter}}
        {{bName}}
        {{counter10times}}
        {{cFirstName}}
        <button @click="modifyBName">修改b的name</button>
        <button @click="modifyCName">修改c的name</button>
    </div>
</template>

<script>
import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'

export default {
    computed: {
        ...mapState({
                        // 將this.$store.state.counter映射為counter
            counter: state => state.counter,
            // 也可以這樣實現
            // counter: 'counter',
            bName: state => state.b.bName
        }),
        // 全局的getters
        ...mapGetters(['counter10times']),
        // 也可以這樣實現,指定組件中的數據名稱
        // ...mapGetters({
        //     counter10times: 'counter10times'
        // }),
        // 子模塊的getters
        ...mapGetters({
            cFirstName: 'a/c/cFirstName'
        }),
        // 帶有命名空間的子模塊也可以這樣實現映射,在方法多的時候可以簡化代碼
        // ...mapGetters('a/c', [
        //     'cFirstName'
        // ])
    },
    methods: {
        // 映射mutations到方法
        ...mapMutations({
            modifyBName: 'b/changeBName'
        }),
        // 也可以這樣實現
        // ...mapMutations('b', {
        //     modifyBName: 'changeBName'
        // }),
        // 帶有命名空間的子模塊映射到組件的方法
        ...mapActions('a', {
            modifyCName: 'callChangeCNameAsync'
        }),
    }
}
</script>

<style>
#app {
    margin-top: 60px;
}
</style>

3. 組件之間共享數據

上面說明了Vuex的使用方法,下面看下Vuex在組件共享數據場景的一個簡單示例。

有兩個子組件bChild和cChild,它們直接從store中獲取數據并渲染。在根組件App.vue中修改store中的數據,可以看到子組件會相應數據更新,展示最新的數據。

<template>
    <div>
        {{counter}}
        {{counter10times}}
        <b-child></b-child>
        <c-child></c-child>
        <button @click="modifyBName">修改b的name</button>
        <button @click="modifyCName">修改c的name</button>
    </div>
</template>

<script>
import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'
import bChild from './components/bChild';
import cChild from './components/cChild';

export default {
    computed: {
        ...mapState({
            counter: state => state.counter,
            // 也可以這樣實現
            // counter: 'counter',
        }),
        // 全局的getters
        ...mapGetters(['counter10times']),
        // 也可以這樣實現,指定組件中的數據名稱
        // ...mapGetters({
        //     counter10times: 'counter10times'
        // }),
    },
    components: {
        'b-child': bChild,
        'c-child': cChild
    },
    methods: {
        // 映射mutations到方法
        ...mapMutations({
            modifyBName: 'b/changeBName'
        }),
        // 也可以這樣實現
        // ...mapMutations('b', {
        //     modifyBName: 'changeBName'
        // }),
        // 帶有命名空間的子模塊映射到組件的方法
        ...mapActions('a', {
            modifyCName: 'callChangeCNameAsync'
        }),
    }
}
</script>

<style>
#app {
    margin-top: 60px;
}
</style>

五、Vuex原理

1. 說明

Vuex通過createStore創建了一個數據中心,然后通過發布-訂閱模式來讓訂閱者監聽到數據改變。

那么Vuex是怎么應用到Vue中的呢?

先來看一個在Vue中使用Vuex的簡單例子:

// main.js
import { createApp } from 'vue';
import App from './App.vue';
import {createStore} from 'vuex';

const store = createStore({
    state: {
      message: 'hello'
    },
    mutations: {
        change(state) {
            state.message = 'world';
        }
    }
});

createApp(App).use(store).mount('#app');
export default {
  name: 'App',
  computed: {
      info() {
          return this.$store.state.message;
      }
  },
  mounted() {
      this.$store.commit('change');
  }
}

可以看到,在Vue中使用Vuex,主要有3個關鍵步驟:

  1. 使用Vuex創建store,再將store注入Vue中。Vue組件中就可以通過this.$store來訪問到store。
  2. Vue使用computed獲取$store中的狀態。
  3. Vue通過store.commit和store.action來修改狀態。

那么我們需要問兩個問題:

  1. 注入的原理是什么?為什么調用use()方法之后,就可以在組件通過$store來訪問store了?
  2. 響應式原理是什么?為什么使用computed可以監聽到store中的狀態改變?

這兩個是Vuex比較核心的兩個原理。

2. 注入原理

store注入 vue的實例組件的方式,是通過vue的 mixin機制,借助vue組件的生命周期鉤子beforeCreate 完成的。

Vue.mixin({
    beforeCreate() {
        if (this.$options && this.$options.store) {
            // 找到根組件 main 上面掛一個$store
            this.$store = this.$options.store;

        }
        else {
            // 非根組件指向其父組件的$store
            this.$store = this.$parent && this.$parent.$store;
        }
    }
});

3. Vuex響應式原理

Vuex使用vue中的reactive方法將state設置為響應式,原理和Vue組件的data設置為響應式是一樣的。

// vuex/src/store-util.js
import {reactive} from 'vue';

store._state = reactive({
    data: state
});

4. 總結

Vuex是個狀態管理器。

它Vuex通過createStore創建了一個數據中心,然后通過發布-訂閱模式來讓訂閱者監聽到數據改變。

Vuex的store注入 vue的實例組件的方式,是通過vue的 mixin機制,借助vue組件的生命周期鉤子beforeCreate 完成的。這樣Vue組件就能通過this.$store獲取到store了。

Vuex使用vue中的reactive方法將state設置為響應式,這樣組件就可以通過computed來監聽狀態的改變了。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容