0

0

0

修罗

站点介绍

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

手写vuex

修罗 2020-10-26 1541 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)


最新评论

  • 1

    1

  • 1

    1

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

    1

  • 1

    1

  • 1

    1

  • 1

    1

  • 1

    1

  • @@5Qa2D

    1

  • 1

    1

  • 1

    1

日历

2025年09月

 123456
78910111213
14151617181920
21222324252627
282930    

文章目录

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