4

微博上写了几段, 想想太长, 还是写成文章发出来, 这事对我还挺重要的
原本是在反思自己为什么兴趣所在的前端项目居然蔓延得那么广
甚至导致技术不专一, 很多细节做不好. 其中的原因何在? 我认为前端太广
于是想到整体发展的趋势上, 于是认为背后还在更深层的事件
按这点进行推想, 也就是站在热替换这点往前眺望, 有种可能性
背后的想法已经挺长时间了, 临时想到整理成文章

文章章节拆分:

  • 热替换技术的现状

  • 后端热替换的能力

  • 热替换的要求和限制

  • 推想一种未来的开发方案

  • 需要着手填补的短板

  • 总结

热替换技术的现状

这是 Elm 和 Webpack 烧起来的很旺的一把火, 随着 React 烧得更远了
很快大家看到对于前端 React 应用, 或者 React Native, 都有了
这种情况下可以一边写代码, 并且预期在模拟器当中直接查看改变
如果代码有错, 可能做到直接把静态检查的错误直接提示出来
那么开发人员直接修复错误, 直到代码正确, 代码进行热替换

目前热替换, 前端方面有 Browserify, Amok 之类, 而 Webpack 支持前端后端
在 cljs 方面基于 boot-reload 能做前端, 基于 figwheel 做前端后端
Elm 能做前端, 只不过不像是前两者那么通用
而 Webpack 和 cljs 我在过去几个月做了几次尝试, 效果是可以的
我最多做到在阿里云跑开发环境, 远程编辑, 前端后端热替换, 直接加功能

前端热替换主要基于 MVC 分离的思路, View 与 Model 分离, 能任意调用
所有的界面数据, 要么作为 global Store, 要么抽象为组件 states
只要保证 store 和 states 能在替换代码过程中能保持, 就有整个界面
而 View 代码想要替换就替换掉好了, 替换完成重新运行就好了
方案主要基于 React 和相关有 virtual DOM 框架. 其他 Vue 也可以

另外 Time Travel Debugging 还有存储每个数据操作方便重演的机制
相关的资料较多, 应该已经都注意到吧

后端代码

比如 Webpack 也能做后端代码的打包和热替换
也就能做到在 WebSocket 不关闭而情况下直接替换代码
因此结合两点之后, 实际上已经能做前后端同时热替换代码
好处就是不需要整个重载程序, 重新连接 WebSocket, 更快更方便

我后端经验不多, 前面试验的代码是用前端 MVC 的思路写的
也就是抽象出内存中的一份 Database 作为我的 Model,
同时把客户端对应的 Store 作为 View, 而 View 是容易替换的
那么代码更新过程中, Model 不受影响, View 部分轻松替换
Webpack 替换的代码是局部的, 所以说 WebSocket 不受干扰

之前问过, 后端开发对热替换似乎兴趣不大, 毕竟本身就做了解耦
数据都在 DB 当中, 不受程序的重启而影响, 所以整个重启就好
所以说不受影响, 即便生成环境所有服务器进程进行重启, 也正常
我觉得这种方式毕竟, 各种 IO 都要重新创建, 有点开销, 而且有点慢
再者, 有 WebSocket 的话, 还是需要方案来保证 WebSocket 稳定

热替换的要求和限制

对于 React 来说, 热替换比较明显的问题在 states 处理上
由于 React states 是私有状态, 不像 Store 管理起来方便
不过这是个老问题, 我也明确了是有解决方案的, 比如设计全局组件状态
能解决就不多讲了

然后是热替换对程序状态的影响, React 中而错误代码会导致进程无法继续
原因就是代码错误, 导致 React 内部记录的状态出错, 因而程序故障
这也是已经有办法的, 主要是静态检查, 在程序执行之间做一次筛选
做得好的比如 Elm, 能分析到具体的类型, 最终能编译通过就很难报错
我没写大的 Elm 程序, 而是用 cljs, cljs 的变量检查已经比较实用了
基本能明确类型设计得更舒服, 在这一点上会明显改善

对抽象的影响, 这就涉及到编写程序时整个程序如何设计的问题了
React 带出的 pure render, stateless, 跟这个直接相关
为了程序能更好地热替换, 其实要求了更严格的对副作用的限制
比如渲染代码会在代码替换时被执行, 那么就不能随便插入数据操作在其中
也就是限制副作用, 而让无状态所以可替换的代码能直接更新掉
于是很多从前的开发习惯就需要转变思路

性能方面, 热替换本身似乎问题不大, 毕竟 Erlang 用了也还是快的
但是在 React 这样的方案观察下来, 由于用了函数式写法, 就可能有问题
函数式编程首先通过不可变数据和高阶函数形成了对内存的压力
而这在 virtual DOM 方案当中, 又是很难消除的因素
性能问题和明了, 也是大家共同的问题, 不多讲

一下子能想到的限制就这些, 估计还有不少琐碎的问题

推想一种未来的开发方案

在热替换基础上, 我尝试的开发方式已经和之前有不小的区别了
除了前端的热替换, 我还把后端联系到了一起, 让后端控制 store
所以我可以在改一段后端代码, 直接发送 diff 到前端, 界面直接更新
也就是说能做到动态修改 View 代码, 而网络连接, 浏览器页面状态都不受影响
Controller 代码也能更新, 但需要通过其他方式重新运行查看结果
Model 由于存储了程序的状态, 处理起来会更复杂, 需要记录每个操作
大体上是这样.. 相当于 React 的效果, 对多个客户端也生效

我想说, 还能走得更远. 我希望是, 能做到很多人同时一起开发
对, 多人同时开发同一个项目, 而不是每个人拷贝一份自己开发再 merge
好处应该想得差不多, 新人来了开发环境不用单独配, 省很多时间
一起做, 相互能熟悉工作内容, 相互也能监督, 降低交流成本
当一个功能有多人同时做, 开发速度也能提高, 相比一个人做不同的细节

想法很简单的, 但现在都没做起来, 显然难点有很多
首先, 即便在服务器上用 Vim 或者在线工具编辑, 多人操作也不方便
其次, 不同的人写的代码相互冲突了怎么办, 怎么开发下去?
一个人写完了一部分, 重启服务测试, 影响到另一个人正在测试怎么办?
虽然有其他问题, 但我认为影响最大是这些问题

首先多人编辑, 印象里有看到新闻有在尝试, 估计不成熟, 待会再回头说
但是代码冲突, 功能调试, 我想热替换已经给了不错的解决方案了
代码冲突首先可以依靠类型静态分析干掉很多, 做到代码有问题不去运行
热替换技术当中单个文件的类型分析很廉价, cljs 中也算成熟了
而功能测试, 由于不需要重启服务器, 相互直接也就少了很多的影响

另外有一点, 热替换要求程序严格控制副作用, 有另外的好处
在 React 而言, 这要求应用的结构, 组件的模块化, 做得更加严格
结果是大家写的 React 组件抽象出 state, 抽象出渲染方法, 其实类似
而这有利于编程思路编程规范相互之间少一点分歧, 读代码更容易

需要着手填补的短板

现在热替换直接上, 做多人协同编程, 有些难度, 我只是觉得可以一试
就 React 而言, 我认为对错误的容忍性太低, Flow 或者 TypeScript 应该更好
我的话是 cljs, 虽然不能避免所有错误, 但变量检查已经能干掉好大一部分数量
我觉得这些主要是加固吧, 为了让调试过程更加增量, 影响范围也更小
多说一句, 我认为需要引入 Haskell 系的强大类型系统来帮忙, 山寨一个也好
ADT 完整的实现确实门槛高, 但是其中实用的地方拿过来总可以吧...

编辑器的问题是大头, 首先就是前后端分离, 跑在服务器上, 浏览器端操作
然后, 每个修改需要是增量的, 这样方便同步到大量的客户端
并且, 操作合并应该只有极少甚至没有冲突, 否则实际操作会很繁琐
在就是 UI 上要做很多的改进, 讲多个人的状态显示出来
的以我目前在单页面的探索来看, 这个事情可能性是达成了的, 虽然难度不少
我可以基于 Cirru 先搞出这样的方案来, 先试验一下效果如何

另外类型检查, 也是个能改进的地方, 注意到吗, 类型检查都打 log
为什么类型检查不能直接在编辑器上提示, 然后给个选项一键纠正?
编辑器是基于文本的, 并不是基于图形界面的, 我估计这个事情麻烦了
如果 IDE 都是像 CSS 这样用树形编辑器操作, 我想也许就好多了
而这同时也是前面说的加强类型检查功能所遇到的问题...
想法还不成熟, 以后再想想

总结

基于上边这些讨论, 我认为热替换带来了不小的突破的机会
多人协同编程的某些障碍, 已经由热替换技术扫平了, 成了相对次要的问题
如果能让编辑器支持多人开发, 距离多人协同编程就很近了
我觉得可以在 Cirru 上继续探索一下...
现在都已经能直播写代码加在线热替换了, 再开发出编辑功能如何?


题叶
17.3k 声望2.6k 粉丝

Calcit 语言作者