Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。vue
一般咱們能夠這樣來使用vuex
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: ` <div>{{ count }}</div> `,
// actions
methods: {
increment () {
this.count++
}
}
})
複製代碼
這個狀態自管理應用包含如下幾個部分:數據結構
vuex的核心就是定義了一個store(容器)來管理應用的的數據狀態,與單純的全局對象數據存儲相比,vuex的功能更強大。異步
在項目中使用vuex
**相信下面的代碼,用vue開發過項目的人都很熟悉import Vue from 'vue'
import Vuex from 'vuex'
// 這裏會執行vuex的install方法
Vue.use(Vuex);
export default new Vuex.store({
state: { // 統一的狀態管理
age:10,
a:100
},
getters:{
// 計算屬性
myAge(state){ // object.defineProperty
return state.age + 18;
}
},
mutations: { // 能夠更改狀態
syncAdd(state,payload){ // $store.commit()
state.age += payload;
},
syncMinus(state,payload){
state.age -= payload
}
},
actions: { // 異步提交更改
asyncMinus({commit},payload){ // action 異步獲取完後 提交到mutation中
setTimeout(()=>{
commit('syncMinus',payload);
},1000)
}
}
})
複製代碼
這樣咱們就能夠在項目中集中管理公用的狀態了。那麼vuex是怎麼實現這一切的呢?vuex的入口提供的install
方法就能夠完成初始化工做async
const install = (_vue) => {
//vue.use 傳入vue的構造函數
Vue = _vue;
// 給vue的組件實例都混入一個鉤子函數
Vue.mixin({
beforeCreate() {
// 首先 store是放在根組件上,咱們須要獲取,並給每一個組件掛上 即 this.$store = store
if(this.$options && this.$options.store) {
// 給根實例增長$store屬性
this.$store = this.$options.store
}else{
// 其餘組件只需從它的父級獲取
this.$store = this.$parent && this.$parent.$store
}
}
})
}
// store類
class Store{
}
export default {
install,
Store
}
複製代碼
vuex的初始化工做很簡單,咱們在 import Vuex 以後,會實例化其中的 Store 對象,返回 store 實例並傳入 new Vue 的 options 中,也就是咱們剛纔提到的 options.store。Store 對象的構造函數接收一個對象參數,它包含 actions、getters、state、mutations、modules 等 Vuex 的核心概念。函數
// 迭代對象的 會將對象的 key 和value 拿到
const forEach = (obj,cb)=>{
Object.keys(obj).forEach(key=>{
cb(key,obj[key]);
})
}
class Store {
constructor(options={}) {
// 將用戶的狀態放到了store中 定義了響應式變化 數據更新 更新視圖
this.s = new Vue({
data() {
return {
state: options.state
}
}
})
this.getters = {};
this.mutations = {};
this.actions = {};
// 計算屬性
forEach(options.getters,(getterName,fn)=>{
Object.defineProperty(this.getters,getterName,{
get() {
return fn(this.state)
}
})
})
//mutations
forEach(options.mutations,(mutationName,fn)=>{
this.mutations[mutationName] = (payload)=>{
// 內部的第一個參數是狀態
fn(this.state,payload)
}
})
//actions
forEach(options.actions,(actionName,fn)=>{
this.actions[actionName] = (payload)=>{
fn(this,payload)
}
})
}
get state(){ // 類的屬性訪問器
return this.s.state
}
//提交更改 找到對應的mutations函數執行
commit = (mutationName,payload)=>{
this.mutations[mutationName](payload)
}
dispatch = (actionName,payload)=>{
this.actions[actionName](payload)
}
}
複製代碼
到此,一個簡陋的vuex狀態管理就實現了,可是把全部的狀態都放到store存儲,那麼這個對象會變的十分的龐大,爲了解決以上問題,Vuex 容許咱們將 store 分割成模塊(module)。每一個模塊擁有本身的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行一樣方式的分割:ui
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的狀態
store.state.b // -> moduleB 的狀態
複製代碼
vuex的模塊是默認是沒有強制命名空間的,除了state按模塊區分了,若是模塊中的matutions和actions和主對象有同名,那麼他們會一塊兒執行。this
class Store {
constructor(options = {}){
this.s = new Vue({
data(){
return {state:options.state}
}
});
this.getters = {};
this.mutations = {};
this.actions = {};
// 模塊把數據格式化成一個有層次的樹狀結構,方面後面解析
this._modules = new ModuleCollection(options);
}
get state() {
return this.s.state
}
}
複製代碼
從數據結構上來看,模塊的設計就是一個樹型結構,store 自己能夠理解爲一個 root module,它下面的 modules 就是子模塊,Vuex 須要完成這顆樹的構建spa
// 模塊的數據結構
let root = {
_raw:options,
_chidlren:{
a:{
_raw:{},
_chidlren:{},
state:{a:1}
},
b:{}
},
state:options.state
}
複製代碼
class ModuleCollection {
constructor(options) {
this.register([], options); // 註冊模塊 將模塊註冊成樹結構
}
register(path,rootModule) {
let module = { // 將模塊格式化
_rawModule: rootModule,
_chidlren: {},
state: rootModule.state
}
if(path.length ==0 ) {
// 若是是根模塊 將這個模塊掛在到根實例上
this.root = module;
}else {
// 若是是modules 裏面的內容
// _children 屬性 找到上一級路徑
let parent = path.slice(0,-1).reduce((root,current)=>{
return root._children[current]
},this.root)
parent._children[path[path.length - 1]] = module
}
//看當前模塊是否有modules
if(rootModule.modules) {
forEach(rootModule.modules,(moduleName, module) => {
this.register(path.concat(moduleName),module)
})
}
}
}
複製代碼
ModuleCollection 實例化的過程就是執行了 register 方法,在源碼中register有3個參數,其中 path 表示路徑,由於咱們總體目標是要構建一顆模塊樹,path 是在構建樹的過程當中維護的路徑;rawModule 表示定義模塊的原始配置,另外一個是runtime表示是不是一個運行時建立的模塊,這裏不影響。設計
初始化模塊後,咱們就有了一個模塊的樹狀結構,咱們就能夠對對模塊中的 state、getters、mutations、actions 作初始化工做,把它們和store對象鏈接起來。
/* rootState:表示根上面的狀態 path: 模塊路徑 rootMudule: 當前模塊對象內容 */
const installModule = (store, rootState, path, rootModule) => {
if(path.length > 0) {
let parent = path.slice(0,-1).reduce((root,currnet)=>{
return root[current]
},rootSate)
// 找到模塊對應得狀態,按路徑 掛在 store.state
Vue.set(parent,path[path.lenght-1],rootModule.state)
}
// 處理 模塊中 getters actions mutation
let getters = rootModule._rawModule.getters;
if(getters) {
forEach(getters,(getterName,fn)=>{
Object.defineProperty(store.getters,getterName,{
get() {
// 讓getter執行當本身的狀態 傳入
return fn(rootModule.state); // 讓對應的函數執行
}
})
})
}
let mutations = rootModule._rawModule.mutations;
if (mutations) {
forEach(mutations, (mutationName, fn) => {
let mutations = store.mutations[mutationName] || [];
// 默認狀況下,沒有強制命名空間,因此 同名得mutations 會一塊兒執行
mutations.push((payload) => {
fn(rootModule.state, payload);
})
store.mutations[mutationName] = mutations;
})
}
let actions = rootModule._rawModule.actions;
if (actions) {
forEach(actions, (actionName, fn) => {
let actions = store.actions[actionName] || [];
actions.push((payload) => {
fn(store, payload);
})
store.actions[actionName] = actions;
})
}
// 處理_children裏面得module
forEach(rootModule._chidlren,(moduleName, module) => {
installModule(store, rootState, path.concat(moduleName), module)
})
}
複製代碼
有了installModule
這個處理模塊數據得方法後,咱們再改下Store的代碼:
class Store {
constructor(options = {}) {
// 將用戶的狀態放到了store中
this.s = new Vue({ // 核心 定義了響應式變化 數據更新 更新視圖
data() {
return { state: options.state }
}
}); // 用來維護全局數據的
this.getters = {};
this.mutations = {};
this.actions = {};
this._subscribes = [];
this._modules = new ModuleCollection(options); // 把數據格式化成一個 想要的樹結構
// this._modules.root 從根模塊開始安裝
installModule(this, this.state, [], this._modules.root);
}
// 提交更改 會在當前的 store上 找到對應的函數執行
commit = (mutationName, payload) => { // 保證this
this.mutations[mutationName].forEach(fn => fn(payload))
}
dispatch = (actionName, payload) => {
this.actions[actionName](payload); //源碼裏有一個變量 來控制是不是經過mutation 來更新狀態的
}
get state() { // 類的屬性訪問器
return this.s.state
}
}
複製代碼
以上就是一個簡易vuex的完整代碼了,有錯誤的地方請你們指正。