浅谈前端状态管理

2019-06-01 12:52:37 +08:00
 liyuanqiu

浅谈前端状态管理

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

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

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

案例分析

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

简单粗暴的做法:

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的代码,有这些隐患:

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

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

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,前后端在各自领域对解藕的探索,最终殊途同归。

2665 次点击
所在节点    前端开发
0 条回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/569707

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX