溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

vuex的實現(xiàn)原理是什么

發(fā)布時間:2023-03-21 09:09:36 來源:億速云 閱讀:118 作者:iii 欄目:編程語言

本篇內(nèi)容主要講解“vuex的實現(xiàn)原理是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“vuex的實現(xiàn)原理是什么”吧!

關(guān)于vuex就不再贅述,簡單回顧一下:當(dāng)應(yīng)用碰到多個組件共享狀態(tài)時,簡單的單向數(shù)據(jù)流很容易被破壞:第一,多個視圖依賴于同一狀態(tài);第二,來自不同視圖的行為需要變更同一狀態(tài)。若解決前者使用傳參的方式,則不適用于多層嵌套的組件以及兄弟組件;若解決后者使用父子組件直接引用或事件變更和同步狀態(tài)的多份拷貝,則不利于代碼維護。

所以,最好的辦法是:把組件的共享狀態(tài)抽取出,以一個全局單例模式管理!這也正是vuex背后的基本思想。

vuex的實現(xiàn)原理是什么

所以,vuex的大致框架如下:

class Store {
    constructor() {
        // state
        // getters  
        // mutations
        // actions
    }
    // commit
    // dipatch
}

接下來,就寫寫看。

一、創(chuàng)建vue項目

vue create vue2-vuex//創(chuàng)建vue2項目

yarn add vuex@next --save//安裝vuex

yarn serve//啟動項目

二、實現(xiàn)原理

1、State

(1)使用

//store.js
// 倉庫
import Vue from 'vue'
import Vuex from 'vuex'
import extra from './extra.js'

Vue.use(Vuex) //引入vuex的方式,說明Store需要install方法
export default new Vuex.Store({
    // 倉庫數(shù)據(jù)源
    state: {
        count: 1,
        dowhat: 'addCount'
    },
}
//app.vue
<template>
    <div class="testState">
        <p>{{mycount}}</p>
        <p>{{dowhat}}:{{count}}</p>
    </div>
</template>

<script>
export default {
    import {
        mapState
    } from 'vuex'
    // 推薦方式
    computed: mapState()({
        mycount: state => state.count
    }),
    // 推薦方式的簡寫方式
    computed: {
        // 解構(gòu)的是getters
        ...mapState(['count', 'dowhat'])
    },
  }
</script>

(2)注意

由于 Vuex 的狀態(tài)存儲是響應(yīng)式的,從 store 實例中讀取狀態(tài)最簡單的方法就是在計算屬性 中返回某個狀態(tài),這種模式導(dǎo)致組件依賴全局狀態(tài)單例。在模塊化的構(gòu)建系統(tǒng)中,在每個需要使用 state 的組件中需要頻繁地導(dǎo)入,并且在測試組件時需要模擬狀態(tài)

Vuex 通過 store 選項,提供了一種機制將狀態(tài)從根組件“注入”到每一個子組件中(需調(diào)用 Vue.use(Vuex)

(3)實現(xiàn)

所以除了Store內(nèi)部的五大屬性以外,還需要考慮插件的一個install方法,所以大致框架如下:

class Store {
    constructor() {
        // state
        // getters  
        // mutations
        // actions
        //modules
    }
    // commit
    // dipatch
}
let Vuex = {
    Store,
    Install
}
export default Vuex

所以,接下來就可以具體實現(xiàn)了,

class Store {
    constructor(options) {
        // state
        this.state = options.state
    }
}
let install = function(_Vue) {
    _Vue.mixin({
        beforeCreate() { //在組件創(chuàng)建之前自動調(diào)用,每個組件都有這個鉤子
            if (this.$options && this.$options.store) { //this.$options讀取根組件
                this.$store = this.$options.store
            } else {
                this.$store = this.$parent && this.$parent.$store
            }
        }
    })
}

然而,上述的state的實現(xiàn)有一個缺點:當(dāng)改變數(shù)據(jù)的時候,state的數(shù)據(jù)不能動態(tài)的渲染。所以如何把state里的數(shù)據(jù)成為響應(yīng)式成為關(guān)鍵問題?實際上,類似vue里的data,也可以通過這種方式讓其成為響應(yīng)式。那么就得從install方法中傳入Vue,所以改變后:

let Vue=null
class Store {
    constructor(options) {
        // state
        this.vm = new _Vue({
          data: {
            state: options.state//data中的數(shù)據(jù)才是響應(yīng)式
          }
        })
    }
      get state() {
        return this.vm.state
    }
}
let install = function(_Vue) {//用于Vue.use(plugin)
    Vue=_Vue
    _Vue.mixin({
        onBeforeCreate() { //在組件創(chuàng)建之前自動調(diào)用,每個組件都有這個鉤子
            if (this.$options && this.$options.store) { //this.$options讀取根組件
                this.$store = this.$options.store
            } else {
                this.$store = this.$parent && this.$parent.$store
            }
        }
    })
}

2、getters

(1)使用

//store.js
export default new Vuex.Store({
    // 計算屬性
    getters: {
        // 這里的函數(shù)不需要調(diào)用,可以直接使用,官方默認前面有g(shù)et
        getCount(state) {//接受 state 作為其第一個參數(shù)
            return state.count * 100;
        }
    },
}

(2)注意

有時候我們需要從 store 中的 state 中派生出一些狀態(tài)(比如增加,刪除,過濾等等),Vuex 允許我們在 store 中定義“getter”(可以認為是 store 的計算屬性)。就像計算屬性一樣,getter 的返回值會根據(jù)它的依賴被緩存起來,且只有當(dāng)它的依賴值發(fā)生了改變才會被重新計算,Getter 接受 state 作為其第一個參數(shù),getter 在通過方法訪問時,每次都會去進行調(diào)用,而不會緩存結(jié)果

(3)實現(xiàn)

 // getters
    let getters = options.getters || {}
    this.getters = {}
    Object.keys(getters).forEach(getterName => {
      Object.defineProperty(this.getters, getterName, {
        get: () => {
          return getters[getterName](this.state)
        }
      })
    })

3、mutations

(1)使用

//store.js
export default new Vuex.Store({
        // 相當(dāng)于methods
    mutations: {
        // mutations內(nèi)部的函數(shù),天生具備一個形參
        add(state, n) {
            state.count += n;
        },
        decrease(state, n) {
            state.count -= n;
        }
    },
}
methods: {
            submit() {
                console.log('success');
            },

            // 解構(gòu)倉庫mutations里面的方法,要啥解構(gòu)啥
            ...mapMutations(['add', 'decrease']),
            // this.$store.commit('add'),
                
            ...mapActions(['addAction', 'decreaseAction']),
            // this.addAction()調(diào)用actions里面的方法
            // this.$store.dispatch('add'),
}

(2)注意

更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation。Vuex 中的 mutation 類似于事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調(diào)函數(shù) (handler) 。這個回調(diào)函數(shù)就是進行狀態(tài)更改的地方,并且它會接受 state 作為第一個參數(shù),不能直接調(diào)用一個 mutation handler。這個選項更像是事件注冊:“當(dāng)觸發(fā)一個類型為 increment 的 mutation 時,調(diào)用此函數(shù)?!币獑拘岩粋€ mutation handler,你需要以相應(yīng)的 type 調(diào)用 store.commit 方法

可以向 store.commit 傳入額外的參數(shù),即 mutation 的 載荷(payload) ,在大多數(shù)情況下,載荷應(yīng)該是一個對象,這樣可以包含多個字段并且記錄的 mutation 會更易讀

(3)實現(xiàn)

   // mutations
    let mutations = options.mutations || {}
    this.mutations = {}
    Object.keys(mutations).forEach(mutationName => {
      this.mutations[mutationName] = (arg) => {//保證多個(第二個)參數(shù)的傳入
        mutations[mutationName](this.state, arg)
      }
    })
    
    commit = (method, arg) => {//使用箭頭函數(shù)改變被調(diào)用的this的指向
        // console.log(this);
        this.mutations[method](arg)
  }

4、actions

(1)使用

//store.js
export default new Vuex.Store({
    actions: {
        addAction(context) {
            // 在這里調(diào)用add方法
            context.commit('add', 10);
        },
        decreaseAction({
            commit
        }) {
            commit('decreaseAction', 5)
        }
    },
}

(2)注意

  • Action 提交的是 mutation,而不是直接變更狀態(tài)。

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

  • Action 函數(shù)接受一個與 store 實例具有相同方法和屬性的 context 對象

  • Action 通過 store.dispatch 方法觸發(fā)

  • Action 通常是異步的,store.dispatch 可以處理被觸發(fā)的 action 的處理函數(shù)返回的 Promise,并且 store.dispatch 仍舊返回 Promise

  • 一個 store.dispatch 在不同模塊中可以觸發(fā)多個 action 函數(shù)。在這種情況下,只有當(dāng)所有觸發(fā)函數(shù)完成后,返回的 Promise 才會執(zhí)行

(3)實現(xiàn)

    // actions
    let actions = options.actions || {}
    this.actions = {}
    Object.keys(actions).forEach(actionName => {
      this.actions[actionName] = (arg) => {
        actions[actionName](this, arg)
      }
    })
    
   dispatch=(method, arg) =>{
    this.actions[method](arg)
  }

5、modules

(1)使用

    // actions
    let actions = options.actions || {}
    this.actions = {}
    Object.keys(actions).forEach(actionName => {
      this.actions[actionName] = (arg) => {
        actions[actionName](this, arg)
      }
    })
    
   dispatch=(method, arg) =>{
    this.actions[method](arg)
  }
    //store.js
    modules: {
        extra: extra
    }

(2)注意

  • 由于使用單一狀態(tài)樹,應(yīng)用的所有狀態(tài)會集中到一個比較大的對象。當(dāng)應(yīng)用變得非常復(fù)雜時,store 對象就有可能變得相當(dāng)臃腫,Vuex 允許我們將 store 分割成模塊(module) 。每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行同樣方式的分割

  • 對于模塊內(nèi)部的 mutation 和 getter,接收的第一個參數(shù)是模塊的局部狀態(tài)對象

  • 對于模塊內(nèi)部的 action,局部狀態(tài)通過 context.state 暴露出來,根節(jié)點狀態(tài)則為 context.rootState

  • 對于模塊內(nèi)部的 getter,根節(jié)點狀態(tài)(rootState)會作為第三個參數(shù)暴露出來

三、整體代碼

let Vue = null//全局的_Vue

class Store {
  constructor (options) {
    // state
    //this.state = options.state 寫法不完美,當(dāng)改變數(shù)據(jù)的時候,不能動態(tài)的渲染,所以需要把data中的數(shù)據(jù)做成響應(yīng)式的
    this.vm = new _Vue({
      data: {
        state: options.state//data中的數(shù)據(jù)才是響應(yīng)式
      }
    })

    // getters
    let getters = options.getters || {}
    this.getters = {}
    Object.keys(getters).forEach(getterName => {
      Object.defineProperty(this.getters, getterName, {
        get: () => {
          return getters[getterName](this.state)
        }
      })
    })

    // mutations
    let mutations = options.mutations || {}
    this.mutations = {}
    Object.keys(mutations).forEach(mutationName => {
      this.mutations[mutationName] = (arg) => {//保證多個(第二個)參數(shù)的傳入
        mutations[mutationName](this.state, arg)
      }
    })

    // actions
    let actions = options.actions || {}
    this.actions = {}
    Object.keys(actions).forEach(actionName => {
      this.actions[actionName] = (arg) => {
        actions[actionName](this, arg)
      }
    })

  }

  dispatch=(method, arg) =>{
    this.actions[method](arg)
  }

  commit = (method, arg) => {
    // console.log(this);
    this.mutations[method](arg)
  }

  get state() {
    return this.vm.state
  }

}

let install = function(_Vue) {
  Vue = _Vue
  Vue.mixin({
    beforeCreate() {//在組件創(chuàng)建之前自動調(diào)用,每個組件都有這個鉤子
      if (this.$options && this.$options.store) {  // this.$options讀取到根組件
        this.$store = this.$options.store
      } else { // //如果不是根組件的話,也把$store掛到上面,因為是樹狀組件
        this.$store = this.$parent && this.$parent.$store
      }
    }
  })
}

let Vuex = {
  Store,
  install
}

export default Vuex

到此,相信大家對“vuex的實現(xiàn)原理是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI