在 2002 年的一本著做中,David Weinberger 将发展迅速的 Web 内容描述成一个 小块松散组合(Small Pieces Loosely Joined)。这个比喻让我印象深入,由于你们通常很容易认为 Web 是一个巨大的技术堆栈。实际上,您访问的每一个网站都是库、语言与 Web 框架的一种独特组合。css
LAMP 堆栈 是早期表现突出的开源 Web 技术集合之一:它使用 Linux® 做为操做系统,使用 Apache 做为 Web 服务器,使用 MySQL 做为数据库,并使用 Perl(或者 Python 和 PHP)做为生成基于 HTML Web 页面的编程语言。这些技术的出现并不是为了一块儿联合工做。它们是独立的项目,由多位雄心勃勃的软件工程师前赴后继地整合在一块儿。自那之后,咱们就见证了 Web 堆栈的大爆发。每一种现代编程语言彷佛都有一个(或两个)对应的 Web 框架,可将各类混杂的技术预先组装在一块儿,快速而又轻松地建立一个新的网站。html
MEAN 堆栈是 Web 社区中赢得大量关注和使人兴奋的一种新兴堆栈:MongoDB、Express、AngularJS 和 Node.js。MEAN 堆栈表明着一种彻底现代的 Web 开发方法:一种语言运行在应用程序的全部层次上,从客户端到服务器,再到持久层。本系列文章演示了一个 MEAN Web 开发项目的端到端开发状况,但这种开发并不只限于简单的语法。本文将经过做者的亲身实践向您深刻浅出地介绍了该堆栈的组件技术,包括安装与设置。参见 下载 部分,以便获取示例代码。node
实际上,你访问的每一个网站都是库、语言与 Web 框架的独特组合。git
MEAN 不只仅是一次首字母缩写的简单从新安排与技术升级。将基础平台从操做系统 (Linux) 转变为 JavaScript 运行时 (Node.js) 让操做系统变得独立:Node.js 在 Windows® 与 OS X 上的运行状况和在 Linux 上同样优秀。angularjs
Node.js 一样取代了 LAMP 堆栈中的 Apache。但 Node.js 远远不止是一种简单的 Web 服务器。事实上,用户不会将完成后的应用程序部署到单机的 Web 服务器上;相反,Web 服务器已经包含在应用程序中,并已在 MEAN 堆栈中自动安装。结果,部署过程获得了极大简化,由于所需的 Web 服务器版本已经与余下的运行时依赖关系一块儿获得了明肯定义。github
尽管本系列文章重点讲述的是 MEAN 太阳系中的四大行星,但也会介绍 MEAN 堆栈中的一些较小的(但并不是不重要的)卫星类技术:web
从传统数据库(如 MySQL)到 NoSQL,再到无架构的、以文档为导向的持久存储(如 MongoDB),这些表明着持久化策略发生了根本性的转变。用户花费在编写 SQL 上的时间将会减小,将会有更多的时间编写 JavaScript 中的映射/化简功能。用户还能省掉大量的转换逻辑,由于 MongoDB 能够在本地运行 JavaScript Object Notation (JSON)。所以,编写 RESTful Web 服务变得史无前例的容易。mongodb
但从 LAMP 到 MEAN 的最大转变在于从传统的服务器端页面生成变为客户端 单页面应用程序 (SPA)。借助 Express 仍然能够处理服务器端的路由与页面生成,但目前的重点在客户端视图上,而 AngularJS 能够实现这一点。这种变化并不只仅是将 模型-视图-控制器 (MVC) 工件从服务器转移到客户端。用户还要尝试从习惯的同步方式转而使用基本由事件驱动的、实质上为异步的方式。或许最重要的一点是,您将从以页面为中心的应用程序视图转到面向组件的视图。shell
MEAN 堆栈并不是以移动为中心,AngularJS 在桌面电脑、笔记本电脑、智能手机、平板电脑和甚至是智能电视上的运行效果都同样,但它不会把移动设备看成二等公民对待。并且测试过后再也不是问题:借助世界级的测试框架,好比 MochaJS、JasmineJS 和 KarmaJS,您能够为本身的 MEAN 应用程序编写深刻而又全面的测试套件。数据库
准备好得到 MEAN 了吗?
您须要安装 Node.js,以便在本系列中的示例应用程序上工做,若是还没有安装它,那就马上开始安装吧。
若是使用 UNIX® 风格的操做系统(Linux、Mac OS X 等),我推荐使用 Node Version Manager (NVM)。(不然,在 Node.js 主页上单击 Install,下载适合您操做系统的安装程序,而后接受默认选项便可。)借助 NVM,您能够轻松下载 Node.js,并从命令行切换各类版本。这能够帮助您从一个版本的 Node.js 无缝转移到下一版本,就像我从一个客户项目转到下一个客户项目同样。
NVM 安装完毕后,请输入命令 nvm ls-remote
查看哪些 Node.js 版本可用于安装,如清单 1 中所示。
$ nvm ls-remote v0.10.20 v0.10.21 v0.10.22 v0.10.23 v0.10.24 v0.10.25 v0.10.26 v0.10.27 v0.10.28
输入 nvm ls
命令能够显示本地已经安装的 Node.js 版本,以及目前正在使用中的版本。
在撰写本文之际,Node 网站推荐 v0.10.28 是最新的稳定版本。输入 nvm install v0.10.28
命令在本地安装它。
安装 Node.js 后(经过 NVM 或平台特定的安装程序都可),能够输入 node --version
命令来确认当前使用的版本:
$ node --version v0.10.28
Node.js 是一种 headless JavaScript 运行时。它与运行在 Google Chrome 内的 JavaScript 引擎(名叫 V8)是同样的,但使用 Node.js 能够从命令行(而非浏览器)运行 JavaScript。
熟悉本身所选浏览器中的开发人员工具。我将在整个系列中通篇使用 Google Chrome,但用户能够自行选择使用 Firefox、Safari 或者甚至是 Internet Explorer。
我曾有些学生嘲笑过从命令行运行 JavaScript 的主意:“若是没有要控制的 HTML,那 JavaScript 还有什么好处呢?" JavaScript 是在浏览器(Netscape Navigator 2.0)中来到这个世界的,所以那些反对者的短视和天真是能够原谅的。
事实上,JavaScript 编程语言并未针对 文档对象模型 (DOM) 操做或造成 Ajax 请求提供本地功能。该浏览器提供了 DOM API,能够方便用户使用 JavaScript 来完成这类工做,但在浏览器以外的地方,JavaScript 不具有这些功能。
下面给出了一个例子。在浏览器中打开一个 JavaScript 控制台(参见 访问浏览器的开发人员工具)。输入 navigator.appName
。得到响应后,请输入 navigator.appVersion
。获得的结果相似于图 1 中所示。
图 1. 在 Web 浏览器中使用 JavaScript navigator
对象
在图 1 中,Netscape
是对 navigator.appName
的响应,而对 navigator.appVersion
的响应则是经验丰富的 Web 开发人员已经熟知但爱恨不一的、神秘的开发人员代理字符串。在图 1 中(截自 OS X 上的 Chrome 浏览器),该字符串是 5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36
。
如今,咱们要建立一个名为 test.js 的文件。在文件中输入一样的命令,并将每一个命令包含在 console.log()
调用中:
console.log(navigator.appName); console.log(navigator.appVersion);
保存文件并输入 node test.js
来运行它,如清单 2 中所示。
navigator is not defined
错误$ node test.js /test.js:1 ion (exports, require, module, __filename, __dirname) { console.log(navigator. ^ ReferenceError: navigator is not defined at Object.<anonymous> (/test.js:1:75) at Module._compile (module.js:456:26) at Object.Module._extensions..js (module.js:474:10) at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12) at Function.Module.runMain (module.js:497:10) at startup (node.js:119:16) at node.js:902:3
正如您看到的那样,navigator
在浏览器中可用,但在 Node.js 中不可用。(很差意思,让您的第一个 Node.js 脚本失败了,但我想确保让您相信,在浏览器中运行 JavaScript 与在 Node.js 中运行它是不一样的。)
根据堆栈跟踪的状况,正确的 Module
没有获得加载。(Modules 是在浏览器中运行 JavaScript 与在 Node.js 中运行它之间的另外一主要区别。咱们将马上讲述 Modules 的更多相关内容。)为了从 Node.js 得到相似的信息,请将 test.js 的内容修改成:
console.log(process.versions) console.log(process.arch) console.log(process.platform)
再次输入 node test.js
,能够看到相似于清单 3 中的输出。
$ node test.js { http_parser: '1.0', node: '0.10.28', v8: '3.14.5.9', ares: '1.9.0-DEV', uv: '0.10.27', zlib: '1.2.3', modules: '11', openssl: '1.0.1g' } x64 darwin
在 Node.js 中成功运行第一个脚本以后,咱们将接触下一个主要概念:模块。
能够在 JavaScript 中建立单一功能的函数,但与在 Java、Ruby 或 Perl 中不一样,没法将多个函数打包到一个可以导入导出的内聚模块或 ”包“ 中。固然,使用 <script>
元素能够包含任意 JavaScript 源代码文件,但这种历史悠久的方法在两个关键方面缺乏正确的模块声明。
首先,使用 <script>
元素包含的任意 JavaScript 将被加载到全局命名空间中。使用模块能够导入的函数被封装在一个局部命名的变量中。其次,同时更为关键的是,可使用模块显式地声明依赖关系,而使用 <script>
元素则作不到这一点。结果,导入 Module A 时也会同时导入依赖的 Modules B 和 C。当应用程序变得复杂时,传递依赖关系管理很快将成为一种关键需求。
顾名思义,CommonJS 项目定义了一种通用的模块格式(包括其余浏览器以外的 JavaScript 规范)。Node.js 属于众多非官方的 CommonJS 实现之一。RingoJS (相似于 Node.js 的一种应用服务器,运行在 JDK 上的 Rhino/Nashorn JavaScript 运行时之上) 基于 CommonJS,流行的 NoSQL 持久存储 CouchDB 和 MongoDB 也是如此。
模块是用户衷心期盼的下一 JavaScript 主要版本 (ECMAScript 6) 的功能,但直到该版本被普遍接受以前,Node.js 目前使用的是它本身基于 CommonJS 规范的模块版本。
使用 require
关键字能够在脚本中包含 CommonJS 模块。例如,清单 4 是对 Node.js 主页上的 Hello World 脚本稍微进行修改后的版本。建立一个名为 example.js 的文件,并将清单 4 中的代码复制到其中。
var http = require('http'); var port = 9090; http.createServer(responseHandler).listen(port); console.log('Server running at http://127.0.0.1:' + port + '/'); function responseHandler(req, res){ res.writeHead(200, {'Content-Type': 'text/html'}); res.end('<html><body><h1>Hello World</h1></body></html>'); }
输入 node example.js
命令运行新的 Web 服务器,而后在 Web 浏览器中访问 http://127.0.0.1:9090。
看一看清单 4 中的头两行。您极可能写过几百次(或几千次)像 var port = 9090;
这样的简单语句。这条语句定义了一个名为 port
的变量,并将数字 9090
赋值给它。第一行 (var http = require('http');
) 用于导入一个 CommonJS 模块。它引入 http
模块并将它指派给一个局部变量。 and assigns it to a local variable. All of the corresponding modules that http
依赖的全部对应模块也同时被 require
语句导入。
example.js 后面的代码行:
这样经过寥寥几行 JavaScript 代码,就能够在 Node.js 中建立了一个简单的 Web 服务器。在本系列随后的文章中您会看到,Express 将这个简单的例子被扩展用于处理更为复杂的路由,同时还将提供静态与动态生成的资源。
http
模块是 Node.js 安装的标准组件之一。其余标准的 Node.js 模块还支持文件 I/O,读取来自用户的命令行输入,处理底层的 TCP 和 UDP 请求等等。访问 Node.js 文档的 Modules 部分,查看标准模块的完整列表并了解它们的功能。
尽管模块列表内容十分丰富,但与可用的第三方模块列表相比,仍然是小巫见大巫。要访问它们,您须要熟悉另外一个命令行实用工具:NPM。
NPM 是 Node Packaged Modules 的简写。要查看包含超过 75,000 个公用第三方 Node 模块的清单,请访问 NPM
网站。在网站上搜索 yo
模块。图 2 显示了搜索结果。
图 2. yo
模块的详细状况
结果页面简要介绍了该模块(搭建 Yeoman 项目的 CLI 工具),并显示它在过去一天、一周和一月内被下载的次数、编写该模块的做者、它依赖于哪些其余的模块(若是存在)等内容。最重要的是,结果页面给出了安装该模块的命令行语法。
要从命令行获取关于 yo
模块的相似信息,请输入 npm info yo
命令。(若是您还不知道模块的官方名称,能够输入 npm search yo
来搜索名称中包含字符串 yo
的全部模块。)npm info
命令显示模块的 package.json 文件的内容。
每一个 Node.js 模块都必须关联一个格式良好的 package.json 文件,所以,熟悉此文件的内容是值得的。清单 五、清单 6 和清单 7 分三部分显示了 yo
模块的 package.json 文件的内容。
如清单 5 中所示,第一个元素一般是 name
、description
和一个可用 versions
的 JSON 数组。
$ npm info yo { name: 'yo', description: 'CLI tool for scaffolding out Yeoman projects', 'dist-tags': { latest: '1.1.2' }, versions: [ '1.0.0', '1.1.0', '1.1.1', '1.1.2' ],
要安装一个模块的最新版本,请输入 npm install _package_
命令。输入 npm install _package_@_version_
能够安装一个特定的版本。
如清单 6 中所示,接下来将显示做者、维护者和能够直接查找源文件的 GitHub 库。
author: 'Chrome Developer Relations', repository: { type: 'git', url: 'git://github.com/yeoman/yo' }, homepage: 'http://yeoman.io', keywords: [ 'front-end', 'development', 'dev', 'build', 'web', 'tool', 'cli', 'scaffold', 'stack' ],
在这个例子中,还能够看到一个指向项目主页的连接和一个相关关键字的 JSON 数组。并不是全部 package.json 文件中都会出现全部这些字段,但用户不多会抱怨与一个项目相关的元数据太多。
最后,清单 7 中列出了附有显式版本号的依赖关系。这些版本号符合主版本.次版本.补丁版本的经常使用模式,被称为 SemVer(语义版本控制)。
engines: { node: '>=0.8.0', npm: '>=1.2.10' }, dependencies: { 'yeoman-generator': '~0.16.0', nopt: '~2.1.1', lodash: '~2.4.1', 'update-notifier': '~0.1.3', insight: '~0.3.0', 'sudo-block': '~0.3.0', async: '~0.2.9', open: '0.0.4', chalk: '~0.4.0', findup: '~0.1.3', shelljs: '~0.2.6' }, peerDependencies: { 'grunt-cli': '~0.1.7', bower: '>=0.9.0' }, devDependencies: { grunt: '~0.4.2', mockery: '~1.4.0', 'grunt-contrib-jshint': '~0.8.0', 'grunt-contrib-watch': '~0.5.3', 'grunt-mocha-test': '~0.8.1' },
这个 package.json 文件代表,它必须安装在 0.8.0 或更高版本的 Node.js 实例上。若是试图使用 npm install
命令安装一个不受支持的版本,那么安装将会失败。
在 清单 7 中,您会注意到,不少依赖关系版本中都有一个波浪符号 (~)。这个符号至关于 1.0.x(也属于有效语法),意思是 ”主版本必须是 1,次版本必须是 0,但您能够安装所能找到的最新补丁版本“。SemVer 中的这种隐含表达法意味着,补丁版本毫不会 对 API 作出重大修改(一般是对现有功能的缺陷修复),而次版本会在不打破现有功能的状况下引入另外的功能(好比新的函数调用)。
除了平台要求以外,这个 package.json 文件还提供几个依赖关系列表:
dependencies
部分列出了运行时的依赖关系。devDependencies
部分列出了开发过程当中须要的模块。peerDependencies
部分支持做者定义项目之间的 ”对等“ 关系。这种功能一般用于指定基础项目与其插件之间的关系,但在这个例子中,它指出了包含 Yeoman 项目与 Yo 的其余两个项目(Grunt 与 Bower)。若是在不指定模块名的状况下输入 npm install
命令,那么 npm
会访问当前目录中的 package.json 文件,并安装我刚刚讨论过的三部份内容中列出的全部依赖关系。
安装一个能正常工做的 MEAN 堆栈,下一步是安装 Yeoman 与相应的 Yeoman-MEAN 生成器。
做为一名 Java 开发人员,我没法想象在没有诸如 Ant 或 Maven 这样的编译系统的状况下如何启动一个新项目。相似地,Groovy 和 Grails 开发人员依靠的是 Gant(Ant 的一种 Groovy 实现)或 Gradle。这些工具能够搭建起一个新的目录结构,动态下载依赖关系,并准备好将项目发布。
在纯粹的 Web 开发环境中,Yeoman 能够知足这种须要。Yeoman 是三种 Node.js 工具的集合,包括用于搭建的纯 JavaScript 工具 Yo,管理客户端依赖关系的 Bower,以及准备项目发布的 Grunt。经过分析 清单 7 能够得出这样的结论:安装 Yo 时也会安装它对等的 Grunt 和 Bower,这要感谢 package.json 中的 peerDependencies
部分。
一般,输入 npm install yo --save
命令能够安装 yo
模块并更新 package.json 文件中的 dependencies
部分。(npm install yo --save-dev
用于更新 devDependencies
部分。)但这三个对等的 Yeoman 模块算不上是特定于项目的模块,它们是命令行实用工具,而非运行时依赖关系。要全局安装一个 NPM 包,须要在 install
命令后增长一个 -g
标志。
在系统上安装 Yeoman:
npm install -g yo
在完成包安装后,输入 yo --version
命令来验证它已经在运行中。
Yeoman 与基础架构的全部余下部分都准备就绪后,即可以开始安装 MEAN 堆栈了。
您能够手动安装 MEAN 堆栈的每一部分,但须要十分当心。谢天谢地,Yeoman 经过其 generators(生成器) 提供了一种更轻松的安装方式。
Yeoman 生成器就是引导一个新 Web 项目更轻松的方式。该生成器提供了基础包及其全部依赖关系。此外,它一般还会包含一个工做的编译脚本及其全部相关插件。一般,该生成器还包含一个示例应用程序,包括测试在内。
Yeoman 团队构建和维护了几个 "官方的" Yeoman 生成器。社区驱动的 Yeoman 生成器(超过 800 个)远远超过官方生成器的数量。
您将用于引导第一个 MEAN 应用程序的社区生成器被称为 MEAN.JS,这也在乎料之中。
在 MEAN.JS 主页上,单击 Yo Generator 菜单选项或者直接访问 Generator 页面,图 3 中显示了其中的一部分。
图 3. MEAN.JS Yeoman 生成器
该页面上的说明指出要首先 Yeoman,这一点您已经完成。下一步是全局安装 MEAN.JS 生成器:
npm install -g generator-meanjs
生成器准备就绪后,即可以开始建立您的第一个 MEAN 应用程序了。建立一个名为 test 的目录,使用 cd
命令进入它,而后输入 yo meanjs
命令生成应用程序。回答最后两个问题,如清单 8 中所示。(您能够为开始四个问题提供本身的答案。)
$ mkdir test $ cd test $ yo meanjs _-----_ | | |--(o)--| .--------------------------. `---------� | Welcome to Yeoman, | ( _�U`_ ) | ladies and gentlemen! | /___A___\ '__________________________' | ~ | __'.___.'__ � ` |� � Y ` You're using the official MEAN.JS generator. [?] What would you like to call your application? Test [?] How would you describe your application? Full-Stack JavaScript with MongoDB, Express, AngularJS, and Node.js [?] How would you describe your application in comma separated key words? MongoDB, Express, AngularJS, Node.js [?] What is your company/author name? Scott Davis [?] Would you like to generate the article example CRUD module? Yes [?] Which AngularJS modules would you like to include? ngCookies, ngAnimate, ngTouch, ngSanitize
在回答最后一个问题后,您会看到一系列行为,这是 NPM 在下载全部服务器端的依赖关系(包括 Express)。NPM 完成后,Bower 将下载全部客户端的依赖关系(包括 AngularJS、Bootstrap 和 jQuery)。
至此,您已经安装了 EAN 堆栈(Express、AngularJS 和 Node.js) — 目前只缺乏 M (MongoDB)。若是如今输入 grunt
命令,在没有安装 MongoDB 的状况下启动应用程序,您会看到相似于清单 9 中的一条错误消息。
events.js:72 throw er; // Unhandled 'error' event ^ Error: failed to connect to [localhost:27017] at null.<anonymous> (/test/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/server.js:546:74) [nodemon] app crashed - waiting for file changes before starting...
若是启动应用程序时看到这条错误消息,请按下 Ctrl+C 键中止应用程序。
为了使用新的 MEAN 应用程序,如今须要安装 MongoDB。
MongoDB 是一种 NoSQL 持久性存储。它不是使用 JavaScript 编写的,也不是 NPM 包。必须单独安装它才能完成 MEAN 堆栈的安装。
访问 MongoDB 主页,下载平台特定的安装程序,并在安装 MongoDB 时接受全部默认选项。
安装完成时,输入 mongod
命令启动 MongoDB 守护程序。
MeanJS Yeoman 生成器已经安装了一个名为 Mongoose 的 MongoDB 客户端模块,您能够检查 package.json 文件来确认这一点。我将在后续的文章中详细介绍 MongoDB 和 Mongoose。
安装并运行 MongoDB 后,最终您能够运行您的 MEAN 应用程序并观察使用效果了。
要启动新安装的 MEAN 应用程序,在运行 MeanJS Yeoman 生成器以前,必定要位于您建立的 test 目录中。在输入 grunt
命令时,输出内容应该如清单 10 中所示。
$ grunt Running "jshint:all" (jshint) task >> 46 files lint free. Running "csslint:all" (csslint) task >> 2 files lint free. Running "concurrent:default" (concurrent) task Running "watch" task Waiting... Running "nodemon:dev" (nodemon) task [nodemon] v1.0.20 [nodemon] to restart at any time, enter `rs` [nodemon] watching: app/views/**/*.* gruntfile.js server.js config/**/*.js app/**/*.js [nodemon] starting `node --debug server.js` debugger listening on port 5858 NODE_ENV is not defined! Using default development environment MEAN.JS application started on port 3000
jshint
和 csslint
模块(均由生成器进行安装)能够确保源代码在句法和语体上是正确的。nodemon
包监控文件系统中的代码修改状况,并在检测到有的状况下自动重启服务器,当开发人员须要快速而频繁地修改代码基础时,这能够极大地提升他们的效率。(nodemon
包只在开发阶段运行,要监测生产环境的变化,必须从新部署应用程序并重启 Node.js。)
按照控制台输出的提示,访问 http://localhost:3000 并运行您的新 MEAN 应用程序。
图 4 显示了 MEAN.JS 示例应用程序的主页。
图 4. MEAN.JS 示例应用程序的主页
菜单栏中单击 Signup 建立一个新的用户帐号。如今填写 Sign-up 页面上的全部字段(如图 5 中所示),而后单击 Sign up。在后续的指南中,您能够经过 Facebook、Twitter 等启用 OAuth 登陆
图 5. MEAN.JS 示例应用程序的 Sign-up 页面
如今,您的本地 MongoDB 实例中已经保存了一组用户证书,您能够开始撰写新的文章了。单击 Articles 菜单选项(当您登陆以后才会显示出来),并建立一些示例文章。图 6 显示了 Articles 页面。
图 6. MeanJS 的文章页面
您已经建立了本身的第一个 MEAN 应用程序。欢迎加入!
在这篇指南中,您完成至关多的内容。安装 Node.js 并编写了第一个 Node.js 脚本。学习了模块并使用 NPM 安装了几个第三方模块。安装 Yeoman 并将它做为可靠的 Web 开发平台,其中包含一个搭建实用工具 (Yo),一个编译脚本 (Grunt),以及一个管理客户端依赖关系的实用工具 (Bower)。安装 MeanJS Yeoman 生成器,并使用它来建立第一个 MEAN 应用程序。安装 MongoDB 与 Node.js 客户端库 Mongoose。最后运行您的首个 MEAN 应用程序。
下一次,咱们会详细了解示例应用程序的源代码,从而了解 MEAN 太阳系中的全部四颗行星 (MongoDB、Express、AngularJS 和 Node.js)是如何相互做用的。
原文出处:精通 MEAN: MEAN 堆栈