1. 程式人生 > 程式設計 >如何手寫一個簡易的 Vuex

如何手寫一個簡易的 Vuex

前言

本文適合使用過 Vuex 的人閱讀,來了解下怎麼自己實現一個 Vuex。

基本骨架

這是本專案的src/store/index.js檔案,看看一般 vuex 的使用

import Vue from 'vue'
import Vuex from './myvuex' // 引入自己寫的 vuex
import * as getters from './getters'
import * as actions from './actions'
import state from './state'
import mutations from './mutations'

Vue.use(Vuex) // Vue.use(plugin)方法使用vuex外掛

// vuex 匯出一個類叫Store,並傳入物件作為引數
export default new Vuex.Store({
 state,mutations,actions,getters,})

Vue.use的用法:

安裝 Vue.js 外掛。如果外掛是一個物件,必須提供 install 方法。如果外掛是一個函式,它會被作為 install 方法。install 方法呼叫時,會將 Vue 作為引數傳入。這個方法的第一個引數是 Vue 構造器,第二個引數是一個可選的選項物件。

  • 該方法需要在呼叫 new Vue() 之前被呼叫。
  • 當 install 方法被同一個外掛多次呼叫,外掛將只會被安裝一次。

即是我們需要在./myvuex.js中匯出 install方法,同時匯出一個類Store,於是第一步可以寫出程式碼:

let Vue = null

class Store {
 constructor(options) {}
}

function install(_Vue) {
 Vue = _Vue // 上面Store類需要能獲取到Vue
}

export default {
 Store,install,}

install 方法

當我們使用 vuex 的時候,每一個元件上面都有一個this.$store屬性,裡面包含了 state,mutations, actions, getters 等,所以我們也需要在每個元件上都掛載一個$store 屬性,要讓每一個元件都能獲取到,這裡我們使用Vue.mixin(mixin),用法介紹如下:

全域性註冊一個混入,影響註冊之後所有建立的每個 Vue 例項。可以使用混入向元件注入自定義的行為,它將影響每一個之後建立的 Vue 例項。

function install(_Vue) {
 Vue = _Vue // install方法呼叫時,會將Vue作為引數傳入(上面Store類需要用到Vue)
 // 實現每一個元件,都能通過this呼叫$store
 Vue.mixin({
  beforeCreate() {
   // 通過this.$options可以獲取new Vue({引數}) 傳遞的引數
   if (this.$options && this.$options.store) {
    // 證明這個this是根例項,也就是new Vue產生的那個例項
    this.$store = this.$options.store
   } else if (this.$parent && this.$parent.$store) {
    // 子元件獲取父元件的$store屬性
    this.$store = this.$parent.$store
   }
  },})
}

state

由於 Vuex 是基於 Vue 的響應式原理基礎,所以我們要讓資料改變可重新整理檢視,則需要建立一個 vue 例項

class Store {
 // options 即是 Vuex.Store({})傳入的引數
 constructor(options) {
  // vuex 的核心就是借用了vue的例項,因為vue的例項資料變化,會重新整理檢視
  let vm = new Vue({
   data: {
    state: options.state,},})
  // state
  this.state = vm.state
 }
}

commit

我們使用 vuex 改變資料時,是觸發 commit 方法,即是這樣使用的:

this.$store.commit('eventName','引數' );

所以我們要實現一個commit方法,把 Store 建構函式傳入的 mutations 做下處理

class Store {
 constructor(options) {
  // 實現 state ...

  // mutations
  this.mutations = {} // 儲存傳進來的mutations
  let mutations = options.mutations || {}
  // 迴圈取出事件名進行處理(mutations[事件名]: 執行方法)
  Object.keys(mutations).forEach(key => {
   this.mutations[key] = params => {
    mutations[key].call(this,this.state,params) // 修正this指向
   }
  })
 }

 commit = (key,params) => {
  // key為要觸發的事件名
  this.mutations[key](params)
 }
}

dispatch

跟上面的 commit 流程同理

class Store {
 constructor(options = {}) {
  // ...

  // actions
  this.actions = {}
  let actions = options.actions || {}
  Object.keys(actions).forEach(key => {
   this.actions[key] = params => {
    actions[key].call(this,this,params)
   }
  })
 }

 dispatch = (type,payload) => {
  this.actions[type](payload)
 }
}

getters

getters 實際就是返回 state 的值,在使用的時候是放在 computed 屬性,每一個 getter 都是函式形式;

getters 是需要雙向繫結的。但不需要雙向繫結所有的 getters,只需要繫結專案中事件使用的 getters。

這裡使用Object.defineProperty()方法,它會直接在一個物件上定義一個新屬性,或者修改一個物件的現有屬性,並返回此物件。

class Store {
 constructor(options = {}) {
  // ...

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

到此為止,已經可以使用我們自己寫的 vuex 做一些基本操作了,但只能通過this.$store.xx的形式呼叫,故需要再實現方法。

map 輔助函式

先來說說 mapState

沒有 map 輔助函式之前這樣使用:

computed: {
 count () {
  return this.$store.state.count
 }
}

當對映的計算屬性的名稱與 state 的子節點名稱相同時,給 mapState 傳一個字串陣列。

computed: {
 // 使用物件展開運算子將此物件混入到外部物件中
 ...mapState(['count'])
}

我們這裡簡單就只實現陣列的情況

export const mapState = args => {
 let obj = {}
 args.forEach(item => {
  obj[item] = function() {
   return this.$store.state[item]
  }
 })
 return obj
}

之後幾個 map 輔助函式都是類似

  • mapGetters
export const mapGetters = args => {
 let obj = {}
 args.forEach(item => {
  obj[item] = function() {
   return this.$store.getters[item]
  }
 })
 return obj
}
  • mapMutations
export const mapMutations = args => {
 let obj = {}
 args.forEach(item => {
  obj[item] = function(params) {
   return this.$store.commit(item,params)
  }
 })
 return obj
}
  • mapActions
export const mapActions = args => {
 let obj = {}
 args.forEach(item => {
  obj[item] = function(payload) {
   return this.$store.dispatch(item,payload)
  }
 })
 return obj
}

完整程式碼

let Vue = null

class Store {
 constructor(options) {
  // vuex 的核心就是借用了vue的例項,因為vue的例項資料變化,會重新整理檢視
  let vm = new Vue({
   data: {
    state: options.state,})
  // state
  this.state = vm.state

  // mutations
  this.mutations = {} // 儲存傳進來的mutations
  let mutations = options.mutations || {}
  Object.keys(mutations).forEach(key => {
   this.mutations[key] = params => {
    mutations[key].call(this,params)
   }
  })

  // actions
  this.actions = {}
  let actions = options.actions || {}
  Object.keys(actions).forEach(key => {
   this.actions[key] = params => {
    actions[key].call(this,params)
   }
  })

  // getters
  this.getters = {}
  let getters = options.getters || {}
  Object.keys(getters).forEach(key => {
   Object.defineProperty(this.getters,})
  })
 }

 commit = (key,params) => {
  this.mutations[key](params)
 }

 dispatch = (type,payload) => {
  this.actions[type](payload)
 }
}

export const mapState = args => {
 let obj = {}
 args.forEach(item => {
  obj[item] = function() {
   return this.$store.state[item]
  }
 })
 return obj
}

export const mapGetters = args => {
 let obj = {}
 args.forEach(item => {
  obj[item] = function() {
   return this.$store.getters[item]
  }
 })
 return obj
}

export const mapMutations = args => {
 let obj = {}
 args.forEach(item => {
  obj[item] = function(params) {
   return this.$store.commit(item,params)
  }
 })
 return obj
}

export const mapActions = args => {
 let obj = {}
 args.forEach(item => {
  obj[item] = function(payload) {
   return this.$store.dispatch(item,payload)
  }
 })
 return obj
}

function install(_Vue) {
 Vue = _Vue // install方法呼叫時,會將Vue作為引數傳入(上面Store類需要用到Vue)
 // 實現每一個元件,都能通過this呼叫$store
 Vue.mixin({
  beforeCreate() {
   // 通過this.$options可以獲取new Vue({引數}) 傳遞的引數
   if (this.$options && this.$options.store) {
    // 證明這個this是根例項,也就是new Vue產生的那個例項
    this.$store = this.$options.store
   } else if (this.$parent && this.$parent.$store) {
    // 子元件獲取父元件的$store屬性
    this.$store = this.$parent.$store
   }
  },})
}

export default {
 Store,}

以上就是如何手寫一個簡易的 Vuex的詳細內容,更多關於手寫 vuex的資料請關注我們其它相關文章!