Web平台能从Node.js学到什么

dwngde 9年前
 

Will Binns-Smith 是一位热爱JavaScript的全栈工程师,喜欢通过技术来解决实际问题。他开发了Bonobos.com的前端购物车功能。Will喜欢与设计师一对一工作,将PC网站转换为针对更小的触摸设备的站点。近日,Will撰写了一篇 文章 ,谈到了Node.js有哪些做法和特性值得Web平台学习。

作为一名Web开发者,我们会非常感激jQuery之类的库,因为他们消除了底层平台的各种不一致与笨拙的情况。曾几何时,构建一个 XMLHTTPRequest对象需要好几行代码,但现在只需一行$.ajax调用即可;通过jQuery与DOM交互很少需要我们针对特定的平台采取一些非常规手段,因为这一切jQuery都帮我们做好了。

使用过Python或是Java语言的开发者都知道这些语言自带了标准库。在浏览器端,jQuery就是这样的标准库,此外还有诸如 underscore之类的工具集。就像大多数标准库一样,我们所编写的代码都会严重依赖于他们。当越来越多的代码开始依赖这些APIs时,我们就很难在不破坏既有Web的情况下向前迈进了。不要妄图为了兼容性而包含进这些库的多个版本,他们常常会有30KB的大小,而且默认情况下会向全局的window 对象写入。

过去,我认为这是软件开发不可避免的一个问题。但现在一切都变了,至少对于我来说是这样的,因为Node.js生态圈出现了。

小模块与组合的出现

Node是一个构建在Chrome V8引擎之上的JavaScript运行时,它几乎没有多少标准库。相反,其他生态圈中的那些标准库都不在其核心平台中,而是通过npm获取的。

在npm中,小模块已经成为了标准,像是 substackSindre Sorhus 这样的用户分别已经发布了685和760个模块,这些模块都遵循着UNIX一次只做一件事,并将这件事做好的原则。像是array-union(返回两个数组的并集)与svg-create-element(提供了用于在DOM中创建SVG的优秀API)这样的模块都是非常小的,看起来应该与语言或是平台一同发布。

Sindre甚至还有一个名为negative-zero的模块,它只是判断一个值是否是-0,实现 只有一行代码 。看起来为这样简单的功能创建一个包有些极端,因为用户可以在自己的代码中实现这样的功能,不过通过这种方式,我们可以集中修改代码,而不必重复实现细节。Sindre对此有个很详尽的 介绍 ,感兴趣的读者可以看看。

甚至连非常流行的 Express Web框架 也只提供了Web应用的核心功能而已。与诸如Ruby on Rails或是Django这样的大型框架不同(本身带有模板、ORMs、csrf防护,以及其他特性),Express本身只提供了托管静态文件的中间件。相反,应用开发者可以自由使用他们喜欢的这些特性的实现,并将其组合起来创建应用。随着想法的不断改进,很多中间件包创建又消亡, 一些发展起来了,另一些则消失了 。这就是Node的哲学。

因此,小模块(以及由这些模块所构成的应用)会拥有非常庞大的 依赖图 (比如说,Bitbucket的前端包含了1000多个JavaScript模块,其中一些是内部模块,另外一些则来自于npm)。

这些模块最棒的一点就是他们并没有紧密绑定到平台上:他们不会受到标准库的影响,借助于 语义版本化 ,他们可以对其API进行迭代而不会对依赖他们的用户造成困扰。

流介绍

Node包含了流,它是异步流动数据的一个抽象,常常用于连接和转换I/O源。Node对流的初始实现(随Node 0.4发布)并不完善, 使用不当会导致数据丢失 。为了解决这一问题,Node 0.10对流进行了修正(也叫做Streams2)。不过,Streams2并不是对流模式的一个简单迭代,实际上在Node 0.10发布前经历了很多的变化。在发布时,它与运行在Node 0.8上的应用兼容。

这怎么可能呢?Streams2源自于 readable-stream 模块,一开始它就是Isaac Schlueter 在2012年7月发布的独立模块 ,这距离 2013年3月它随着Node 0.10的发布 已经相隔很长时间了。它经历了多次迭代,在这个过程中API与功能也不断成熟,Node社区发现它非常适合于作为流的实现。

时至今日, readable-stream的最新实现也是作为用户模块在npm中维护的 ,可用于Node 0.8及之前的版本中。很多用户都喜欢 使用用户模块而不是绑定的模块 ,这样可以实现生态圈的兼容性。

一系列不幸的APIs

与此相反,JavaScript与Web平台中现有的APIs都是很难改变的。对JavaScript语言的迭代变更(不能有任何的向后不兼容变化以防止现有系统出现问题)必须要通过添加新特性来实现。比如说,在发现Mutation Events API的性能问题后,人们引入了Mutation Observers来解决这个问题;废弃WebSQL,拥抱更加底层但使用起来却略显笨重的IndexedDB;逐步淘汰Application Cache,拥抱更加底层但更通用的ServiceWorker。Object.observe是ES2016的一个提案,通过它可以观测对象的属性,不过在React的单向数据流逐步流行起来并为主流所采用后,Object.observe提案则被 撤回 了。

对这些感到困惑么?我们不应该期待着一下子就将事情做对,不过我们需要一个平台,通过这个平台可以试错,然后逐步向好的方向迭代。

平台演化

可扩展的Web宣言 的支持者们希望Web能够像Node那样提供弹性的用户试错空间。其使命是让平台提供尽可能多的底层构建块,这样浏览器之外的库就可以自由尝试,从而避免正式的标准化流程所经历的代价高昂且冗长的过程。其中一种这样的底层原语就是 Web Components 的APIs,它向开发者提供了通过JavaScript来创建动态自定义元素与属性的能力,这一切都是通过封装来实现的。一个库无论大小都实现了对话框功能,不过API的迭代可以交给用户来完成,一开始无需放在平台内部实现。借助于底层原语,用户可以通过小模块的形式自由探索更高层次的抽象。换句话说,我们 不再需要AppCache 了。

幸好,我们已经看到了这种做法的好处。我们无需再陷入诸如AppCache这种实际使用很少的特性了。相反,我们有了一个 Promises A+ Specification 的标准化实现,npm中的 Q 已经证明了这一点,并且在年初已经成为了ES2015的一部分。WHATWG也在制订 流的规范 ,在很大程度上它受到了来源于Node的流的演化的影响。

就像平台的其他方面一样,这些新标准是很难改变的,不过幸运的是,其想法与APIs都是通过用户来实现的,这一切都要归功于Node!