一个人的 ClojureScript 技术栈

作者:题叶
链接:https://zhuanlan.zhihu.com/p/24425284
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

今天(昨天)分享完关于 ClojureScript 的话题, 算是如实重负. 我嗓子不好, 之前分享过 React, 但相比在网上总是差很多, 这次分享也是紧张, 会场的 Keynote 没有按照预想放在挺高的地方, 也没有下一页的预览, 看着大屏幕衔接没做好. 但是说真的心里还是忐忑的, 提问的同学很犀利, 几个都在点子上, 关于 Redux 还有深度传递的属性, 以及全局 state 问题, 其实我并没有想清楚, 我有自己的方案, 但并没有足够说服别人的方案.

实际上我的角色是转述国外社区的方案, 然后加上我自己的理解来强化对于技术的解释. 经过一层转述毕竟是不好的. 提问当中相关的问题, 其实 David Nolen 都提出过方案, 全局 state 和类似 GraphQL 的问题. 我没吃透倒是真的. 有机会还是建议去 Youtube 上刷一遍大会上关于 ClojureScript 的视频.

我有信心笃定函数式编程是对的, 首先国外已经有人实践了, 其次我自己使用下来感觉可靠. 主流前端不看 Clojure 大会的视频, 其实很少有渠道报道能够获知报道的. 我因为当初挖 React 和 Haskell 的原因专门去看了, 所以我知道. 显然国外也不是稀罕事. 但是在中文技术社区就很少这方面的声音, 更不用说闲着蛋疼去写 ClojureScript 代码了. 但我需要. 我之前推广 React 结果面对那么多期待之外的复杂度, 我亟需一个解释, 我需要找到自己的支点和方向. 维护和平, 防止自己世界观崩溃.

我的世界观确实蛮脆弱的, 跟很多从小镇进入大城市上学工作的人一样, 整个过程就是世界观瓦解和重建的过程. 到了编程也差不多, 从最开始 CSS, 到会写代码, 到发现编程范式, 到挖函数式编程的历史, 我花费了好大的力气避免自己崩溃. 我应该是不多的身为 JavaScript 程序员, 却长时间混迹 Clojure, Haskell, Elixir 圈子的人. 按照我的判断, 这三门语言未来会越来越重要, 至于会不会进语言榜前十, 我并不在乎, 影响力是方方面面的, 比如 Haskell 的研究看似漫无边际, 实际上已经影响到 C# Swift CoffeeScript 等等语言的设计了.

知乎上关于 D2 有同学提到我很孤单, 其实我一想想这这么觉得. 虽然未必是正确的方向, 但是我真的走了好远好远, 真的有孤单的感觉. 平时我遇到 Clojure 问题要去英文社区问 ClojureScript 的维护者们, 这还正常, 但是我用的 Stack Editor 全世界恐怕只有我一个人在用, 然后维护者说你的东西好奇怪, 跟我们平常的都不一样所以帮不了你... 就这样我被一门小众语言的开发者吐槽说我的东西很罕见... 除了 Stack Editor, Respo 的情况也类似, Respo 的功能覆盖不全面, 当然别人是不乐意用到生产环境的. 虽然有几个特性很新奇, 但结果还是我一个人用.

我还在 GitHub 上一个人建立团队维护的我的项目, 开几十个仓库, 结果就是我一个人龟速堆代码. Respo, Cumulo, Cirru 是其中最严重的几个. 当然介绍已经不少了. 我对自己的定位本来不是前端, 但这些年在后端和设计方向上我可以说进展微渺, 已经没了信心, 就算说年轻, 光是编程上的坑就能拖着我好久了. 看整个的历史, 好像也应该是这样的, 大家都看到了前面的曙光, 但是走在前面的人会先填了近的坑, 结果累了慢下来, 新人就赶超到前面去了. 我现在做一些乱七八糟的东西, 后面会有很多很多人做比我牛逼很多的事情, 而原本我期待出那种风头的是我.

Lisp 在国内缺乏声音, 有名气的大概田春的 Common Lisp, 王垠的 Scheme, 其他的很少在社区看到. Clojure 社区我们当然还有 Lo 姐, 但人家明明在英国过日子. 至于群里其他朋友, 前端圈应该很少听到了. 但是 Lisp 很重要, 宋鸿兵说过, 看一个国家的现在和将来, 就要看他的历史, 原话当然是记不准确了, 我想说对于编程而言, 同样有历史的因素. 面向对象半个世纪, 函数式编程半个世纪, 如果只是面对 JavaScript 这几年的新闻, 实在是窄了一点. JavaScript 也许是迄今发展最迅猛的一个生态, 真是蛮夸张的. 然而面对真实世界的问题, 最终还是需要寻求依靠, 编程理论和工程经验是知识的重要源头, 也就是半个多世纪计算机理论和工程方面的研究.

Clojure 好在哪? 我最在意的一点是 Clojure 当中的各种功能都是经过深思熟虑确定的, 我是指语言本身的核心部分, 语言坚持哪些特性, 哪些会做折中, 都做了相当的考虑. 当然这并不意味着 Clojure 没有问题, 但是对比静态类型语言编码的成本和严格, 对比脚本语言的散漫, Clojure 在其中维持了挺微妙的一个状态. 类似的语言当然也还有, 但是在前端使用领域来说 ClojureScript 是除了 JavaScript 之外生态相对完整的一门语言. TypeScript 由于是静态类型, 所以不拿来对比. 同时 Clojure 也在函数式编程和操作副作用之间努力寻找平衡, 既要借助函数式编程提升程序的可靠性, 又要考虑实际操作状态时如何处理.

我微博上发过, 我觉得 JavaScript 社区其实归根到底是大厂博弈的过程. 原本的大厂的竞争, 苹果有 Objective-C 和 Swift 等等, 微软有 .Net 平台大量的编程语言, 微软是开放的, 虽然苹果借助 iOS 再次反超. 但 Mozilla 和 Google 作为后来者在新的领域发起战争, 特别是 Chrome, 点燃了 JavaScript 引擎性能的竞赛, 以及一连串的 Web 平台的战略. Facebook 也想在 HTML5 上寻找突破, 虽然说是失败了, 但是以 React, Webpack, Babel 目前在社区的影响力, 简直是开辟了新战场. 用户当然是一个方面, 但是谁吸引和控制了开发者, 谁的话语权依然会很大. 而 TypeScript, Dart, Babel 对开发者的争夺, Angular, React 对开发者的争夺, 某种程度可以认为竞争不会消弭.

两相对比, 特殊之处, 之前苹果微软可以在语言和操作系统层次进行竞争, 而现在由于 JavaScript 的特殊性, 战场非常狭小, 简直要出现肉搏. ECMAScript 需要有统一标准, 但是不同的厂商会有不同的技术点的利益的诉求. D2 期间也听鬼道解释了一遍, React Native 长时间不接受他们某个特性增强的改进的代码, 可能由于涉及较深入的修改. 但公司利益是公司利益. 当 Google 或者微软强行在自家浏览器增加各种神奇功能时, 也是面临这样的尴尬. Facebook 把编译搞得如此复杂, 因为大家都觉得 JavaScript 是语言, 要按照标准做, 分开 stage 功能, 各种配置项. 然后社区当中也是各种风格相互冲击, 套路能不多么.

所以当然我看到 WebAssembly 前两次更新三家大厂一起更新博客的时候, 我简直眼镜都要掉下来了. 态度如此一致! 我估计他们是真的想好后招了. 像是 Clojure 那样, 一个公司控制好语言的核心, 按照特定的习惯设计好工具链, 花点时间打磨一下开发体验, 把 VS 的成功经验迁移过来, 难道不好么. WebAssembly 不仅仅提高性能, 同时作为汇编, 也让从 CoffeeScript 2009 开始发出的声音, 终于看到了一个清晰的出路, Web 就是需要一门新的汇编, 而不是单单 JavaScript. 之后大家可以各自建立起自家完善的工具链, 稳定的语法和语言特性, 类型推断类型检查, IDE 和各种生成工具, 提供平台 API 等等. 美好的未来.

当然我是心急了. 加上年初有三个月无业游民的时间, 除了总结下 Respo, Quamolit, Cumulo, Cirru 几个项目的思考, 我大把的时间都用在熟悉 ClojureScript 的思维方式上, 到入职我的 JavaScript 封装还是原地踏步的状态, 反正公司直接 ESlint. 于是我用 ClojureScript 搭建了整套我在 React Webpack 当初折腾的热替换开发环境除出来, 包含纯粹 ClojureScript 实现的 React 主题功能也就是 Respo, 还有编码方式从 Sublime Text 迁移到了 Cirru Light Editor, 到下半年把 Light Editor 升级成了 Stack Editor, 而这已经是别人完全不熟悉的一套工具链了.

文章标题讲的就是我的技术栈, 后面的篇幅就粗略介绍一下:

Respo

我用 ClojureScript 实现的 Virtual DOM 方案. 整体采用 pure render 所以对副作用也就是自定义组件等需求支持有欠缺, 但是在 Virtual DOM 操作和热替换方面舒服很多, DSL 略啰嗦但我觉得还算顺手. 性能上我做了不少优化, 总体会比 React 之类框架慢几倍, 大概应该在一个数量级上. 文档方面我整理了 API 和术语的解释, 也编写了纯 ClojureScript 的 examples, 可以在 GitHub 页面找到. 其他 UI 和 router 等常用组件因为用到, 也就依照我的 React 经验堆了一些出来, 当然体量是太小了.

Stack Editor

一个人的 ClojureScript 技术栈一个人的 ClojureScript 技术栈

其实就是 Cirru 的项目的 Clojure 形态, 在 DOM 上编辑 AST, 后端生成 Clojure 代码. 由于是图形界面的编辑器, 我可以通过前端代码实现简单的跳转到定义, 自动生成变量. Stack Editor 最重要的一个想法是, 侧边栏应该显示函数名或者变量, 而且是根据调用栈显示, 从而方便具体功能的开发. 从实际使用效果看确实比 Sublime Text 手写来得快. 我在微博上常发视频, 有兴趣可以看.

Stack Workflow

由于 Respo 和 Stack Editor 带来了模板代码, 所以我需要一个项目作为模板项目, 也就是实际意图了. 通过模板代码, 我的开发习惯实际上稳定了下来, 并且能增量得改进. 实际上在这个 workflow 当中项目源码是用 .ir 文件存储的, src 当中的 cljs 代码都是编译出来的. 我想这应该已经超乎很多人想象了.

Cumulo Workflow

Cumulo 项目关系到在分享里讲的服务端到前端的数据同步, 最粗暴的办法就是直接用 Diff 了, 当然我不是完全没优化的那种 Diff, 还是有基于纯函数和缓存的优化的. 这样我就把 Respo 的 Store 直接搬到了服务端编写. 当然这相对来说还是项目非常早期的一个状态, 性能远远不够. 但是搭配 ClojureScript 的服务端热替换功能, 开发还是蛮顺畅的, 之前我用 Webpack 服务端热替换做到了改修代码 WebSocket 不断开, 而在 ClojureScript 环境当中更简单.

除此之外的小工具还有一些, 但都不如上面几个重要了. 我知道对于别人来说这些项目会是奇葩, 但是对于我来说这是一个不错的开始.

上一篇:强大的矩阵奇异值分解(SVD)及其应用


下一篇:在R中运行Shell命令脚本(Call shell commands from R)