浅谈前端状态管理 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
liyuanqiu
V2EX    前端开发

浅谈前端状态管理

  •  
  •   liyuanqiu 2019-06-01 12:52:37 +08:00 3157 次点击
    这是一个创建于 2354 天前的主题,其中的信息可能已经有所发展或是发生改变。

    浅谈前端状态管理

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

    redux vuex mobx

    通常我们会选择 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的代码,有这些隐患:

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

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5411 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 32ms UTC 08:34 PVG 16:34 LAX 00:34 JFK 03:34
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86