浅谈前端状态管理,第1张

浅谈前端状态管理,第2张

浅谈前端状态管理

前端开发者或多或少都听说过,或者使用过 状态管理器:

浅谈前端状态管理,redux,第3张 浅谈前端状态管理,vuex,第4张 浅谈前端状态管理,mobx,第5张

通常我们会选择 redux, vuex, mobx 这些著名的工具。

那么为什么要使用状态管理器呢?这篇文章,我来谈一谈这个形而上学的问题。

案例分析

有一个输入框,输入文本后,在输入框下方展示出所有包含已输入文本的名字。

浅谈前端状态管理,第6张

简单粗暴的做法:

codepen

const names = ["张零", "李一", "王二", "张三", "李四", "王五"];
const input = document.getElementById("input");
const list = document.getElementById("list");
function handleInputChange(e) {
  const { value } = e.target;
  list.innerHTML = "";
  if (value === "") {
    return;
  }
  names.filter(name => name.includes(value)).forEach(name => {
    const p = document.createElement("p");
    p.innerText = name;
    list.append(p);
  });
}
input.addEventListener("keyup", handleInputChange);

这段代码完美地实现了上述场景的需求。

现在我们这个项目要更加工程化一些,input 和 list 是两个程序员开发的,像下面这样:

// data.js
const names = ["张零", "李一", "王二", "张三", "李四", "王五"];
// input.js
const input = document.createElement("input");
// list.js
const list = document.createElement("list");
function handleInputChange(e) {
  const { value } = e.target;
  list.innerHTML = "";
  if (value === "") {
    return;
  }
  names.filter(name => name.includes(value)).forEach(name => {
    const p = document.createElement("p");
    p.innerText = name;
    list.append(p);
  });
}
input.addEventListener("keyup", handleInputChange);

我们看list.js的代码,有这些隐患:

  • input.js 的引用依赖,这会导致 list 必须晚于 input 加载,但其实这两个组件从意义上讲,并没有这层限制,只是因为程序中有引用才导致这种情况
  • list 直接关注了 input 的具体业务逻辑(按键),这是一个问题,每当增加一种修改 input 值的方式,list 就需要多注册一个回调来追踪这种改变。但细想起来,list 真正应该关注的是 input 的数据,而不是 input 有哪些事件可能会修改其数据
  • input.js 的迭代非常危险,需要时刻关注有哪些模块在依赖他的事件,对自身事件的增删改操作都会对整个应用伤筋动骨

因此,如果在这种开发模式下去开发一个前端应用,过不了很久,甚至在你的应用变得庞大之前,你已经做不下去了。

为了避免这种困境,我们来对这个小应用进行改造:

codepen

// data.js
const names = ["张零", "李一", "王二", "张三", "李四", "王五"];
// 共同的数据源 source.js
const source = (() => {
  let data = "";
  const listeners = [];
  return {
    subscribe(listener) {
      listeners.push(listener);
    },
    setData(newData) {
      data = newData;
      listeners.forEach(listener => listener());
    },
    getData() {
      return data;
    }
  };
})();
// input.js
const input = document.createElement("input");
function handleInputChange(e) {
  source.setData(e.target.value);
}
input.addEventListener("keyup", handleInputChange);
// list.js
const list = document.createElement("list");
source.subscribe(() => {
  list.innerHTML = "";
  const value = source.getData();
  if (value === "") {
    return;
  }
  names.filter(name => name.includes(value)).forEach(name => {
    const p = document.createElement("p");
    p.innerText = name;
    list.append(p);
  });
});

这样一来是不是变得逻辑非常清晰,input 与 list 互不关心对方的具体实现,从而可以各自实现高内聚的业务逻辑,且完全没有互相的依赖。

状态管理器

其实我们上面的这段代码,就是一个状态管理器的原型。

// 共同的数据源 source.js
const source = (() => {
  let data = "";
  const listeners = [];
  return {
    subscribe(listener) {
      listeners.push(listener);
    },
    setData(newData) {
      data = newData;
      listeners.forEach(listener => listener());
    },
    getData() {
      return data;
    }
  };
})();

它对状态进行了封装,并提供了一套管理状态的范式。各个模块在这个范式的约定下,对状态进行操作,从而分离模块之间的依赖。

结语

我们通过这个简单的状态管理器原型,成功分离了两个本就应该各自独立的前端模块,这也印证了状态管理器存在的价值。但是状态管理器所用的模式,并非前端领域独有,我们常用的各种 MessageQueue 系统,都是基于相同的模式在工作,也因为 MQ 的存在,大型分布式系统的各个模块间才得以各自独立,消除强依赖。所以这不是一个新的 Dogma,前后端在各自领域对解藕的探索,最终殊途同归。

----------------------- 以下是精选回复-----------------------

 

DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
网站模板库 » 浅谈前端状态管理

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情