react、vue 将一个利用划分成不同的组件,一个组件的状态可能会影响另一个组件,随着我的项目日渐简单,组件间通信就是一个令人头疼的问题...

我:qio tto ma tte!我的项目日渐简单导致状态治理艰难?

  • 引起一个 model 变动的因素可能会有很多种:服务器响应、缓存数据、本地数据、ui 状态、用户事件...咱们怎么来解决这些状态?
  • 多个组件依赖同一个状态,各组件如何更新、同步状态?

那就是用 redux。

说到 redux,厉害了,她就是来解决这个问题的,所以不晓得的同学须要认真钻研钻研 redux 。

开始了开始了

为了跟风前端新技术,为了晋升集体竞争力,必须要去理解一些原理,做一个知其所以然的智慧码农,他日软妹背后炫技,硬核自信不懵逼。

我心目中的状态治理

其实处于前端社会底层的搬砖少年,并没有什么心目中的状态,我就觉着 redux 挺好的,我很观赏

先外表上逢迎:卧槽,这概念,这招式,卧槽,厉害了;实际上心里真心觉着厉害。

我要是本人能实现一个,我是不是一样牛叉?是的!

当初,假如我曾经实现了这个状态管理器,我该怎么来用,什么姿态用的爽,值得思考...

先创立个 module

const app = {  namespace: 'app',  state: {    num: 0  },  actions: {    change (state, value) {      this.setState({        num: value      })    }  }}

而后创立个 store

const store = createStore({  modules: [app]})

这个 store 就是咱们全局的状态,你能够把她类比为 redux 的 store,但请别说是抄的。

那么 store 还应该向外暴露出获取数据和批改数据的办法

store 应该蕴含的 api

const actions = store.mapActions({  change: 'app/change'})const states = store.mapStates({  num: 'app/num'})

到这里,所有都那么理所应当,用法简单明了,一度狐疑本人是个蠢才。

const app = {  namespace: 'app',  state: {    num: 0  },  actions: {    change (state, value) {      this.setState({        num: value      })    }  }}const { mapActions, mapStates } = createStore({  modules: [app]})const actions = store.mapActions({  change: 'app/change'})const states = store.mapStates({  num: 'app/num'})actions.change(1)states.num // 1

创立逻辑

忽然一下,如同没有了方向,面对本人意淫的假代码示例,再也不能应用 cc、cv 的挫败感油然而生,这种感觉就像是失恋了一样。

没方法,生存还要持续,依稀记得老师们说过:面对苦难,化繁为简,一一击破...

于是你列下了几个实现步骤:

  1. store 是一个类
  2. 须要装置 app 这个 modules,要留神 modules 可能会有多个
  3. module 通过命名空间来划分
  4. 实现 mapActions
  5. 实现 mapStates
  6. 喝杯水,微信问候一下企慕已久的女神,过粗劣的生存

store 的实现

class Store {  constructor (options) {    this._state = {}    this._modules = {}    this.setModules(options.modules)  }    setModules (modules) {    [].concat(modules).forEach(m => {      let ns = m.namespace      let _m = new Module(m, this)            this._module[ns] = _m      this._state[ns] = _m.state    })  }}function createStore (options) {  return new Store(options)}

你发现通过简略的组合能够简化 Store 的逻辑,所以你打算实现一个 Module 类以不便扩大

Module 类的实现

class Module {  constructor (m, _store) {    this._store = _store    this.state = m.state    this.actions = m.actions    this.namespace = m.namespace  }  setState (state) {    Object.assign(this.state, state)  }}

当初,store 的构造曾经能够整理出来了,然而办法( actions )、状态( state )还没方法裸露进来,所以当初须要来实现 mapActions、mapState。

dispatch 的必要性

想到之前 mapActions 的调用:

const actions = store.mapActions({  change: 'app/change'})

难道,只是把 Module 的对应 actions 找到返回去就行了么,这也太 low 了吧,而 redux 的中间件都是通过批改 dispath 来实现加强和拓展,这太厉害了,借鉴借鉴。

所以,mapActions 应该都是对 dispatch 的封装实现;

dispatch 的实现

class Store {  // ...  dispatch = (path, ...args) => {    let { ns, key } = normalizePath(path);    ns = this._module[ns]    if (!ns) { return }    let action = ns.actions[key]    return action.call(ns, ns.state,...args)  }}function normalizePath (path) {  const [ns, key] = path.split('/')  return { ns, key }}

mapActions、mapStates 的实现

mapActions 返回的所有办法应该都是对 dispatch 的封装,这样所有办法都走的是 dispatch,这样咱们当前增加中间件就极其不便。

class {  // ...  mapActions = map => {    let res = {}    forEachValue(map, (path, fkey) => {      let fn = (...args) => {        this.dispatch(path, ...args)      }            res[fkey] = fn    })    return res  }  mapStates = map => {    let res= {}        forEachValue(map, (path, fkey) => {      const { ns, key } = normalizePath(path);      const m = this._module[ns]      res[fkey] = m.state[key]    })    return res  }}function forEachValue (obj, fn) {  Object.keys(obj).forEach(key => fn(obj[key], key))}

棘手实现了 mapStates

到这,你看着清单上的最初一条邪魅一笑,心心念:技术扭转世界,扭转本人,感觉本人就是个人生赢家。

刚刚的平凡创举给予你十足的勇气,你开心的关上微信,纯熟的关上与女神的对话框,小心翼翼的发了一句:在吗?

整顿回顾

女神可能又在忙,你打算测试一下本人的代码:

const actions = mapActions({  change: 'app/change'})const states = mapStates({  num: 'app/num'})change(1)console.log(states.num) // 0console.log(store._store._state.app.state.num) // 1const add = val => actions.change(states.num + 1)const reudce = val => actions.change(states.mum - 1)

发现代码有问题:

  1. states 并不能响应式的批改
  2. mapActions 太局限;上述 actions 应反对如下优化
const actions = store.mapActions({  change: 'app/change',  add (dispatch) {    dispatch('app/change', states.val + 1)  },  reduce (dispatch) {    dispatch('app/change', states.val - 1)  }})

将 mapStates 返回的值代理到 module 的 state 上

class Store {  // ...  mapStates = map => {    let res= {}        forEachValue(map, (path, fkey) => {      const { ns, key } = normalizePath(path);      const m = this._module[ns]      if (!m) { return }            proxyGetter(res, fkey, m.state, key)    })    return res  }}function proxyGetter (target, key, source, sourceKey) {  sharedPropertyDefinition.get = function () {    return source[sourceKey]  }  Object.defineProperty(target, key, sharedPropertyDefinition)}

批改 mapActions 以反对拓展

class Store {  // ...  mapActions = map => {    let res = {}    forEachValue(map, (path, fkey) => {      let fn            if (typeof path === 'function') {        fn = (...args) => {          path(this.dispatch, ...args)        }      } else {        fn = (...args) => {          this.dispatch(path, ...args)        }      }            res[fkey] = fn    })    return res  }}

到这里,前端状态管理器性能曾经根本实现,你能够在

去这里 查看她的简略用法和源代码;
查看 undo, redo 的简略示例

她曾经能够满足本人的应用,然而她仍存在限度和有余:

  • dispath 对 setState 的不可预知

例如:

const app = {  namespace: 'app',  state: { num: 0 },  actions: {    async getNumer () {      // waiting...      this.setState({ num: 'xxx' })    }  }}

能够看到如果 action 是一个异步的办法,那么咱们就不晓得 setState 什么时候会被调用。

  • 代码中没有错误处理的逻辑
  • module 并不反对多级 module,像 vuex 那样;

不过你能够通过命名空间的形式本人来定义:

const app = { namespace: 'app' }const app = { namespace: 'app:user' }const app = { namespace: 'app:system' }

这样还能够使得数据更加扁平化不是么?

  • 在 module 的 action 中调用其余 action
const other = {  namespace: 'other',  actions: {    foo () {      this.dispatch('app/getNumber')    }  }}