最近比较正经地用现代前端技术开发了两个东西。都没写完,但是时间也不短了。说说感受吧,想到哪说到哪。

我同时进行两个极端:

一个极端是本博客:抗拒新潮前端技术,HTML、CSS、JS互相分离。从HTML来看本博客就是一个良好的文档。JS只是增加一点小功能,没有框架、也没有大量包依赖。(可以看看本页面的源代码)

另一个极端是我最近写的一些东西:没有启用JS什么都干不了,也不抗拒CSS-in-JS。依赖成千上万,各种框架携老带幼。

我觉得这两者都是合理的。Web现在有两个端点,一个端点是纯粹的文档,另一个端点是纯粹的应用程序。对于博客的情况,网站是一系列文档,所以用老旧的思路去组织是正确的。对于Web App来说就不需要想那么多了。只需要JS就够了。内容样式相互分离什么的,要分离在JS内分离各种模块吧。

Web App我选择的技术栈是React全家桶 + Typescript。选择React也没有多少原因,可能对函数式方法好感度比较高的缘故吧。选择Typescript虽然要多写一些类型标注,可是

  1. 能让我在Javascript这种很没有安全感的语言中获得安全感。
  2. 类型即文档。
  3. 类型本身就是细粒度的测试,可以省下很多测试却能让程序保持比较正确的行为。
  4. 而且上述文档和测试永远不会被忽视,out-of-date。
  5. 辅助IDE,WebStorm那么好用的。重构什么的可以得到很大帮助。

也考察过其它编译到Javascript的语言,或者编译到wasm的Rust。但是都没有采纳。各种周边设施TS是最完善的,JS除外。

随意吐槽一点,Rust作为本命语言用来写前端当然是最期待的。不过现在前端框架看上去还太丑了,用的人也不多。最近的版本稳定了过程宏以后更有希望了。希望过几年我能用Rust搞搞全栈。

ReasonML据说是对OCaml的改进。我不懂OCaml,但还是觉得ReasonML不太好。初步学习的时候就发现语法上的一些毛刺,比如说对浮点数运算要用奇怪的运算符。而且OCaml函数调用可以不用括号的,ReasonML却需要。

PureScript看着蛮难的。用的人也太少,特别是中文使用者和资料都没有。我不确定有什么坑,也许没有,但总觉得花太多时间学成了坑也凉了。

Elm忘了为什么没去用了。以前看过蛮多对Elm的吐槽,也许是这个原因吧。

好,让我们来专注Typescript。一开始对Typescript充满了很多期待,比如说期望很多很甜很甜的语法糖。实际上手才发现,除了类型部分,TS对JS几乎是秋毫无犯。而且还有柳下惠之嫌,禁止了JS里面很多操作。把TS代码删掉类型部分大致就是你编译出的JS代码了。

而Typescript的类型系统确实很强大,能处理得了JS这个银河系最动态的动态语言,显然是有几把刷子。TS对类型的操作确实有眼花缭乱的感觉,很多奇怪的JS行为也能用TS类型系统描述。类型上的三目运算符真的特别厉害(虽然我没实际用到过)。

用了Typescript以后再用JS社区的框架,出问题的概率就会更高了。我尽量去用那些流行的、和Typescript工作得好的框架。大部分就是React全家桶了,React / Redux / React Router / Immutable…

其中大部分工作得确实蛮不错。印象深刻的是用Material-UI的时候,这个UI库带的Typescript整合让代码中的CSS-in-JS也能获得良好的类型检查和补全,真的挺奇妙的。

但是Redux配上TS就会变得很恶心。本来模板代码已经很多了,还要写更多。其实Redux的那些事件,用一个Algebraic Data Type里的Sum Type(说人话就是Haskell的data,Rust的enum)就能很优雅地表达了,这些模板代码就是语言特性缺失导致的债务。

而Typescript自身也能用别扭的方法支持Algebraic Data Type的类型推导(足以看出本身的强大),但是因为没有语法糖,所以写起来更加别扭。

现在的语言版本(借助babel)让JS的表现能力突飞猛进。对象解包能让代码变得简洁优雅。但历史包袱还是处处都是,习惯了Rust的「面向表达式」写JS/TS也常有束手束脚的感觉,明显一个表达式嵌套就能搞定的事情却要写好多冗余代码。

React能看到这个框架水平很高。本身JSX/TSX写起来也蛮好的,新版本的新特性作为新手也很自然地接触到。搭配不可变数据结构也便于优化和写出正确的逻辑。它处处浸润了函数式的种种思想。文档里提到组合优于继承让我感慨继承这个概念到现在几乎是人人喊打,在游戏开发届也靠着ECS开始驱逐继承了。

最初用React边学边写一种应用,它的数据定义不怎么严谨,各种数据之间有跨越组件的奇妙交缠。写的时候感觉很难受,要不断去处理各种各样的特例,不可变数据结构在这时候有点拖后腿的感觉。

引进了Redux重写了以后稍微好转了一些。还给应用增加了一个很棒的日志系统(CoC人物卡)。但我对Redux的好感度不是很高。写起来太啰嗦了,不断提醒你JS是个垃圾语言。

或许这种情况基于Observer的MVVC框架比较适合,想用什么数据直接拿就行了。工作的地方在用Vue,因为目前Vue对Typescript支持有限以及别的原因,我总是兴致缺缺。

现在比较专注的笔记本项目我倒觉得挺适合React的。因为不熟悉也闹出了一些笑话,比如说我设计了一种在递归组件深处局部更新树的机制沾沾自喜,却忘了我用的是immutable数据结构,对局部的修改都会传导到全局的,所以是无用功。

因为是笔记本应用,所以碰上关于编辑器的各种问题,几次纠结最后还是用上了专门的编辑器组件库Draft.js(也是Facebook家的)。哪怕这样还是遇到了很多编辑器特有的问题。比如说焦点,处理得焦头烂额,体会到了编辑器哪怕不是富文本的也是大坑。后来快要绝望了,用浏览器的调试工具逆向了一下别人的代码翻出了一个很少见的方法,试了一下只能说差强人意。最后还是靠从IDE补全中翻出了一个比较少见的事件监听属性onSelect搭配上其他一些方法才比较完美地解决了困扰很久的焦点问题。

谈谈周边工具链,Parcel + Yarn都是不错的工具。Yarn人人爱就不说了。Parcel作为0配置工具真的做到了0配置,虽然肯定使得灵活性下降了,但是我觉得还是挺舒服的。甚至还内置了Rust整合。之前尝试把Rust编译成前端库,因为很多原因没能弄好。感觉Rust这方面还有很远的路,不过我觉得未来是非常美好的。

总体的感觉现在前端开发技术,虽然还是有很多坑,但是已经被前赴后继踩得很顺滑了。用来开发UI,有很成熟的方法也有很棒的表现能力。而且写一个Web就能自动被全平台所支持。也不怪那么火热了。