扎稳阵脚,再进一步。
注意:原文发表于2017-12-31,随着框架不断演进,部分内容可能已不适用。
给迫不及待的小伙伴们的快速入门:Sapper 文档 和快速模板 starter template。
如果要列出完美的 Node.js Web 应用框架的特征,你可能会想到这些:
它应该支持服务端渲染(SSR),能够快速初始化并加载内容,且对 SEO 友好;
应用支持同构为更通用的服务端和客户端的代码,这是当下合情合理的趋势预期;
客户端应用支持与服务端渲染的 HTML 混写,绑定事件侦听到现有的元素上,而非重新渲染它们;
导航到其他页面快如闪电;
离线及其他渐进式 Web 应用特性,务必支持开箱即用;
首个展示的页面应该按需仅加载必要的 JavaScript 和 CSS,这意味着框架支持路由级别的自动代码分割,及动态
import(...)
以实现更细粒度的手动控制;性能毫不妥协;
绝佳的开发体验,使用热模块重载及其他辅佐搭配工具;
生成的代码应该易于理解和维护;
框架应该是浅显易懂的,并且各个方面均可自定义,没有被 webpack 的配置锁死在某个框架中,以及少之又少会“暗藏玄机”;
一个小时内就可以将整个框架悉数习得,不论是久经沙场的老手,还是初入门径的新人。
Next.js 就十分接近这个理想值。
如果你与 Next 还素未谋面,我强烈推荐你去看 learnnextjs.com 上面的教程学学。
Next 提出了一个绝妙的想法:你应用中所有的页面都是 your-project/pages
目录下的文件,每个文件都是一个 React 组件。
这是 Next 所有突破性设计的根基。
找到某个页面的代码轻而易举,只需要在文件系统中查看即可,而不用去猜测组件的名称是做什么的。
同时,对于用什么样的项目文件结构这类鸡毛蒜皮争论不休的事情便一去不再了。
而 SSR(服务器端渲染)及代码分割的结合更是从容不迫(不过 React 团队放弃了 Router,对那些尝试 SSR 和代码分割的人说,“祝你好运!”)。
然而但是 —— 虽然 Next 这么美好,恕我出言不逊,Next 仍然美中不足:
-
Next 使用“rout masking”的技术来创建更为美观的 URL(譬如
/blog/hello-world
替换掉/post?slug=hello-world
)。这破坏了应用程序结构与目录结构一一对应的保证,并迫使你维护这两种形式之间转换的配置。
-
你所有的路由都假定是“页面”。
但通常你需要一些服务端路由,比如 301 重定向,或者专门为页面提供数据的 API endpoint,而 Next 没有兼顾到这一点(
注:Next 新版本已经具备 API 路由能力
)。你可以将处理这类情况的逻辑写到
server.js
中,不过这样会让人感觉与页面声明的方式不太一致。 要使用客户端路由,链接不能是标准的
<a>
标签。 相反你必须使用框架指定的<Link>
组件,譬如在这篇用 markdown 写的博客文章中的使用<Link>
是不可能的了。
然而,真正的问题是,所有这些好处都是要付出代价的。
最简单的 Next 应用(仅展示一个“hello world”静态文本的页面),其 JavaScript 使用 gzip 压缩后是 66kb,解压后,它占用 204kb,对于移动端来说这是一个非常庞大的代码量了,但这已是“最低消费”。此时,性能是决定你的用户是否会留下来的关键因素。
这事我们可以做得更好!
编译器即框架的逆袭
Svelte 提出了一个激进的想法:如果你的UI框架压根就不是框架,而是一个编译器呢?如果它把你的组件编译成独立的 JavaScript 模块会怎么样呢?
我们生成高度优化的普通的 JavaScript,而不是使用 React 或者 Vue 这些类库,其实它们对你的应用一无所知,因为框架必须提供一个通用的解决方案。
如果只生成应用所需的代码,便可以完全避免了基于虚拟 DOM 方案的内存和性能开销。
JavaScript 领域正在朝着这种趋势发展。
Stencil 是一个来自 Ionic 团队的深受 Svelte 启发的轻量级框架,它支持编译为 Web Components。
Glimmer 虽说并没有编译为独立的 JavaScript(不深入地讲了,要详尽叙述其优缺点的内容都可以独立成篇),但是团队正在围绕将模板编译为字节码做了一些有趣的研究。
(React 现在也开始加入了这个阵营,尽管他们目前研究的重点是优化 JSX 的代码,这与近年来 Angular、Reactive 和 Vue 所做的预优化其实是异曲同工。)
试想一下,如果我们破旧立新,尝试使用这种新的方式作为新的起点,其结果又将如何?
Sapper 简介
Sapper 或许可以回答这个问题(这个名字源于“工兵”,它也是“Svelte 应用程序制作器”的代名词)。
Sapper 是一个类似 Next.js 的框架,旨在满足前文提及的 11 个理想标准,同时极大减少发送到浏览器的代码量。
它可以与 Express 的中间件兼容,这意味着它很容易理解和定制。
同样的 “hello world” 使用 React 和 Next 需要 204kb,而使用 Sapper,仅有 7kb。
随着我们不断探索其空间优化的可能性,这个数字未来会进一步下降,比如,除了处理客户端路由的微型 Sapper 运行时以外,其余情况压根不会为没有交互的页面发送任何 JavaScript。
有没有一个更真实的示例看看呢?
其实 RealWorld 就提供了这么一个开发中型项目的示例。
展示这个 RealWorld 示例的交互式首页,Sapper 的实现 仅需39.6kb(压缩后 11.8kb)。
整个应用的尺寸是132.7kb(压缩后为 39.9kb),远远小于用来参照的 React/Redux 327kb(85.7kb)的实现,即便 132.7kb 仍然是挺大的,不过关键是,得益于代码分割,它会使人感观上觉得跑起来非常快速。
代码分割当下早已蔚然成风了,不过如果你的应用使用了 React 或者 Vue 等传统的框架,代码分割就有一个“最低消费” —— 框架本身,它可能占据整个应用程序总尺寸的很大一部分。
代码分割也不是免费的 —— 譬如 React 等框架使用了代码分割,它体积会更大。
不过如果使用的是 Svelte,情况就大相径庭。
当然,尺寸大小问题也不过是开发的关注点之一罢了。
Svelte 写的应用同时具有极高的性能和内存利用率,并且它包含了强大的功能,而如果你选择其他某个框架,搭配“小型”或者“简易”的 UI 库的话,那么可能会牺牲掉这些东西。
权衡
对于许多开发者来说,Sapper 最大缺点是什么呢?
“我还是喜欢 React,而且用惯用熟了。” —— 其实会这么觉得,也是无可厚非。
如果你属于上面这个阵营的话,那我建议你至少尝试一下其他框架,很可能你会喜出望外!
Sapper 的 RealWorld 实现,总共才 1,201 行代码,而其他框架需要 2,377 行,因为你可以使用非常简洁、易于表达的模板语法(只需要5分钟就可以上手)。
你还能享受到范围受限的局部CSS,内置了对未使用的样式自动删除和最小化能力,你也可以使用 Less 这样的预处理器。
这样你就不用再依赖 Babel 了。
而 SSR 也是非常地快速,因为它只是字符串拼接而已。
我们最近还加入了 svelte/store,这是一个小型的全局状态管理库,它可以零引入就能在组件层次结构之间同步状态。
就算是最糟糕的情况下,你也会觉得自己的选择是英明的!
尽管如此,我们还是有值得权衡取舍之处。
你比方说,某些人对“模板语言”深恶痛绝,难道你就是其中之一?
JSX 的支持者会说 “它其实就是 JavaScript” 这样的话来刺激刺激你,React 最大的优点就是它这种写法有无限的灵活性。
对于这种灵活性,React 有其自己的一套权衡逻辑,总之一言难尽,我们不在此展开过多讨论。
接着聊聊生态。
特别是 React 周边生态 —— 开发工具、编辑器集成、辅助类库、教程指南、StackOverflow问答数量、框架自身的天坑以及工作机会等等……这些 React 简直天下无敌。
诚然,如果你将“生态系统”视为选型的重要依据的话,说明你已局限在个人技术的天花板了,势必成为你技能上升的桎梏。
但它仍旧是当下普惠开发者最重要的举措。
路线图
我们还不会发布 v1.0.0 版本,因为还有很多变数在里面。不过相信指日可待了,想必届时会有许多令人兴奋的可能性。(注:事实上 v1.0 可能永远都不会发布,作者对技术有了一番新的思考)
我认为Web性能的下一个风口将会是 “全应用程序优化”。
目前 Svelte 的编译器是作用在组件级别,编译器通过理解组件之间的边界,能够生成更有效的代码。
React 团队的 Prepack 研究 也基于类似的想法,Glimmer 团队也在这一块做一些有趣的研究。
Svelte 和 Sapper 可以很好地利用这些想法。
说起 Glimmer,我们可能会在 2018 尝试借鉴他们“将组件编译为字节码”的想法。
试想一下,像 Sapper 这样的框架,可以根据应用的特性来决定使用哪种编译模式,它甚至可以为 JavaScript 的初始路由提供最快的启动时间,然后为后续路由提供延迟的字节码解释器,从而实现启动大小和应用程序总大小的最佳组合。
不过大多数情况下,我们希望 Sapper 的发展方向由用户决定。
如果你乐于助我们一臂之力,并希望协助我们打造 Web 应用的未来,欢迎加入 Github 和 Discord。
< The End >