自定义全局变量存储和订阅属性变化实现分析 - 文章教程

自定义全局变量存储和订阅属性变化实现分析

发布于 2020-11-30 字数 6358 浏览 916 评论 0

FluxRedux ,还有 VuxMobx 等,前端数据流和状态存储的控制库很多,也很强大,适用于复杂的项目中对数据状态的管理和组件之间的通信控制。另外加入响应式编程的 RxJS 的话,就变得更强大了……

本篇和上边的各种都无感,只是分析如何实现一个极简的全局变量存储和支持订阅状态属性变化。

实现

从 lodash 说起

lodash_.set(obj,key,value)_.get(obj,key) 这些方法。如下

_.set(obj,key,value)

var object = { 'a': [{ 'b': { 'c': 3 } }] };
 
_.set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c);
// => 4
 
_.set(object, ['x', '0', 'y', 'z'], 5);
console.log(object.x[0].y.z);
// => 5

_.get(obj,key)

var object = { 'a': [{ 'b': { 'c': 3 } }] };
 
_.get(object, 'a[0].b.c');
// => 3
 
_.get(object, ['a', '0', 'b', 'c']);
// => 3
 
_.get(object, 'a.b.c', 'default');
// => 'default'

下边自己实现模拟类似的 set/get 方法,定义名为 Store 的类 :

(1)Store.set 方法实现类似 _.set 效果

自定义全局变量存储和订阅属性变化实现分析

(2)Store.get 方法实现类似 _.get 效果

自定义全局变量存储和订阅属性变化实现分析

Observer 类

  • 定义 数据 data 管理全局数据
  • 定义 observers,管理全局的 Observer 实例
  • 定义 Observer 类
export const data = {}; // 管理全局数据
export const observers = {}; // 管理所有Observer实例

export const uuid = () => {
  return Math.random()
    .toString(16)
    .substr(2);
};

export class Observer {
  id: string;
  key: string;
  fn: Function;
  constructor(id: string, key: string, fn: Function) {
    this.id = id || uuid();
    this.key = key;
    this.fn = fn;
  }
 // 模拟解除监听
  unsubscribe() {
    delete observers[this.id];
  }
}

改进 Store 类

  • Store.setStore.get 函数的第一个参数 obj 都移除,变成内部直接使用 data;
  • 新增 subscribe 静态函数,实现订阅效果
import { Observer, uuid, observers, data } from "./observer";

class Store {
  // 模拟lodash(或underscore)的函数 _.set()
  static set(key, val) {

    if (!key) {
      return;
    }
    if (typeof key !== "string") {
      return;
    }
    let props = key.split(".");
    let value = data;
    for (let i = 0; i < props.length - 1; i++) {
      const prop = props[i];
      if (!value[prop]) {
        value[prop] = {};
      }
      value = data[prop];
    }
    value[props[props.length - 1]] = val;
  }

  // 模拟lodash(或underscore)的函数 _.get()
  static get(key) {

    if (!key) {
      return;
    }
    if (typeof key !== "string") {
      return;
    }

    const props = key.split(".");
    let value;
    for (let i = 0; i < props.length; i++) {
      const prop = props[i];
      if (i === 0 && !data[prop]) {
        return undefined;
      }
      if (!value) {
        value = data[prop];
      } else {
        value = value[prop];
      }
    }

    return value;
  }
  // 订阅
  static subscribe(key, fn) {
    const id = uuid();
    const ob = new Observer(id, key, fn);
    observers[id] = ob;
    const val = this.get(key);
    if (val !== null && val !== undefined) {
      fn(val);
    }
    return ob;
  }
}
// test set()
Store.set("sex", "male");
Store.set("name.first", "Nickbing");
Store.set("name.last", "Lao");
console.log(data);

// 输出:{ sex: 'male', name: { first: 'Nickbing', last: 'Lao' } }

// test get()
console.log(Store.get("sex")); // 输出:male
console.log(Store.get("name.last")); // 输出:Lao

// test subscribe()
Store.subscribe('name', (res) => {
  console.log(`subscribe的结果:${JSON.stringify(res)}`)
})

// 输出:subscribe的结果:{"first":"Nickbing","last":"Lao"}

到此,已实现的内容有:

  • 全局数据状态data 的管理(set\get)
  • 订阅属性 subscribe 和 订阅者 observers 的管理,通过 Store.subscribe 函数可以延迟获取属性值

属性订阅监听

订阅监听效果就是属性值发生变化,就会触发 subscribe 函数的调用,所以我们需要修改 Store.set 这个静态方法,使得值设置变化时,触发 subscribe 绑定的回调函数调用。

 // 模拟lodash(或underscore)的函数 _.set()
  static set(key, val) {

    if (!key) {
      return;
    }
    if (typeof key !== "string") {
      return;
    }
    let props = key.split(".");
    let value = data;
    for (let i = 0; i < props.length - 1; i++) {
      const prop = props[i];
      if (!value[prop]) {
        value[prop] = {};
      }
      value = data[prop];
    }
    value[props[props.length - 1]] = val;
    
   // 新增
    // 触发已有的订阅回调
    for (let id in observers) {
      const observer = observers[id];
      if (key.indexOf(observer.key) === 0 || key === observer.key) {
        if (observer.fn) {
          observer.fn(val);
        }
      }
    }
  }

新增 updated 函数,主动触发更新所有监听 key 的回调:

  static updated(key: string) {
    for (const id in observers) {
      if (key.indexOf(observers[id].key) === 0) {
        observers[id].fn(Store.get(observers[id].key));
      }
    }
  }

总结

通过上边步骤,依次实现了

  • 全局数据状态变量 data 控制只能通过 set 和 get 修改
  • 提供 subscribe 订阅属性,提供 unsubscribe 解除订阅
  • 修改 数据状态data,触发 subscribe 函数
  • 提供 统一触发指定 key 的 所有 subscribe 订阅列表下的回调函数

如果你对这篇文章有疑问,欢迎到本站 社区 发帖提问或使用手Q扫描下方二维码加群参与讨论,获取更多帮助。

扫码加入群聊

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

目前还没有任何评论,快来抢沙发吧!

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

2583 文章
29 评论
84935 人气
更多

推荐作者

Jay

文章 0 评论 0

guowei007

文章 0 评论 0

2668157715

文章 0 评论 0

HY阳

文章 0 评论 0

想挽留

文章 30 评论 3