Redux助力美团点评前端进阶之路

内容来源:2017年4月8日,美团点评前端技术专家张强在“第二届中国前端开发者大会”进行《Redux的打开方式》演讲分享。IT大咖说(ID:itdakashuo)作为独家视频合作方,经主办方和讲者审阅授权发布。

阅读字数:3312 | 5分钟阅读

嘉宾演讲视频回放及PPT,请点击链接:http://t.cn/RddcGFj

摘要

都说Redux好,但Redux到底好在哪,它真的解决了业务中遇到的问题吗?

因为在业务中引入Redux而带来的额外成本是否让你苦恼过?

会不会是我们打开Redux的方式不对?

本次分享将主要介绍美团点评以React+Nodejs全栈开发为背景,对redux的模块化尝试与思考。

简明前端史

我对于Web前端历史的划分会站在数据以及代码可维护性的角度,把前端历史划分为古典时代、中世纪和文艺复兴三个阶段。

作为前端工程师,怎样把数据传递给用户,并把用户的想法意图转化为数据,这是我们要解决的本质问题。

人脑是无法直接读懂数据的,于是我们有了UI,UI成为了人与数据之间的桥梁。UI捕获用户的输入,然后UI按照数据源的接口对数据源进行变更操作。数据源根据变更后的最新数据按照UI能理解的格式进行渲染并传输到UI,最后UI用人们能理解的格式展现数据。

这就是我们早在90年代就开始使用的最传统的BS架构。

对于当时的Web应用来说,数据源只有一个,就是远程GDB Server。所有对于数据的变更操作都是用URL来区分不同的请求。所有被试图更新的操作都靠刷新完整页面来进行。浏览器维护的history通过记录UI变化来维护不同状态的切换,最典型的使用场景就是浏览器提供的前进后退按钮。

总结

我们当时的Web框架非常可靠,不容易出错,即使出了错也很容易定位。浏览器帮我们搞定了大部分事情,开发者只需关心UI渲染html+css。这种架构的设计简单明了,学习成本低。

古典时代:AJXJ来了(2005)

它对于前端开发来说是最好的时代,但是对于用户来说这却是最坏的时代。

AJAX带来的变化

以前只有单一数据源,现在又多了很多本地的临时数据。

以前只通过URL进行数据变更,现在增加了AJAX异步请求,而且同时用户输入会使得UI对本地存放的临时数据进行修改。

曾经是后端统一直出html,现在变成前端直接通过DOM操作进行局部渲染。

曾经浏览器的前进后退功能都无效了,数据状态只能靠自己管理。

总结

应用中有多个数据源,维护多个数据源之间的一致性将变得非常困难。

因为多个数据源之间是有关联的,导致应用内会有多处代码来操作同一处数据,预测一个代码带来的数据变更愈发困难。

整个应用内的任何代码都能随便修改DOM,当出现问题的时候不知道谁修改了DOM。

状态管理更是无从谈起。

从此前端代码变得复杂又混乱。BUG越来越多,程序员需要加班修BUG。页面经过多次迭代,代码无法维护,程序员又得加班重构。

中世纪:React(2003)

因此我将2005年之后的这段时期定义为“中世纪”,无尽的BUG,无尽的黑暗。

到2013年出现了转机,Facebook开源了React。

React强势把应用拆分成组件树,每个组件内的数据由state和props构成。Props由父组件传进来,state则是内部维护的一个本地状态。state和props的任何变化都会触发组件的重新渲染。

裸用React

每个组件都有自己本地的state,而React间组件的通信非常繁琐。所以要依靠React组件之间的通信去同步多个state之间的数据将变得非常痛苦。

React没有对数据变更进行约束。

在UI渲染方面React做得很好,没有DOM操作,与真实DOM隔离。为我们省去了很多关于渲染性能优化的工作。

React+Redux(2015)

Redux诞生于2015年,诞生不久就被官方输入了React的豪华全家桶之内。

Redux要搭配React使用首先就要摈弃React组件内部的state。这时React就将回归纯渲染,意味着传给最顶层的父组件一个Props数据,整个组件树构成的view就渲染出来了。

因此我们可以把React组件树抽象为一个函数。

这是一个纯函数,意味着输入一个确定的参数Props,它就会输出一个确定的view。只要输入的Props不变,那么输出的view就一定不会改变。

React+Redux数据流

React和Redux结合使用有一点需要注意的是,Redux启用了一个中间件的机制,中间件可以拦截全局触发的action,并根据自己拦截的action按需进行修改或再次触发其它action。

这个中间件的设计非常强大,使得Redux的扩展性得到很大的提升。

Redux三大原则

单一数据源。

state只读,只能通过触发action来进行更改。

action通过reducer来修改state,reducer必须为纯函数。

时间旅行

我们只要拿到最初始的state和用户会话中触发的所有action,我们就能一一还原出本次会话的所有空间状态。

又因为reducer渲染成view本身也是一个纯函数,我们就能通过state还原当前用户会话的所有UI变化。

Redux官方称这种变化为时间旅行。

总结

React+Redux的架构只有一个数据源,就是React的全局state。所有变更都统一由action触发,页面的渲染则统一由React组件树来完成。“时间旅行”的特性使状态管理变得非常容易。

文艺复兴

我把2013年至今的这段时间定义为“文艺复兴”时代,前端代码重新变得清晰有序,化繁为简。

但是Redux看似简单,用起来却不容易。

大象关冰箱with Redux

用Redux来解决“把大象关进冰箱要几步”这个经典哲学问题。

为了区分action,首先要定义三个不同的action type:“打开冰箱门”、“把大象关进去”、“关上冰箱门”。

然后需要实现三个actionCreator,去创建并触发这三个不同的action。

最后还要实现三个reducer去处理这三个action。

当我第一次看到Redux文档的时候我好像突然顿悟了,但当我第一次写Redux应用的时候,我的内心是崩溃的。

Redux在处理异步这方面也是有问题的。

它并没有明确规定异步处理应该放在哪一层来做,这导致每个开发都有自己的理解。

因此在一个Redux项目里,AJAX请求满天飞,写出来的代码简直没法看。

Redux的模块化

任何大型应用都无法避免多人协同开发,而协同开发一定离不开模块化。然而Redux官方并没有提及模块化方面的实践思路。

每个模块都拥有自己的action/actionType/reducer。

我们必须要保证不同模块的actionType避免重名。

模块之间要具备通信功能。

解决模块动态加载破坏了reducer纯净的问题。

Redux的API

Redux一共对外暴露了10个API,其中有5个与Redux的扩展性相关。这说明Redux需要被扩展和加强。

综上所述,Redux只提供了核心的状态管理器,并为此实现了尽可能简化的API。缺乏约束的设计使得Redux社区出现了N种最佳实践,这对于社区来说是好事,但对于普通开发者来说则未必。所以我觉得Redux不适合直接用于生产环境。

因此,我觉得我们需要一款框架对Redux进行封装和约束。

duxjs

duxjs是一个可用于生产环境的、基于React+Redux的前端框架。duxjs的部分思想借鉴了ducks,它的部分API设计则借鉴了choo。

duxjs特性

声明式API,没有样板代码。

模块化/组件化,可嵌套,可动态加载。

统一的异步处理。

duxjs同时也支持同构、热替换以及插件功能。

组件是duxjs中对于业务进行封装的最小容器。组件内可以定义自己的state、actions,selectors、views、subscriptions和components。组件内可以定义子组件,说明组件之间可以嵌套,最终形成一个组件树。

duxjs把actions分为同步action和异步action。

同步action严格按照Flux的action标准进行约束,整个action实体都可被序列化。

每个异步action都有一个effect,异步操作都会写在effect里面。除了effect还有子action,子action必须是同步的action。现在每个异步action分别默认附带了三个子action。也可以扩展更多自己的子action。Effect的异步任务执行过程中可以触发任何其它action。

State

定义组件内的初始化state。

Selector

对外暴露的state数据接口。

View

集成与组件相关的view。

Subscriptions

订阅来自外部系统的消息,比如websocket、全局键盘事件以及jsbridge通知。

Module

在组件之外我们还有一个模块的概念,就是module。duxjs的组件可以形成组件树,模块就是这个组件树的容器。和组件一样,模块也能定义在组件中成为子模块。

模块和组件的区别就在于,同一个模块内,同一个module组件是耦合的。同一模块内不同组件定义的所有action与module 和selector都共享空间,而模块与模块之间是完全解耦的。

在component中定义子模块,这里我们支持模块的静态加载和动态加载两种方式。

子模块如果向父模块通信,首先父模块在定义子模块的时候,还需要定义好想监听的函数。父模块对子模块的特点action进行监听,当监听被触发时可以就可以做一些想做的事件。

父模块如果向子模块通信,父模块可以直接获取子模块的action进行subscription,父模块可以直接访问selector进行取值。父模块也能拿到子模块的view进行渲染。

Module间的解耦

不同模块在全局state中的空间完全隔离。

由父模块指定命名空间,确保模块内所有action、selector、view的全局唯一性,不用担心重名的问题。

只有父子module能通信,禁止隔代通信。

每个action都有自己唯一ID的值,以及action被触发时的源信息。

只要拿到用户单次操作所触发的所有action,就能用图表形式将这个action的调用栈画出来,这对我们理清业务逻辑和排查BUG是非常有帮助的。

Plugin

Plugin就是加强版的module。

除了模块具备的所有能力之外,还能劫持全局的同步/异步action。

监听全局的state变化。

捕获全局的异常。

有自己的view。

可以只使用duxjs的状态管理功能,所有关于view的实现都独立于duxjs之外。

混合模式可以部分使用duxjs的模块view。

全承载模式是完全使用duxjs应用内的数据和视图进行封装和管理。

duxjs现状

duxjs在美团点评中还处于内测阶段,我们会根据实际使用的情况去调整API设计。

内测完毕后将进行开源。

我今天的分享就到这里,感谢聆听!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,049评论 25 707
  • 本文将开始详细分析如何搭建一个React应用架构。 一. 前言 现在已经有很多脚手架工具,如create-reac...
    字节跳动技术团队阅读 4,263评论 1 23
  • 一、CMS管理系统功能 CMS是ContentManagementSystem的缩写,意为"内容管理系统"。 CM...
    默默先生Alec阅读 5,292评论 0 7
  • 今早起床,刚出门就闻到了一股熟悉的味道——下雨的味道。这种味道,让我一下子又回到了原来的地方,那个总是阴雨连绵的家...
    陌影笙阅读 286评论 0 2
  • 今天神明充当画家一名 给我画了一个大太阳 春风一不小心让柳絮跑了出来 它们满天满地——飞 弄痒了我的鼻子 我打个喷...
    北方辰宿阅读 250评论 4 8