vuex4源码分析学习

通过学习和理解,实现一个简易版的vuex

原理

各个组件状态管理,通过vue提供的依赖注入provide\ inject 方法实现

1. createStore

首先创建一个Store类。创建实例后,app.use安装插件,向组件提供键值为store的值

javascript 复制代码
export function createStore(options) {
  return new Store(options);
}

class Store {
  // vue中app.use需要install
  install(app) {
    app.provide('store', this);
  }
}

2. useStore

使用时需要注入store,获取对应store键值提供的值并返回

javascript 复制代码
import { inject } from 'vue'

export function useStore() {
  return inject('store');
}

State

  1. state有两种使用方式,一是函数返回,二是普通对象
  2. state需要通过reactive实现响应式
  3. state的值只能通过commit进行修改,直接设置store.state是禁止的
javascript 复制代码
class Store {
  constructor(options) {
    this._modules =  new ModuleCollection(options);
  
    // 获取state
    const state = this._modules.root.state;
    
    // 重置(初始化)getters及配置getters的compute
    resetStoreState(this, state);
  }
  
  get state() {
    return this._state.data;
  }

  set state(value) {
    // 禁止直接修改state
    console.log('禁止直接修改state');
  }
}

function resetStoreState(store, state) {
  // 设置数据响应式,这一步最重要
  store._state = reactive({
    data: state,
  });
}

class ModuleCollection {
  constructor(options) {
    this.register(options);
  }

  register(rawState) {
    // 注册根对象
    this.root = new Module(rawState);
  }
}

class Module {
  constructor(rawModule) {
    this.state = typeof rawModule.state === 'function' ? rawModule.state() : rawModule.state;
  }
}

getters

javascript 复制代码
class Store {
  constructor(options) {
    this._modules =  new ModuleCollection(options);
    this._wrappedGetters = Object.create(null);
    ...

    for (const gettersKey in options.getters) {
      registerGetters(this, gettersKey, options.getters[gettersKey]);
    }

    // 重置(初始化)getters及配置getters的compute
    resetStoreState(this, state);
  }

  ...
}

function resetStoreState(store, state) {
  ...

  store.getters = {};
  
  for (const gettersKey in store._wrappedGetters) {
    Object.defineProperty(store.getters, gettersKey, {
      get() {
        const data = computed(() => store._wrappedGetters[gettersKey]());
        return data.value;
      },
      enumerable: true,
    })
  }
}

function registerGetters(store, type, getters) {
  // 赋值到_wrappedGetters
  store._wrappedGetters[type] = () => {
    return getters(store.state); // 相当于使用时state的传值,例count(state) { return state.count }
  }
}

mutations

commit方法中执行对应的mutations中所有函数,同步执行

javascript 复制代码
class Store {
  constructor(options) {
    this._modules =  new ModuleCollection(options);
    this._wrappedGetters = Object.create(null);
    this._mutations = Object.create(null);
    ...

    for (const mutationsKey in options.mutations) {
      registerMutation(this, mutationsKey, options.mutations[mutationsKey])
    }

    ...
  }

  ...
  
  commit(type, payload) {
    const entry = this._mutations[type];

    entry.forEach(fn => {
      fn(payload)
    });
  }
}

function registerMutation(store, type, mutation) {
  // 赋值到_mutations
  const entry = store._mutations[type] || (store._mutations[type] = []);
  entry.push(function (payload) {
    mutation.call(store, store.state, payload);
  });
}

actions

  1. 在dispatch方法调用时,需要重新绑定dispath和commit的this指向
  2. dispatch方法中通过Promise.all同时执行对应的mutations中所有函数,支持异步执行
javascript 复制代码
class Store {
  constructor(options) {
    this._modules =  new ModuleCollection(options);
    this._wrappedGetters = Object.create(null);
    this._mutations = Object.create(null);
    this._actions = Object.create(null);
  
    ...

    for (const actionsKey in options.actions) {
      registeAction(this, actionsKey, options.actions[actionsKey])
    }

    // 需要重新绑定dispath和commit的this指向
    const store = this
    const { dispatch, commit } = this
    this.dispatch = function boundDispatch (type, payload) {
      return dispatch.call(store, type, payload)
    }
    this.commit = function boundCommit (type, payload) {
      return commit.call(store, type, payload)
    }

    // 重置(初始化)getters及配置getters的compute
    resetStoreState(this, state);
  }

  ...

  dispatch(type, payload) {
    const _this = this;
    
    const entry = this._actions[type];

    if (!entry) {
      return
    }

    const result = Promise.all(entry.map(handler => {
      handler.call(_this, payload)
    }));

    return new Promise((resolve, reject) => {
      result.then((res) => {
        resolve(res);
      })
    })
  }
}

function registeAction(store, type, action) {
  // 赋值到_actions
  const entry = store._actions[type] || (store._actions[type] = []);
  entry.push(function (payload) {
    action.call(store, {
      state: store.state,
      // commit: (type, payload) => store.commit.call(store, type, payload),
      commit: store.commit,
      // dispatch: (type, payload) => store.dispatch.call(store, type, payload),
      dispatch: store.dispatch,
    }, payload);
  });
}

如果commitdispatch未重新绑定this,下述registerAction中引入这两个方法会出现this指向错误

javascript 复制代码
function registeAction(store, type, action) {
  ...
  entry.push(function (payload) {
    action.call(store, {
      state: store.state,
      commit: store.commit,
      dispatch: store.dispatch,
    }, payload);
  });
}

// 可修改为,如下写法
function registeAction(store, type, action) {
  ...
  entry.push(function (payload) {
    action.call(store, {
      state: store.state,
      commit: (type, payload) => store.commit.call(store, type, payload),,
      dispatch: (type, payload) => store.dispatch.call(store, type, payload),
    }, payload);
  });
}

完整代码

javascript 复制代码
import { computed, inject, reactive } from 'vue'

export function createStore(options) {
  return new Store(options);
}

export function useStore() {
  return inject('store');
}

class Store {
  constructor(options) {
    this._modules =  new ModuleCollection(options);
    this._wrappedGetters = Object.create(null);
    this._mutations = Object.create(null);
    this._actions = Object.create(null);
  
    // 获取state
    const state = this._modules.root.state;

    for (const gettersKey in options.getters) {
      registerGetters(this, gettersKey, options.getters[gettersKey]);
    }

    for (const mutationsKey in options.mutations) {
      registerMutation(this, mutationsKey, options.mutations[mutationsKey])
    }

    for (const actionsKey in options.actions) {
      registeAction(this, actionsKey, options.actions[actionsKey])
    }

    const store = this
    const { dispatch, commit } = this
    this.dispatch = function boundDispatch (type, payload) {
      return dispatch.call(store, type, payload)
    }
    this.commit = function boundCommit (type, payload) {
      return commit.call(store, type, payload)
    }

    // 重置(初始化)getters及配置getters的compute
    resetStoreState(this, state);
  }

  get state() {
    return this._state.data;
  }

  set state(value) {
    // 禁止直接修改state
    console.log('禁止直接修改state');
  }

  install(app) {
    app.provide('store', this);
  }

  commit(type, payload) {
    console.log('commit-this', this);
    
    const entry = this._mutations[type];

    entry.forEach(fn => {
      fn(payload)
    });
  }

  dispatch(type, payload) {
    const _this = this;
    console.log('_this', _this);
    
    const entry = this._actions[type];

    if (!entry) {
      return
    }

    const result = Promise.all(entry.map(handler => {
      console.log('handler', handler);
      
      handler.call(_this, payload)
    }));

    return new Promise((resolve, reject) => {
      result.then((res) => {
        resolve(res);
      })
    })
  }
}

class ModuleCollection {
  constructor(options) {
    this.register(options);
  }

  register(rawState) {
    // 注册根对象
    this.root = new Module(rawState);
  }
}

class Module {
  constructor(rawModule) {
    this.state = typeof rawModule.state === 'function' ? rawModule.state() : rawModule.state;
  }
}

function resetStoreState(store, state) {
  // 设置数据响应式,这一步最重要
  store._state = reactive({
    data: state,
  });

  store.getters = {};
  
  for (const gettersKey in store._wrappedGetters) {
    console.log(store._wrappedGetters[gettersKey]);
    Object.defineProperty(store.getters, gettersKey, {
      get() {
        const data = computed(() => store._wrappedGetters[gettersKey]());
        return data.value;
      },
      enumerable: true,
    })
  }
}

function registerGetters(store, type, getters) {
  // 赋值到_wrappedGetters
  store._wrappedGetters[type] = () => {
    return getters(store.state); // 相当于使用时state的传值,例count(state) { return state.count }
  }
}

function registerMutation(store, type, mutation) {
  // 赋值到_mutations
  const entry = store._mutations[type] || (store._mutations[type] = []);
  entry.push(function (payload) {
    mutation.call(store, store.state, payload);
  });
}

function registeAction(store, type, action) {
  // 赋值到_actions
  const entry = store._actions[type] || (store._actions[type] = []);
  entry.push(function (payload) {
    action.call(store, {
      state: store.state,
      // commit: (type, payload) => store.commit.call(store, type, payload),
      commit: store.commit,
      // dispatch: (type, payload) => store.dispatch.call(store, type, payload),
      dispatch: store.dispatch,
    }, payload);
  });
}
相关推荐
kyriewen6 小时前
Webpack vs Vite:一个是“老黄牛”,一个是“猎豹”,你选谁?
前端·webpack·vite
打小就很皮...6 小时前
html2canvas + jsPDF 生成 PDF 的踩坑与解决方案总结
前端·pdf
全栈前端老曹7 小时前
【前端地图】多地图平台适配方案——高德、百度、腾讯、Google Maps SDK 差异对比、封装统一地图接口
前端·javascript·百度·dubbo·wgs84·gcj-02·bd09
雾岛听风6917 小时前
JavaScript基础语法速查手册
开发语言·前端·javascript
遇见~未来7 小时前
第三篇_现代布局_从弹性到网格
前端·css3
前端那点事7 小时前
Vue前端SEO优化全攻略(实操落地版,新手也能上手)
前端·vue.js
Dxy12393102167 小时前
HTML 如何使用 SVG 画曲线
前端·算法·html
用户2367829801687 小时前
从零实现 GIF 制作工具:LZW 压缩与 Median Cut 色彩量化
前端·javascript
hahaha 1hhh8 小时前
中文乱码 ubuntu autodl
linux·运维·前端
Codebee8 小时前
Harness Engineering:AICode 的灵魂
前端·人工智能·前端框架