Web UI组件化最佳实践的思考
前端组件化的几种思路
- 重客户端的SPA模式,将大量的逻辑和数据处理放在客户端,服务器端提供JSON数据
- 服务器端处理业务逻辑,客户端只负责两个事情:显示和发送数据到服务器端,服务器来调度UI的显示
- 混合模式,混合使用以上两种方案,交叉使用rjs + html + json
第一种方式现在比较流行,也有很多比较的框架支持,比如 React, angularjs 等。优点是,速度很快,交互体验比较好,UI容易组件化。缺点是 客户端非常重,需要编写大量的JS代码。对程序员的要求也很高,需要非常熟悉服务器端和客户端代码。
第二种方案是Rails推荐的方案。优点是开发速度非常快,但是用不好。因为Rails 太灵活,服务器端可以向客户端发送各种格式的数据:比如 HTML, JSON, JS。并且,这种方案 UI 特别不容易组件化。如果用官方传统的方案使用 render partial 方案会让view层代码膨胀,并且定制化也不好做,需要做大量的if else 判断。
第三种方案在一个无序的项目中经常出现。这样的后果是,项目代码膨胀,维护性很差,并且UI很难组件化,重用的可能性几乎为零。
我的实践
我以前非常喜欢的方案是重客户端的方案, Rails/Express
只负责提供数据和业务逻辑,不负责UI的生成和流程。因为这种方案有一个非常大的优势,就是它是一种非常标准的Clint - Server
开发模式。客户端的代码甚至可以直接打包成App client
(这是一种非常理想的状态)。在实践的过程中,我发现了有很多问题:首先,需要熟悉客户端的框架,需要较多的技术栈,学习曲线陡峭,不仅仅需要熟悉Rails,还需要熟悉JS的技术栈,比如webpack打包,react 等框架;第二,富客户端框架与Rails整合复杂;第三,Rails 的功能弱化了,无法使用Rails提供的大量方便的特性。
第二种方案是对Rails
这种的重服务器的框架来说,是一种可选的方案。但如果只使用Rails自带的View + Helper
方式很难实现UI组件化的问题。所以,我们需要新的解决方案,它应该满足如下几个条件:
1. UI可以重用:这是核心需求,只有UI组件可以重用,才能提升生产力,提高产品质量
2. UI的样式可以定制化:可以定制化才可以扩展项目的UI库,所以也很重要
3. 不同UI组件可以通信:不同组件之间的通信是一个非常常见的case,这是提升用户体验的关键
4. 开发速度一定要快:就是要使用方便
5. 与Rails整合比较好:这是后台服务器的技术决定的,不同的服务器端框架需要做对应的适配
一个简单的例子: Todo List
我打算用一个非常简单的例子: Todo List, 来实践上面定的方案。
如下图所示,我打算做一个简单的Todo list。它只有一个功能,在input框中输入任务名称,下面的任务列表会自动刷新。
分析:
1. 可以将页面分成几个部分:TodoContainer
和 Todo
2. TodoContainer
是组合容器,不负责具体的UI的显示
3. Todo
是普通的UI组件,负责具体的UI的显示,但不负责UI直接的交互
4. UI组件直接的交互通过Rails
完成
如下是我的架构设计图
从图中可以看出,UI组件直接的更新是通过服务器来触发的,不同组件不要在客户端通信。这样就降低了组件的复杂度,并且解耦合。
我实践了 Rails + RJS + cells 能很好的满足我的要求。代码在 https://github.com/chucai/cell_case 。 下面我简单的说明一下我的实践流程:
- 初始化项目
rails new cell_case
安装 guard , guard-livereload
主要是用在修改服务器端代码,可以自动刷新浏览器。这是web开发必备神器,特别是在有双屏的条件下,可以极大的提供我们的开发效率。安装 livereload 的 chrome 插件
3.1 安装地址
https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei
3.2 配置
访问http://localhost:3000
,然后在浏览器的右上角,点击livereload
的加载按钮。
这一步很关键,否则自动刷新将无法使用。
3.2 启动 guard
guard
安装
cells
Rails UI 组件化的一个库新建一个Model - Todo
rails g cell todo
- 创建首页
rails g controller dash index
- 创建第一个容器组件 - TodoContainerCell
rails g cell todo_container
- 使用容器组件组合其他组件
其中有几个值得注意的:我们在传递额外的数据给cell组件的时候,可以使用 Hash
的方式,这样数据会保存在Cell
对象的options
中。
总结
不足之处在于 cell
组件过于简单的,没有不同的角色,比如 Container
组件,功能太单一了,只是一个简单的View-Model
。如果要深度使用,还需要在cells
的基础中进行定制化。cells
的代码比较简单,可以在这个基础上做一些扩展。