4.2 需求变动与开发模型 Requirement change and development models
前端开发模式 发展简史
- HTML+CSS+JS+jQuery spaghetti: 面条式代码。一个需求,到处改动。根据页面变动的地方,找到其 id/selector,修改所有与其相关的 handler、样式、逻辑。这种组织,所有组件都依赖于 dom 元素的一个 id,也决定了 DOM API 的操作方式
- MV*(P or C):然后这时就开始分离关注点了。分离是怎么分离呢,就是人为划分出一个数据 model、展示 view 的概念,然后把其他所有的都划归为 controller 或 presentor。这是初步的 SOC,结果就是,数据和视图之间不能直接接触,事件发生后,由 controller 从 model 中拿数据,处理后结果反映到 model 中去,并通知 view 使用新的 model 更新页面,也即是 model 和 view 分别作为数据和展示的 single source of truth,所有控制(比如获取并处理 model、通知 view 等)类的工作全部揉到控制器中做。这其实只是上个阶段代码的重新组织,但也不是毫无进步。数据的这层解耦,使得一大部分数据的获取不再需要去 dom 中取;但本质上,只是把 jQuery way of working 中的5步划分了责任:
getData
给 model 做,callAPI
给 controller 做,并负责通知 view 做recalculateStyle
、renderDOM
和renderCSSOM
的操作。并未改变 VC 仍然需要通过全局的 html-selector 获取 HTML 元素进行所有后续操作的本质 - 声明式、双向绑定:我认为所有近现代的前端框架都是抓住了 需要全局 html-selector 这个痛点入手的,而解决方案就是声明式的双向绑定;实现上则比如有 observable(Knockout.js / Angular.js)、props(React)、框架自实现的 html-anchor 标签(
data-bind
(Knockout.js)、v-if
(Vue.js)、JSX(React)、ng-app
(Angular 1.x))、变化检测(脏检查、VirtualDOM)等。这个使你的handler
真正地能专注于业务处理、数据改变即可,数据你也不需获取,就在你自己控制下,反过来是框架来监测你的数据;数据改变后,你也不需要手动通知视图了,框架会帮你通知到特定的 HTML 元素上并做更新;更新视图时,你也不需要通过 jQuery 提供 html-selector 了,框架会用你所使用的 html-anchor 来追踪到你真正需要更新的 html view 上。可以说,这个阶段 通过声明式解决了全局的 html-selector 问题,通过双向绑定解决了对视图的手动通知,让开发者可以真正专注在业务开发上。简直伟大的设计,演变到这里,基本就是近代前端框架的能力所在了,代表作有 Knockout / Backbone / Angular 1 等。但是不是到这里就没有继续发展了呢?当然不是,否则「现代」框架提法哪里来 - Modularization: 模块化。模块化说起来是 JS 本身的缺乏,ES6 的
import/export
应该是完美解决方案,可惜目前还没有原生实现,不过能通过 babel 使用也很满足了。除此之外,很多框架提供了自己框架特定的解决方案,比如labjs
,angular
自带的module
机制等。严格来说,模块化是整个 JS 领域发展的潮流,是 JS 代码的组织方式,不特定于框架,因此它应该算是一波独立的浪潮,与前端应用的框架没有紧耦合的联系 - Component: React 为代表作,Angular 1.5 component 跟上,后来成为许多现代前端框架组织前端代码的默认方式。它比第3步讲的分层
jQuery 操作模式
bindEvents(#selector, handler) function handler(event) { const { currentState, params } = getNecessaryData(#selector) const { result, nextState } = callAPI(currentState, params) const style = recalculateStyle(nextState, previousState) renderDOM(#selector, result) renderCSSOM(#selector, style) }
可以看到这个模式的短板在哪里?
handlers
都是全局绑定!原生的 JS+jQuery 是没有模块的。再说,JS 应该无法提供对 HTML 的模块化;只能通过模块化方案来解决;- 在 handler 内部通过 HTML Selector 来进行元素定位,以进行 数据获取、数据写回 及 样式改变 等工作。这会带来几个问题:
- 无法测试,因为 selector 是内部使用,无法传入,无法 mock,是函数黑箱行为。只能真实运行应用,真实使用 jQuery 进行测试,失去了单元测试快速的优势。不快,难测,最终都只可能导向两个结局:为了测一个简单功能堆叠大量测试数据,以至测试无法维护没人敢碰没人想写;没有测试
- 无法查找引用点。任何 selector 的引用都是全局的,没有模块、没有组件的概念
- 副作用难以预料。只要有一个 selector,应用的任何部分都可以向页面元素发出更改;你的页面挂了,可能是来自其他很远地方页面或脚本执行造成的结果
- 关注点无法分离。
handler
需要自己处理数据搜集、API 调用、更新页面、更新样式等操作。实际上,真正业务发生的地方,只有 API 部分,更新页面和样式只不过是显示问题。在核心业务的 API 部分,它无法做到类似 redux reducer 这样纯净的 API:nextState = reducer(prevState, action)
现代前端框架 解决方案
现代的前端框架,先古如 Backbone / Knockout.js 者,后来者如 Angular / React / Vue.js 者,解决上面的哪些问题?使用了何理念和技术实现解决的?
写完前面的前端发展简史,我们应该就能发现,近现代框架进入前的 jQuery 时代有两个问题:无法分离的关注点,以及全局的 html-selector。前者主要是体现在,一个 event handler 需要处理多如 获取数据、业务逻辑、API 调用、更新视图、更新样式 等事务;后者主要是体现在一个 selector 是绑定到全局作用域下的,代码会污染全局环境。近现代框架提供的解决方案,主要即是解决了这两个问题。
无法分离的关注点一问,
在 MV* 和组件化的关系上,徐飞蜀黍讲,框架在做分层(就是我理解的 MVC、MVVM 等分层)的过程中其实推动了代码的组件化。
再提几个问题:解决后,现代的前端领域状态如何?又出来了哪些新的问题域?这些问题的可能解决方案又是什么?
复杂性管理
有 JS+ES6 是不够的,你需要组织文件,以反映需求变动的结构。我觉得「设计」的难点在于二:一是敏锐发现需求变动实际变的是哪个点,把它 SRP 出来;二是衡量设计好坏。
复杂性是不会凭空消失的,它只是从其中一处转移到另一处。对于设计评判,即看对于特定场景,该设计是否减少了复杂度。也即,复杂度是否正确地被转移到了框架内部,这取决于场景下的痛点是否客观存在,框架定位是否准确。
库和框架的区别?争论不休,有三点不错:
- 都是在解决开发过程的「重复」问题。框架解决「代码组织、架构」方面的重复,库解决其他的重复
- 库多为开发者主动调用,框架留下调用点,你写完由框架调用。这点区别基本是由上点确立的
- 另一点忘了
组件化、模块化的关系是什么?提前设计的好处?提前设计的度?如何设计?
- 不吹不黑聊聊前端框架:https://www.zhihu.com/lives/846356429794336768/messages
- 模块化:https://zhuanlan.zhihu.com/p/24575395
- 模块化:https://zhuanlan.zhihu.com/p/26231889
- 模块化(余果):https://yuguo.us/weblog/javascript-module-development-history/
- 模块化(颜海镜):http://yanhaijing.com/javascript/2015/03/28/js-module/
- 模块化(lifesigner):前端模块化开发那点历史 seajs/seajs#588
- 模块化时间线总结:https://juejin.im/entry/5905e9b2a0bb9f0065018bc7
- 数据流:https://segmentfault.com/a/1190000004292245
- https://www.meetyou.ca:8443/User/getArticleById.action?article.id=280
- https://zhuanlan.zhihu.com/p/24476917
- https://github.com/wxyyxc1992/Web-Development-And-Engineering-Practices
- https://github.com/kuitos/kuitos.github.io/issues
- 构建单页Web应用 xufei/blog#5
- 以下是组件化系列:
- Web应用组件化的权衡 xufei/blog#22
- https://leeluolee.github.io/fequan-netease/#/
- http://www.alloyteam.com/2015/11/we-will-be-componentized-web-long-text/
- https://zhuanlan.zhihu.com/p/26799645
- http://chenzhutian.org/blog/2016/%E6%B5%85%E8%B0%88VisMooc%E7%9A%84%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%8C%96/
- http://div.io/topic/1275
- http://www.ituring.com.cn/article/25069
库和框架的问题:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: 高阶的编程模型
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论