0

0

0

修罗

站点介绍

只有了解事实才能获得真正的自由

手写vuex

修罗 2020-10-26 1738 0条评论 vue

首页 / 正文

手写vuex

任务分析

实现一个插件:声明Store类,挂载$store

Store具体实现:

  • 创建响应式的state,保存mutations、actions和getters
  • 实现commit根据用户传入type执行对应mutation
  • 实现dispatch根据用户传入type执行对应action,同时传递上下文
  • 实现getters,按照getters定义对state做派生

定义vuex.js

store/vuex.js

let Vue;

class Store{
  /**
   * 下面构造参数options格式:
   * {
      state: {},
      mutations: {},
      actions: {},
      getters: {}
    }
   */
  constructor(options){
    /*
    下面mutations格式
    mutations: {
      add(state){
        state.count++
      }
    }*/
    this._mutations = options.mutations
    this._actions = options.actions
    this._wrappedGetters = options.getters

    // 响应化处理state
      this.state = new Vue({
        // 根组件直接赋值,不用函数
        data: options.state
      })
  }

  // type: add
  // playload是参数
  commit(type, payload){
    // 拿到用户在index定义的mutations
    const entry = this._mutations[type];
    if(entry){
      entry(this.state, payload)
    }
  }

  dispatch(type, payload){
    // 拿到用户在index定义的actions
    const entry = this._actions[type];
    if(entry){
      // this就有dispatch/commit
      entry(this, payload)
    }
  }
}

function install(_Vue){
  Vue = _Vue
  Vue.mixin({
    beforeCreate(){
      if(this.$options.store){
        Vue.prototype.$store = this.$options.store
      }
    }
  })
}

export default{
  Store,
  install
}

store/index.js

import Vue from 'vue'
import Vuex from './vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 10 
  },
  mutations: {
    add(state){
      state.count++
    }
  },
  actions: {
    add({ commit }){
      setTimeout(() => {
        commit('add')
      }, 1000)
      
    }
  }
})

1603720077240.png

效果

1603720122261.png

但是点击dispatch时发现报错。原因是vuex.js的commit没有绑定this,如果commit在定时器提交,this就会乱

修改代码,下面代码放在vuex.js的store的构造里

this.commit = this.commit.bind(this)
this.dispatch = this.dispatch.bind(this)

再点击dispatch就会发现等待一秒count+1了

继续优化

将vuex.js的store的构造中的

// 响应化处理state
this.state = new Vue({
    // 根组件直接赋值,不用函数
    data: options.state
})

换成

// 严格模式下官方定义了watch,只要有任何修改就报错
this._vm = new Vue({
    data: {
        // 加两个$,Vue不做代理,无法通过_vm拿到
        $$state: options.state,
    }
})

再定义读取函数作为vuex.js的store的普通函数

// 存取器
// this.state返回this._vm._data.$$state
get state(){
    // _data可换成$data
    return this._vm._data.$$state
}

set state(v){
    console.error('不允许直接修改哦')
}

此时,直接this.$store.state.count = {}会报错

1603721099263.png

加入getters支持

也是完整demo代码

store/vuex.js

let Vue;

class Store{
  /**
   * options:
   * {
      state: {},
      mutations: {},
      actions: {},
      getters: {}
    }
   */
  constructor(options){
    /*
    下面mutations格式
    mutations: {
      add(state){
        state.count++
      }
    }*/
    this._mutations = options.mutations
    this._actions = options.actions
    this._wrappedGetters = options.getters

    // 定义computed选项
    const computed = {}
    this.getters = {}
    // computed无参,这里有state需要进行封装
    // doubleCounts(state){ return state.count *= 2 }
    const store = this
    Object.keys(this._wrappedGetters).forEach(key => {
      // 获取用户定义的getter,注意fn带了state参数
      const fn = store._wrappedGetters[key]
      // 转换为computed可以使用的无参数形式
      computed[key] = function(){
        // 把this.state传递进去
        return fn(store.state)
      }
      // 为getter定义只读属性
      Object.defineProperty(store.getters, key, {
        get: () => store._vm[key]
      })
    })

    /* // 响应化处理state
      this.state = new Vue({
        // 根组件直接赋值,不用函数
        data: options.state
      })
    */

    // 严格模式下官方定义了watch,只要有任何修改就报错
    this._vm = new Vue({
      data: {
        // 加两个$,Vue不做代理,无法通过_vm拿到
        $$state: options.state,
      },
      computed
    }) 

    // 绑定commit.dispatch的上下文store实例,没有绑定this,如果commit在定时器提交,this就会乱
    this.commit = this.commit.bind(this)
    this.dispatch = this.dispatch.bind(this)
  }

  // 存取器
  get state(){
     // _data可换成$data
     return this._vm._data.$$state
  }

  set state(v){
    console.error('不允许直接修改哦')
  }

  // type: add
  // playload是参数
  commit(type, payload){
    // 拿到用户在index定义的mutations
    const entry = this._mutations[type];
    if(entry){
      // this.state返回this._vm._data.$$state
      entry(this.state, payload)
    }
  }

  dispatch(type, payload){
    // 拿到用户在index定义的actions
    const entry = this._actions[type];
    if(entry){
      // this就有dispatch/commit
      entry(this, payload)
    }
  }
}

function install(_Vue){
  Vue = _Vue
  Vue.mixin({
    beforeCreate(){
      if(this.$options.store){
        Vue.prototype.$store = this.$options.store
      }
    }
  })
}

export default{
  Store,
  install
}

store/index.js

import Vue from 'vue'
import Vuex from './vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 10 
  },
  mutations: {
    add(state){
      state.count++
    }
  },
  actions: {
    add({ commit }){
      setTimeout(() => {
        commit('add')
      }, 1000)
      
    }
  },
  getters: {
    // 返回2倍的count
    doubleCounts(state){
      return state.count * 2
    }
  }
})

效果

1603721380032.png

评论(0)


最新评论

  • https://www.studyglobus.com/author/carroll34a5552/

    Ценники на вывоз мусора сейчас космос, любые лайфхаки по экономии в тему.

  • Richardgrold

    1xbet onlayn kazino platforması ilə vaxtınızı daha səmərəli və maraqlı keçirin. Saytın canlı kazino bölməsi istifadəçilərə fərqli bir dünya təklif edir. Burada canlı dilerli kazino oyunlarının hər bir növünü sınaqdan keçirə bilərsiniz. Krupyelərin peşəkarlığı oyunun dinamikasını və marağını artırır. Müntəzəm olaraq yenilənən oyun siyahısı cansıxıcılığa yol vermir. Hesabınızı artırmaq üçün onlarla fərqli və etibarlı üsul mövcuddur. Qazanılan pullar cəmi bir neçə dəqiqə ərzində şəxsi hesabınıza köçürülür. Aktiv fəaliyyətinizə görə fərdi bonuslar və hədiyyələr qazana bilərsiniz. Dəstək xidməti həm çat, həm də e-poçt vasitəsilə kömək göstərir. 1xbet ailəsinə qoşulun və böyük uduşlar üçün ilk addımı atın. onlayn kazino

  • 砂岩白丘

    我一直梦想, 看到你们相册那样的地方。真棒。

  • 城市地標

    我喜欢, 这里分享真实经验。你的项目 就是 属于这里的。请继续。

  • 宗教地標

    我热爱这样的想法, 去那么多国家。真的很鼓舞。

  • 觀景高地

    能感受到热爱。继续保持 感受。

  • 最佳季節

    我关注这样的资源, 写得很实在。你的网站 就是 属于这里的。加油。

  • 1

    1

  • 1

    1

  • -1' OR 2+158-158-1=0+0+0+1 or 'TKCTZnRa'='

    1

日历

2026年07月

   1234
567891011
12131415161718
19202122232425
262728293031 

文章目录

推荐关键字: Linux webpack js 算法 MongoDB laravel JAVA jquery javase redis