单页面程序及组件化相关笔记

随着 Vue 文档的阅读,单页面程序、组件等概念出现地越来越频繁。但我始终感觉对这几个词缺少一种逻辑联系上的认知。直到看到 xufei 在 github 上的关于这个主题的一系列博文,始有醍醐灌顶之感。综合各方面资料,自己进行一个整理,帮助自己深入理解。

提高开发效率的两个主要途径:

  • 加快开发速度:少造轮子。
  • 减少变更代价:清理模块之间的关系,合理分层。

单页面程序

SPA(单页面程序,Single Page Application):在一个页面上集成多种功能,甚至整个系统就只有一个页面,所有的业务功能都是它的子模块,通过特定方式挂接到主页面上。

优越性

高效,体现在用户体验和运行效率上。把 AJAX 的无刷新机制发挥到极致。

实现前后端的天然分离。后端不再承担模板渲染、输出页面等工作,专注于提供 API,同时不必再针对每个端作差异化设计。

代码合并与加载策略

典型特征:部分加载。

一块界面可以是一个 HTML 片段,像请求数据一样用 AJAX 获取。如果有逻辑的 JavaScript 代码,也可以用 require 之类的异步加载机制去运行时加载。

因此,如果在页面间存在可复用的代码,一般提取成单独的文件,并按照每个页面的需求去进行合并。

需要考虑的点

路由与状态的管理

界面上的各种功能区块是动态生成的,需要对路由进行管理以减少用户的导航成本。具体做法就是把产品功能分为若干状态,每个状态映射到相应的路由,然后通过 pushState 这样的机制,动态解析路由,使之与功能界面匹配。

缓存与本地存储

动态加载的界面模板或 JavaScript 代码可以做一些自定义的缓存机制,在非首次的请求中直接取缓存版本,以加快加载速度。

在本地存储一些临时数据可以用 localStorage。

服务器通信

WebSocket 可以让服务端便利地使用反向推送,前端只响应确实产生业务数据的事件,减少无意义的 AJAX 轮询。

例如 socket.io 之类的库提供了在不同浏览器中的兼容方案,在不支持 WebSocket 的浏览器上会降级成使用 AJAX 或 JSONP 等方式,对业务代码完全透明、兼容。

困难与缺陷

SPA 遇到的困难:代码组织的复杂度大为提高,难以保证对一个数据的更新能够实时反馈到所有使用它的地方。

缺陷:不利于 SEO;要注意内存管理。

样式规划

基准样式的分离

包括浏览器样式的重设、全局字体的设置、布局的基本约定和响应式设计。

组件样式的划分

组件样式应当尽量减少相互依赖,各组件的样式允许冗余。

堆叠次序的管理

在单页应用中,需要提前为各种 UI 组件规划堆叠次序(z-index)。应预先分段,不同类型的组的 z-index 落到各自的区间,以避免相互冲突。

组件化

整个应用形成倒置的组件树,每个组件提供对外接口,然后内部只关注自己的实现。

职责更专一。可以做单元测试的覆盖以保证质量,再通过场景测试来保证整体流程。

  • HTML 的组件化:界面的片段化和模版化。界面片段在动态请求得到之后,借助模版引擎之类的技术,通过某种转换,放置到主界面相应的地方。
  • JavaScript 的组件化:目标是清晰的职责,松耦合,便于单元测试和重复利用。总的原则是先分层次,层内再作切分。
  • CSS 的组件化:传统 CSS 为扁平的文本结构,变更成本高。把实际使用的 CSS 当作输出结果,而另有一种适合变更的方式当作中间过程。探索结果包括预处理器、后处理器。

开发框架

jQuery 轻量、灵活,但对代码缺乏约束。

需要在代码极具膨胀的情况下控制每个模块的内聚性,并适当在模块间产生数据传递与共享。

因此需要做一些架构方面的提升 => 设计模式 => MVC | MVP | MVVM 框架,基本思路都是在 JS 层创建模块分层和通信机制。

AngularJS, Vue:MVVM。通过类似模板的语法,描述界面状态与数据的绑定关系,然后通过内部转换,把这个结构建立起来,当界面发生变化的时候,按照配置规则去更新相应的数据,然后再根据配置好的规则,从数据更新界面状态。

React:函数式。推崇单项数据流:给定原始界面(或数据),施加一个变化,就能推导出另外一个状态(界面或数据的更新)。

依赖注入:

  • 核心理念:通过配置来实例化所依赖的组件。
  • 缺点:性能以及跟踪调试的便利性上的损失。
  • 优点:松耦合,可替代性 => 可单独测试,随手引入。
  • 应用:对从事某一领域的企业,可将所有不常变动领域模型的业务代码都用此类办法维护。

MV*框架的基本原理

充当前端逻辑工具的 JavaScript 不能做入口,因此逻辑要先挂在配置文件(HTML)上,先由另外的容器(浏览器或者 Hybird 的壳)把配置文件加载起来,然后才能从某个入口开始执行逻辑。从这时起,框架启动:

  • 初始化自身(bootstrap)
  • 异步加载可能尚未引入的 JavaScript 代码(require)
  • 解析定义在 HTML 上的规则(template parser)
  • 实例化模型(scopr)
  • 创建模型和 DOM 的关联关系(binding, injection)

这些是主线流程,还有一些支线,比如:

  • 解析 url 的 search 字符串,恢复状态(route)
  • 加载 HTML 部件模板(template url)
  • 部件模板和模型的关联(binding)

业务模型

指所处领域中的业务数据、规则、流程的集合。即使抛开所有展示层,这一层也应当能够运作。

Redux、Vuex 之类的辅助方案帮助将业务模型的每一个扰动都收敛到确切的状态。

参考资料

博文参考资料

延伸阅读资料