β

2017,我们从Node.js的版本号大飞跃谈起

程序师 1 阅读

本文作者: i5ting

版本帝?

Chrome浏览器已经蹦到57版本了,是名副其实的版本帝,作为兄弟的Node.js也一样,1.0之前等了6年,而从1.0到8.0,只用了2年时间,这世界到底怎么了?

我们就数一下

整体来说趋于稳定

已无性能优势?

Node.js在2009年横空出世,可以说是纯异步获得高性能的功劳。所有语言几乎没有能够和它相比的,比如Java、PHP、Ruby都被啪啪的打脸。但是山一程,水一程,福祸相依,因为性能太出众,导致很多语言、编程模型上有更多探索,比如go语言产生、php里的swolo和vm改进等,大家似乎都以不支持异步为耻辱。后来的故事大家都知道了,性能都提到非常高,c10问题已经没人再考虑,只是大家实现早晚而产生的性能差距而已。

编程语言的性能趋于一样的极限,所以剩下的选择,只有喜好

那么在这种情况下,Node.js还有优势么?

误读:Node.js已无性能优势,它现在最强大的是基于npm的生态

上面是成本上的比较,其实大家把关注点都转移到基于npm的生态上,截止2017年2月,在npm上有超过45万个模块,秒杀无数。npm是所有的开源的包管理里最强大的,我们说更了不起的Node.js,其实npm居功甚伟,后面会有独立的章节进行阐述。

来自www.modulecounts.com的各个包管理模块梳理的比较

npm生态是Node的优势不假,可是说“Node.js没有性能优势”真的对么?这其实就是误读,Node.js的性能依然很好呀,而且它有npm极其强大的生态,可谓性能与生态双剑合璧,你说你死不死?

异步和回调地狱?

天生异步,败也异步,成也异步

正因为异步导致了api设计方式只能采用error-first风格的回调,于是大家硬生生的把callback写成了callback hell。于是各种黑粉就冒出来,无非是一些浅尝辄止之辈。但也正因为回调地狱是最差实践,所以大家才不得不求变,于是thunk、promise等纷沓而至。虽然Promise/A+不完美,但对于解决回调地狱是足够的了。而且随着ES6等规范实现,引入generator、co等,让异步越来越近于同步。当async函数落地的时候,Node已经站在了同C#、Python一样的高度上,大家还有什么理由黑呢?

名称 说明
callback Node.js API天生就是这样的
thunk 参数的求值策略
promise 最开始是Promise/A+规范,随后成为ES6标准
generator ES6种的生成器,用于计算,但tj想用做流程控制
co generator用起来非常麻烦,故而tj写了co这个generator生成器,用法更简单
async函数 原本计划进入es7规范,结果差一点,但好在v8实现了,所以node 7就可以使用,无须等es7规范落地

有时,将一件事儿做到极致,也许能有另一种天地

应用场景

MEAN是一个Javascript平台的现代Web开发框架总称,它是MongoDB + Express +AngularJS + NodeJS 四个框架的第一个字母组合。它与传统LAMP一样是一种全套开发工具的简称。在2014和2015年喜欢讲这个,并且还有MEAN.js等框架,但今天已经过时,Node.js有了更多的应用场景。

《Node.js in action》一书里说,Node所针对的应用程序有一个专门的简称:DIRT。它表示数据密集型实时(data-intensive real-time)程序。因为Node自身在I/O上非常轻量,它善于将数据从一个管道混排或代理到另一个管道上,这能在处理大量请求时持有很多开放的连接,并且只占用一小部分内存。它的设计目标是保证响应能力,跟浏览器一样。

这话不假,但在今天来看,DIRT还是范围小了。其实DIRT本质上说的I/O处理的都算,但随着大前端的发展,Node.js已经不再只是I/O处理相关,而是更加的“Node”!

这里给出Node.js的若干使用场景

可以说目前大家能够看到的、用到的软件都有Node.js身影,当下最流行的软件写法也大都是基于Node.js的,比如PC客户端luin/medis采用electron打包,写法采用React+Redux。我自己一直的实践的【Node全栈】,也正是基于这种趋势而形成的。在未来,Node.js的应用场景会更加的广泛。更多参见sindresorhus/awesome-nodejs。

Web框架

演进时间线大致如下:

我们可以根据框架的特性进行分类

框架名称 特性 点评
Express 简单、实用,路由中间件等五脏俱全 最著名的Web框架
Derby.js && Meteor 同构 前后端都放到一起,模糊了开发便捷,看上去更简单,实际上上对开发来说要求更高
Sails、Total 面向其他语言,Ruby、PHP等 借鉴业界优秀实现,也是Node.js成熟的一个标志
MEAN.js 面向架构 类似于脚手架,又期望同构,结果只是蹭了热点
Hapi和Restfy 面向Api && 微服务 移动互联网时代Api的作用被放大,故而独立分类。尤其是对于微服务开发更是利器
ThinkJS 面向新特性 借鉴ThinkPHP,并慢慢走出自己的一条路,对于Async函数等新特性支持,无出其右
Koa 专注于异步流程改进 下一代Web框架

对于框架选型

个人学习求新,企业架构求稳,无非喜好与场景而已

我猜大家能够想到的场景,大约如下

如果只是做这些,和Java、PHP等就没啥区别了。如果再冠上更了不起的Node.js,就有点名不符实了。所以这里我稍加整理,看看和大家想的是否一样

技术栈演进

自从ES 2015(俗称ES 6)在Node.js 落地之后,整个Node.js开发都发生了翻天覆地的变化。自从0.10开始,Node.js就逐渐的加入了ES 6特性,比如0.12就可以使用generator,才导致寻求异步流程控制的tj写出了co这个著名的模块,继而诞生了Koa框架。但是在4.0之前,一直都是要通过flag才能开启generator支持,故而Koa 1.0迟迟未发布,在Node 4.0发布才发布的Koa 1.0。

2015年,成熟的传统,而2016年,变革开始

核心变更:es语法支持

对比一下变革前后的技术栈选型,希望读者能够从中感受到其中的变化

分类 2015年 2016年 选型原因
Web框架 express 4.x koa 1.0 && 2.0 (koa2.0刚发布不久,喜欢折腾的可以考虑) 主要在流程控制上的便利,异步毕竟要时刻注意,心累
数据库 mongoose(mongodb) mongoose(mongodb) 对mongodb和mysql支持都一样,不过是mongodb更简单,足以应付绝大部分场景
异步流程控制 bluebird(Promise/A+实现) bluebird(Promise/A+实现)1) Koa 1.0 使用co + generator 2) Koa 2.0 使用async函数 流程控制演进路线,从promise到async函数,无论如何,promise都是基石,必要掌握的
模板引擎(视图层) ejs && jade jade && nunjucks 给出了2种,一种可读性好,另一种简洁高效,都是非常好的
测试 mocha ava mocha是Node.js里著名的测试框架,但对新特性的支持没有ava那么好,而ava基于babel安装也要大上好多
调试 node-inspector VSCode 在Node 6和7出来之后,node-inspector支持的不是那么好,相反VSCode可视化,简单,文件多时也不卡,特别好用

预处理器

前端预处理可分3种

这些都离不开Node.js的支持,对于前端工程师来说,使用Node.js来实现这些是最方便不过的。

名称 实现 描述
模板引擎 artmustacheejshbsjade … 上百种之多,自定义默认,编译成html,继而完成更多操作
css预处理器 lesssassscsseworkpostcss 自定义语法规则,编译成css
js友好语言 coffeescript、typescript 自定义语法规则、编译成js

跨平台

跨平台指的是PC端、移动端、Web/H5

平台 实现 点评
Web/H5 纯前端 不必解释
PC客户端 nw.js和electron 尤其是atom和vscode编辑器最为著名,像钉钉PC端,微信客户端、微信小程序IDE等都是这样的,通过web技术来打包成PC客户端
移动端 cordova(旧称PhoneGap),基于cordova的ionicframework 这种采用h5开发,打包成ipa或apk的应用,称为Hybrid开发(混搭),通过webview实现所谓的跨平台,应用的还是非常广泛的

构建工具

说起构建工具,大概会想到make、ant、rake、gradle等,其实Node.js里有更多实现

名称 介绍 点评
jake 基于coffeescript的大概都熟悉这个,和make、rake类似 经典传统
grunt dsl风格的早期著名框架 配置非常麻烦
gulp 流式构建,不会产生中间文件,利用Stream机制,处理大文件和内存有优势,配置简单,只有懂点js就能搞定 grunt的替代品
webpack + npm scripts 说是构建工具有点过,但二者组合勉强算吧,loader和plugin机制还是非常强大的 流行而已

构建工具都不会特别复杂,所以Node.js世界里有非常多的实现,还有人写过node版本的make呢,玩的很嗨

HTTP Proxy

1)请求代理

对于http请求复杂定制的时候,你是需要让Node.js来帮你的,比如为了兼容一个历史遗留需求,在访问某个CSS的时候必须提供HEADER才可以,如果放到静态server或cdn上是做不到的。

2)SSR && PWA

SSR是服务器端渲染,PWA是渐进式Web应用,都是今年最火的技术。如果大家用过,一定对Node.js不陌生。比如React、Vuejs都是Node.js实现的ssr。至于pwa的service-worker也是Node.js实现的。那么为啥不用其他语言实现呢?不是其他语言不能实现,而是使用Node.js简单、方便、学习成本低,轻松获得高性能,如果用其他语言,我至少还得装环境

3)Api Proxy

产品需要应变,后端不好变,一变就要设计到数据库、存储等,可能引发事故。而在前端相对更容易,前端只负责组装服务,而非真正对数据库进行变动,所以只要服务api粒度合适,在前端来处理是更好的。

Api的问题

所以,在前端渲染之余,加一层Api Proxy是非常必要的。淘宝早起曾公开过一张架构图,在今天看来,依然不过时

这里的Model Proxy其实就是我们所说的Api Proxy,这张图里只是说了结果,把聚合的服务转成模型,继而为HTTP服务提供Api。

下面我们再深化一下Api Proxy的概念

这里的Node Proxy做了2件事儿,Api和渲染辅助。

所以Api后面还有一个服务组装,在微服务架构流行的今天,这种服务组装放到Node Proxy里的好处尤其明显。既可以提高前端开发效率,又可以让后端更加专注于服务开发。甚至如果前端团队足够大,可以在前端建一个Api小组,专门做服务集成的事儿。

说完了Proxy,我们再看看利益问题。Node.js向后端延伸,必然会触动后端开发的利益。那么Proxy层的事儿,前后端矛盾的交界处,后端不想变,前端又求变,那么长此以往,Api接口会变得越来越恶心。后端是愿意把Api的事儿叫前端的,对后端来说,只要你不动我的数据库和服务就可以。

但是Node.js能不能做这部分呢?答案是能的 ,这个是和Java、PHP类似的,一般是和数据库连接到一起,处理带有业务逻辑的。目前国内大部分都是以Java、PHP等为主,所以要想吃到这部分并不容易。

国内这部分一直没有做的很好,所以Node.js在大公司还没有很好的被应用,安全问题、生态问题、历史遗留问题等,还有很多人对Node.js的误解

这些对于提供Api服务来说已经足够了。

其他

用途 说明 前景
爬虫 抢了不少Python的份额,整体来说简单,实用 看涨
命令行工具 写工具、提高效率,node+npm真是无出其右 看涨
微服务与RPC Node做纯后端不好做,但在新项目和微服务架构下,必有一席之地 看涨
微信公众号开发 已经火了2年多了,尤其是付费阅读领域,还会继续火下去,gitchat就是使用Node.js做的,而且还在招人 看涨
反向代理 Node.js可以作为nginx这样的反向代理,虽然线上我们很少这样做,但它确确实实可以这样做。比如node-http-proxy和anyproxy等,其实使用Node.js做这种请求转发是非常简单的 看涨

更好的写法

Async函数与Promise

我整理了一张图,更直观一些。

结论:Promise是必须会的,那你为什么不顺势而为呢?

推荐:使用Async函数 + Promise组合,如下图所示。

实践

合理的结合Promise和Async函数是可以非常高效的,但也要因场景而异

那么,在常见的Web应用里,我们总结的实践是,dao层使用Promise比较好,而service层,使用Async/Await更好。

dao层使用Promise:

这种用promisefyAll基本几行代码就够了,一般单一模型的操作,不会特别复杂,应变的需求基本不大。

而service层一般是多个Model组合操作,多模型操作就可以拆分成多个小的操作,然后使用Await来组合,看起来会更加清晰,另外对需求应变也是非常容易的。

ES.next

Node.js + ES.next = ♥

Flow && TypeScript

Type Systems Will Make You a Better JavaScript Developer

ES6模块

现在ES6自带了模块标准, 也是JS第一次支持module(之前的CommonJS、AMD、CMD都不算), 但目前的所有Node.js版本都没有支持,目前只能用用Traceur、BabelJS, 或者TypeScript把ES6代码转化为兼容ES5版本的js代码,ES6模块新特性非常吸引人,下面简要说明。

ES6 模块的目标是创建一个同时兼容CommonJS和AMD的格式,语法更加紧凑,通过编译时加载,使得编译时就能确定模块的依赖关系,效率要比 CommonJS 模块的加载方式高。而对于异步加载和配置模块加载方面,则借鉴AMD规范,其效率、灵活程度都远远好于CommonJS写法。

ES6 模块标准只有2部分,它的用法更简单,你根本不需要关注实现细节:

多模块管理器:Lerna

A tool for managing JavaScript projects with multiple packages.

https://lernajs.io/

在设计框架的时候,经常做的事儿是进行模块拆分,继而提供插件或集成机制,这样是非常好的做法。但问题也随之而来,当你的模块模块非常多时,你该如何管理你的模块呢?

法1虽然看起来干净,但模块多时,依赖安装,不同版本兼容等,会导致模块间依赖混乱,出现非常多的重复依赖,极其容易造成版本问题。这时法2就显得更加有效,对于测试,代码管理,发布等,都可以做到更好的支持。

Lerna就是基于这种初衷而产生的专门用于管理Node.js多模块的工具,当然,前提是你有很多模块需要管理。

你可以通过npm全局模块来安装Lerna,官方推荐直接使用Lerna 2.x版本

更好的NPM替代品:Yarn

Yarn是开源JavaScript包管理器,由于npm在扩展内部使用时遇到了大小、性能和安全等问题,Facebook携手来自Exponent、Google和Tilde的工程师,在大型JavaScript框架上打造和测试了Yarn,以便其尽可能适用于多人开发。Yarn承诺比各大流行npm包的安装更可靠,且速度更快。根据你所选的工作包的不同,Yarn可以将安装时间从数分钟减少至几秒钟。Yarn还兼容npm注册表,但包安装方法有所区别。其使用了lockfiles和一个决定性安装算法,能够为参与一个项目的所有用户维持相同的节点模块(node_modules)目录结构,有助于减少难以追踪的bug和在多台机器上复制。

Yarn还致力于让安装更快速可靠,支持缓存下载的每一个包和并行操作,允许在没有互联网连接的情况下安装(如果此前有安装过的话)。此外,Yarn承诺同时兼容npm和Bower工作流,让你限制安装模块的授权许可。

2016年10月份, Yarn在横空出世不到一周的时间里,github上的star数已经过万,可以看出大厂及社区的活跃度,以及解决问题的诚意,大概无出其右了!

替换的原因

与hack npm限制的做法相反,Facebook编写了Yarn

Yarn, which promises to even give developers that don’t work at Facebook’s scale a major performance boost, still uses the npm registry and is essentially a drop-in replacement for the npm client.

很多人说和ruby的gem机制类似,都生成lockfile。确实是一个很不错的改进,在速度上有很大改进,配置cnpm等国内源来用,还是相当爽的。

友好语言

总结

坦诚的力量是无穷的

Node.js是为异步而生的,它自己把复杂的事儿做了(高并发,低延时),交给用户的只是有点难用的Callback写法。也正是坦诚的将异步回调暴露出来,才有更好的流程控制方面的演进。也正是这些演进,让Node.js从DIRT(数据敏感实时应用)扩展到更多的应用场景,今天的Node.js已经不只是能写后端的JavaScript,已经涵盖了所有涉及到开发的各个方面,而Node全栈更是热门种的热门。

直面问题才能有更好的解决方式,Node.js你值得拥有!

作者:程序师
用程序师的眼光看世界
原文地址:2017,我们从Node.js的版本号大飞跃谈起, 感谢原作者分享。

发表评论