首頁 > web前端 > Vue.js > 深入了解vuex的實現原理

深入了解vuex的實現原理

青灯夜游
發布: 2023-03-20 18:23:57
轉載
2269 人瀏覽過

當面試被問到vuex的實現原理,你要怎麼回答?以下這篇文章就來帶大家深入了解vuex的實作原理,希望對大家有幫助!

深入了解vuex的實現原理

關於vuex就不再贅述,簡單回顧一下:當應用程式碰到多個元件共享狀態時,簡單的單向資料流很容易被破壞:第一,多個視圖依賴相同狀態;第二,來自不同視圖的行為需要變更相同狀態。若解決前者使用傳參的方式,則不適用於多層嵌套的組件以及兄弟組件;若解決後者使用父子組件直接引用或事件變更和同步狀態的多份拷貝,則不利於代碼維護。

所以,最好的方法是:把元件的共享狀態抽出,以一個全域單例模式管理!這也正是vuex背後的基本想法。

深入了解vuex的實現原理

【相關推薦:vuejs影片教學web前端開發

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

class Store {
    constructor() {
        // state
        // getters  
        // mutations
        // actions
    }
    // commit
    // dipatch
}
登入後複製

接下來,就寫寫看。

一、建立vue專案

vue create vue2-vuex//创建vue2项目

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

yarn serve//启动项目
登入後複製

#二、實作原理

1、State

#(1)使用<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">//store.js // 仓库 import Vue from &amp;#39;vue&amp;#39; import Vuex from &amp;#39;vuex&amp;#39; import extra from &amp;#39;./extra.js&amp;#39; Vue.use(Vuex) //引入vuex的方式,说明Store需要install方法 export default new Vuex.Store({ // 仓库数据源 state: { count: 1, dowhat: &amp;#39;addCount&amp;#39; }, }</pre><div class="contentsignin">登入後複製</div></div><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">//app.vue &lt;template&gt; &lt;div class=&quot;testState&quot;&gt; &lt;p&gt;{{mycount}}&lt;/p&gt; &lt;p&gt;{{dowhat}}:{{count}}&lt;/p&gt; &lt;/div&gt; &lt;/template&gt; &lt;script&gt; export default { import { mapState } from &amp;#39;vuex&amp;#39; // 推荐方式 computed: mapState()({ mycount: state =&gt; state.count }), // 推荐方式的简写方式 computed: { // 解构的是getters ...mapState([&amp;#39;count&amp;#39;, &amp;#39;dowhat&amp;#39;]) }, } &lt;/script&gt;</pre><div class="contentsignin">登入後複製</div></div>

(2)注意

由於Vuex 的狀態儲存是響應式的,從store實例中讀取狀態最簡單的方法就是在計算屬性 中傳回某個狀態,這種模式

導致元件依賴全域狀態單例

。在模組化的建置系統中,在每個需要使用state 的元件中需要頻繁地導入,並且在測試元件時需要模擬狀態Vuex 通過

store

選項,提供了一種機制將狀態從根元件「注入」到每一個子元件中(需呼叫Vue.use(Vuex)

(3)實作

所以除了Store內部的五大屬性以外,還需要考慮外掛程式的一個install方法,所以大致框架如下:<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">class Store { constructor() { // state // getters // mutations // actions //modules } // commit // dipatch } let Vuex = { Store, Install } export default Vuex</pre><div class="contentsignin">登入後複製</div></div>所以,接下來就可以具體實現了,<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">class Store { constructor(options) { // state this.state = options.state } } let install = function(_Vue) { _Vue.mixin({ beforeCreate() { //在组件创建之前自动调用,每个组件都有这个钩子 if (this.$options &amp;&amp; this.$options.store) { //this.$options读取根组件 this.$store = this.$options.store } else { this.$store = this.$parent &amp;&amp; this.$parent.$store } } }) }</pre><div class="contentsignin">登入後複製</div></div>然而,上述的state的實作有一個缺點:當改變資料的時候,state的資料不能動態的渲染。所以如何把state裡的資料變成響應式成為關鍵問題?實際上,類似

vue

裡的data,也可以透過這種方式讓其成為響應式。那就得從install方法傳入

Vue

#,所以改變後:

let Vue=null
class Store {
    constructor(options) {
        // state
        this.vm = new _Vue({
          data: {
            state: options.state//data中的数据才是响应式
          }
        })
    }
      get state() {
        return this.vm.state
    }
}
let install = function(_Vue) {//用于Vue.use(plugin)
    Vue=_Vue
    _Vue.mixin({
        onBeforeCreate() { //在组件创建之前自动调用,每个组件都有这个钩子
            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: {
        // 这里的函数不需要调用,可以直接使用,官方默认前面有get
        getCount(state) {//接受 state 作为其第一个参数
            return state.count * 100;
        }
    },
}
登入後複製
(2)注意

有時我們需要從store 中的state 衍生出一些狀態(例如增加,刪除,過濾等等),Vuex 允許我們在store 中定義「getter」(可以認為是store 的計算屬性)。就像計算屬性一樣,getter 的返回值會根據它的依賴被緩存起來,並且只有當它的依賴值發生了改變才會被重新計算,Getter 接受state 作為其第一個參數,getter 在通過方法訪問時,每次都會去進行調用,而不會緩存結果##(3)實現

#
 // 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({
        // 相当于methods
    mutations: {
        // mutations内部的函数,天生具备一个形参
        add(state, n) {
            state.count += n;
        },
        decrease(state, n) {
            state.count -= n;
        }
    },
}
登入後複製
methods: {
            submit() {
                console.log(&#39;success&#39;);
            },

            // 解构仓库mutations里面的方法,要啥解构啥
            ...mapMutations([&#39;add&#39;, &#39;decrease&#39;]),
            // this.$store.commit(&#39;add&#39;),
                
            ...mapActions([&#39;addAction&#39;, &#39;decreaseAction&#39;]),
            // this.addAction()调用actions里面的方法
            // this.$store.dispatch(&#39;add&#39;),
}
登入後複製
#(2)注意更改Vuex 的store 中的狀態的唯一方法是提交mutation。 Vuex 中的 mutation 類似於事件:每個 mutation 都有一個字串的 事件類型 (type) 和 一個

回呼函數 (handler)

。這個回呼函數就是進行狀態變更的地方,而且它會接受 state 作為第一個參數,不能直接呼叫一個 mutation handler。這個選項更像是事件註冊:「當觸發一個類型為increment 的mutation 時,呼叫此函數。」要喚醒一個mutation handler,你需要以對應的type 呼叫store.commit 方法

可以向store.commit 傳入額外的參數,即mutation 的

負載(payload)

,在大多數情況下,負載應該是一個對象,這樣可以包含多個欄位且記錄的mutation 會更易讀(3)實作

   // mutations
    let mutations = options.mutations || {}
    this.mutations = {}
    Object.keys(mutations).forEach(mutationName => {
      this.mutations[mutationName] = (arg) => {//保证多个(第二个)参数的传入
        mutations[mutationName](this.state, arg)
      }
    })
    
    commit = (method, arg) => {//使用箭头函数改变被调用的this的指向
        // console.log(this);
        this.mutations[method](arg)
  }
登入後複製

4、actions

#(1)使用######
//store.js
export default new Vuex.Store({
    actions: {
        addAction(context) {
            // 在这里调用add方法
            context.commit(&#39;add&#39;, 10);
        },
        decreaseAction({
            commit
        }) {
            commit(&#39;decreaseAction&#39;, 5)
        }
    },
}
登入後複製
######(2)注意#######
  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作
  • Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象
  • Action 通过 store.dispatch 方法触发
  • Action 通常是异步的,store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise
  • 一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行

(3)实现

    // 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)注意

  • 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿,Vuex 允许我们将 store 分割成模块(module) 。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割
  • 对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象
  • 对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState
  • 对于模块内部的 getter,根节点状态(rootState)会作为第三个参数暴露出来

三、整体代码

let Vue = null//全局的_Vue

class Store {
  constructor (options) {
    // state
    //this.state = options.state 写法不完美,当改变数据的时候,不能动态的渲染,所以需要把data中的数据做成响应式的
    this.vm = new _Vue({
      data: {
        state: options.state//data中的数据才是响应式
      }
    })

    // 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) => {//保证多个(第二个)参数的传入
        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() {//在组件创建之前自动调用,每个组件都有这个钩子
      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
登入後複製

(学习视频分享:vuejs入门教程编程基础视频

以上是深入了解vuex的實現原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:juejin.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板