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

发布于 2020-11-30 13:28:08 字数 5836 浏览 1109 评论 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 订阅列表下的回调函数

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

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

0 文章
0 评论
84961 人气
更多

推荐作者

醉城メ夜风

文章 0 评论 0

远昼

文章 0 评论 0

平生欢

文章 0 评论 0

微凉

文章 0 评论 0

Honwey

文章 0 评论 0

qq_ikhFfg

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文