如今,在创建一个Web应用的过程中,你需要做出许多架构方面的决策。当然,你会希望做的每一个决定都是正确的:你想要使用能够快速开发的技术,支持持续的迭代,最高的工作效率,迅速,健壮性强。你想要精益求精并且足够敏捷。你希望你选择的技术能够在短期和长期上都让你的项目取得成功。但这些技术都不是轻而易举就能选出来的。
我的经验告诉我,全栈式JavaScript符合了这所有的要求。可能你已经发现了些许端倪,又或许你已经在考虑它的实用性,并且在和朋友讨论争论它的话题。但是你是否亲自尝试过呢?在这篇文章中,我会对于全栈式JavaScript给出一个比较全面的介绍,为什么它会是正确的选择,它又是如何施展它的魔法的。
先给出一个概括预览:
接下来我会一项一项地介绍这些组件。但是在这之前,我们简短地回顾一下,我们是如何发展到现在的这个阶段的。
我为什么选择用JavaScript
从1998年开始,我就是一个Web开发者。当时,我们使用Perl进行大多数的服务器端的开发;但是从那时候开始,我们就在客户端使用JavaScript。Web服务器端的技术已经发生了翻天覆地的变化:我们被一波又一波的技术潮流推着往前走,PHP,ASP,JSP,.NET,Ruby,Python,这里只列出了几个例子。开发人员们开始意识到,在服务器端和客户端使用不同的语言使得事情变得复杂化。
在早期的PHP和ASP的时代,那个时候模板引擎还仅仅是个设想,开发人员们在HTML中嵌入他们的应用代码。我们经常可以看到下面这种脚本嵌入的写法:
1 2 3 4 5 6 7 8 9 |
<script> <?php if ($login == true){ ?> alert("Welcome"); <?php } ?> </script> |
或者更糟糕:
1 2 3 4 5 6 7 8 9 10 11 |
<script> var users_deleted = []; <?php $arr_ids = array(1,2,3,4); foreach($arr_ids as $value){ ?> users_deleted.push("<php>"); <?php } ?> </script> |
对于新手来说,很容易被不同语言之间的用法而混淆,犯下一些很典型的错误,比如for和foreach。更为不爽的是,以这样的方式来写代码,使得服务器端和客户端很难以非常和谐的方式处理相同的数据结构,即使是今天也是如此(当然除非你的开发团队有专职的前端和后端工程师 — 但即使他们之间能够共享信息,但仍然不能仅仅基于对方的代码进行合作)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?php $arr = array("apples", "bananas", "oranges", "strawberries"), $obj = array(); $i = 10; foreach($arr as $fruit){ $obj[$fruit] = $i; $i += 10; } echo json_encode(obj); ?> <script> $.ajax({ url:"/json.php", success: function(data){ var x; for(x in data){ alert("fruit:" + x + " points:" + data[x]); } } }); </script> |
最初,对于统一使用一种编程语言的尝试是使用后台的语言编写客户端的组件,然后编译成JavaScript。但这种方式并没有如期望的一样很好地工作,许多相关的项目都失败了(比如被ASP MVC取代了的ASP.NET Web forms, 又比如正在逐步被Polymer取代的GWT)。当然这些想法都是伟大的,从本质上讲,都是想在服务器端和客户端使用同一种语言,让我们可以重用一些组件和资源(注意这里的关键词:资源)。
最终得出的答案很简单:将JavaScript放到服务端
其实JavaScript诞生之初是在网景公司的企业及服务器的服务端,只是当时它还没有完全准备好。经过数年的磨炼和错失,最终Node.js出现了,它不仅将JavaScript放到了服务器端,同时也推广了非阻塞式编程(non-blocking programming)的思想,这种思想来自于nginx的世界。感谢Node的创始者们nginx的技术背景,并且继续(聪明地)保持了它的简单性,也感谢JavaScript天生的事件轮询机制。
(一句话概括,非阻塞式编程目的在于将消耗时间的任务放到一边,通过指定在这些任务结束时需要做的操作,这样可以在同一时刻让处理器去处理其他的请求。)
Node.js永久性地改变了我们处理I/O访问的方式。作为Web开发者,我们过去一直使用如下的方式访问数据库(I/O):
1 2 |
var resultset = db.query("SELECT * FROM 'table'"); drawTable(resultset); |
这里的第一行代码本质上已经阻塞了你的代码,因为你的代码停止下来等待数据库驱动返回一个结果集(resultset)。而与此同时,你的平台架构其实给你提供了并发的方法,通常是通过线程(threads)和派生(forks)。
在Node.js和非阻塞式编程的帮助下,我们可以更多的控制我们程序的执行流。现在(尽管在数据库I/O驱动器的背后可能已经有并行执行),你可以定义你的程序在I/O操作期间并行做的事情,以及在接收到结果集之后做的操作。
1 2 3 4 |
db.query("SELECT * FROM 'table'", function(resultset){ drawTable(resultset); }); doSomeThingElse(); |
上面的代码片段中,我们定义了两个程序流:第一个在我们发出数据库查询之后执行的操作,第二个是以回调的方式在我们接收到结果集之后做的操作。这是一个非常优雅并且强大的处理并发的方式。正如他们所说的,“一切都在并行执行——除了你的代码。(Evetything runs in paral方面的决策。当然,你会希望做的每一个决定都是正确的:你想要使用能够快速开发的技术,支持持续的迭代,最高的工作效率,迅速,健壮性强。你想要精益求精并且足够敏捷。你希望你选择的技术能够在短期和长期上都让你的项目取得成功。但这些技术都不是轻而易举就能选出来的。
我的经验告诉我,全栈式JavaScript符合了这所有的要求。可能你已经发现了些许端倪,又或许你已经在考虑它的实用性,并且在和朋友讨论争论它的话题。但是你是否亲自尝试过呢?在这篇文章中,我会对于全栈式JavaScript给出一个比较全面的介绍,为什么它会是正确的选择,它又是如何施展它的魔法的。
先给出一个概括预览:
接下来我会一项一项地介绍这些组件。但是在这之前,我们简短地回顾一下,我们是如何发展到现在的这个阶段的。
我为什么选择用JavaScript
从1998年开始,我就是一个Web开发者。当时,我们使用Perl进行大多数的服务器端的开发;但是从那时候开始,我们就在客户端使用JavaScript。Web服务器端的技术已经发生了翻天覆地的变化:我们被一波又一波的技术潮流推着往前走,PHP,ASP,JSP,.NET,Ruby,Python,这里只列出了几个例子。开发人员们开始意识到,在服务器端和客户端使用不同的语言使得事情变得复杂化。
在早期的PHP和ASP的时代,那个时候模板引擎还仅仅是个设想,开发人员们在HTML中嵌入他们的应用代码。我们经常可以看到下面这种脚本嵌入的写法:
1 2 3 4 5 6 7 8 9 |
<script> <?php if ($login == true){ ?> alert("Welcome"); <?php } ?> </script> |
或者更糟糕:
1 2 3 4 5 6 7 8 9 10 11 |
<script> var users_deleted = []; <?php $arr_ids = array(1,2,3,4); foreach($arr_ids as $value){ ?> users_deleted.push("<php>"); <?php } ?> </script> |
对于新手来说,很容易被不同语言之间的用法而混淆,犯下一些很典型的错误,比如for和foreach。更为不爽的是,以这样的方式来写代码,使得服务器端和客户端很难以非常和谐的方式处理相同的数据结构,即使是今天也是如此(当然除非你的开发团队有专职的前端和后端工程师 — 但即使他们之间能够共享信息,但仍然不能仅仅基于对方的代码进行合作)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?php $arr = array("apples", "bananas", "oranges", "strawberries"), $obj = array(); $i = 10; foreach($arr as $fruit){ $obj[$fruit] = $i; $i += 10; } echo json_encode(obj); ?> <script> $.ajax({ url:"/json.php", success: function(data){ var x; for(x in data){ alert("fruit:" + x + " points:" + data[x]); } } }); </script> |
最初,对于统一使用一种编程语言的尝试是使用后台的语言编写客户端的组件,然后编译成JavaScript。但这种方式并没有如期望的一样很好地工作,许多相关的项目都失败了(比如被ASP MVC取代了的ASP.NET Web forms, 又比如正在逐步被Polymer取代的GWT)。当然这些想法都是伟大的,从本质上讲,都是想在服务器端和客户端使用同一种语言,让我们可以重用一些组件和资源(注意这里的关键词:资源)。
最终得出的答案很简单:将JavaScript放到服务端
其实JavaScript诞生之初是在网景公司的企业及服务器的服务端,只是当时它还没有完全准备好。经过数年的磨炼和错失,最终Node.js出现了,它不仅将JavaScript放到了服务器端,同时也推广了非阻塞式编程(non-blocking programming)的思想,这种思想来自于nginx的世界。感谢Node的创始者们nginx的技术背景,并且继续(聪明地)保持了它的简单性,也感谢JavaScript天生的事件轮询机制。
(一句话概括,非阻塞式编程目的在于将消耗时间的任务放到一边,通过指定在这些任务结束时需要做的操作,这样可以在同一时刻让处理器去处理其他的请求。)
Node.js永久性地改变了我们处理I/O访问的方式。作为Web开发者,我们过去一直使用如下的方式访问数据库(I/O):
1 2 |
var resultset = db.query("SELECT * FROM 'table'"); drawTable(resultset); |
这里的第一行代码本质上已经阻塞了你的代码,因为你的代码停止下来等待数据库驱动返回一个结果集(resultset)。而与此同时,你的平台架构其实给你提供了并发的方法,通常是通过线程(threads)和派生(forks)。
在Node.js和非阻塞式编程的帮助下,我们可以更多的控制我们程序的执行流。现在(尽管在数据库I/O驱动器的背后可能已经有并行执行),你可以定义你的程序在I/O操作期间并行做的事情,以及在接收到结果集之后做的操作。
1 2 3 4 |
db.query("SELECT * FROM 'table'", function(resultset){ drawTable(resultset); }); doSomeThingElse(); |
上面的代码片段中,我们定义了两个程序流:第一个在我们发出数据库查询之后执行的操作,第二个是以回调的方式在我们接收到结果集之后做的操作。这是一个非常优雅并且强大的处理并发的方式。正如他们所说的,“一切都在并行执行——除了你的代码。(Evetything runs in paral044814-11">11