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);
  });
}
相关推荐
sophie旭2 小时前
一道面试题,开始性能优化之旅(6)-- 异步任务和性能
前端·javascript·性能优化
年少不知有林皇错把梅罗当球王2 小时前
vue2、vue3中使用pb(Base64编码)
前端
FanetheDivine3 小时前
常见的AI对话场景和特殊情况
前端·react.js
sophie旭3 小时前
一道面试题,开始性能优化之旅(5)-- 浏览器和性能
前端·面试·性能优化
lypzcgf3 小时前
Coze源码分析-资源库-编辑知识库-前端源码-核心组件
前端·知识库·coze·coze源码分析·智能体平台·ai应用平台·agent平台
小墨宝3 小时前
web前端学习 langchain
前端·学习·langchain
北城以北88883 小时前
Vue--Vue基础(一)
前端·javascript·vue.js
IT_陈寒4 小时前
Python 3.12新特性实战:5个让你的代码提速30%的性能优化技巧
前端·人工智能·后端
sniper_fandc4 小时前
Vue Router路由
前端·javascript·vue.js