本文翻译自Hackathon Starter的Github页面。
在线demo: http://hackathonstarter.herokuapp.com
本文翻译时Hackathon Starter的version:2.3.2
Hackathon Starter是专门为Node.JS Web开发而准备的一个样板。
如果你以前参加过黑客马拉松(hackathon),那么你一定会意识到项目准备阶段会花费大量时间:比如决定制作什么、选择编程语言、选择web 框架,以及选择css框架。一段时间过后,你好不容易在Github上建立起初始化的项目,然后其他成员才终于能够开始工作。或者考虑一个更简单的情景, 使用Facebook账户登录。如果你不熟悉OAuth 2.0的话,这将耗费你大量的时间。
当我开始本项目时,我首要考虑的是简单和易用。我也试着让它尽量的兼容以及可复用,使它能够在大多数hackathon web app上使用。在最坏的情况,比如你只对使用Google账户登录感兴趣,你也可以将它当做一个学习指南。
你很可能不需要使用所有的账号登录认证功能,不用担心,这些在Hackathon Starter 2.1版本之后是可选择的。
现代风格
扁平化Bootstrap主题
默认主题
Hackathon Starter生成器界面
特性
- 本地登录认证(使用Email与密码)
- OAuth 1.0a认证( Twitter)
- OAuth 2.0认证(Facebook、Google、Github、Linkedin等)
- 快速提示
- MVC项目结构
- Node.JS 集群支持
- Rails 3.1风格的 Asset pipeline,由connect-assets提供
- LESS样式表(自动编译无需Gulp/Grunt)
- Bootstrap 3 + Flat UI + iOS 7
- 联系表单(支持Mailgun、Sendgrid、Mandrill)
- 账户管理
Gravatar头像
用户详细资料
改密码
找回密码
重置密码
绑定社交账号
注销账号 - CSRF保护
- API案例(Facebook等)
环境依赖
- MongoDB
- Node.JS
- 命令行工具
Mac OS X:Xcode
Windows:Visual Studio
Ubuntu:sudo apt-get install build-essential
Fedora:sudo yum groupinstall "Development Tools"
OpenSUSE:sudo zypper install --type pattern devel_basis
注意:如果你是Node.JS新手,建议阅读教程Getting Started With Node.js, Express, MongoDB。
入门指南
最简单的开始方法就是克隆Github仓库:
# Get the latest snapshot git clone --depth=1 https://github.com/sahat/hackathon-starter.git myproject cd myproject # Install NPM dependencies npm install node app.js
注意:强烈建议安装Nodemon,它能监控你的Node.JS App的任何改动并自动重启,从长远来看着将节省你大量时间。
生成器(Generator)
Hackathon Starter生成器目前还在实验阶段,它与目前的代码紧密相连,一旦移动或改变项目代码,生成器将有可能不可用,因此建议在下载HS后第一时间使用。
生成器能够选择账号认证、改变发送邮件的服务商。
获得API密钥(略过)
本部分讲如何从Facebook、Google等服务提供商处获取API密钥。
项目结构
Name | Description |
---|---|
config/passport.js | 本地与OAuth的账号认证策略,包括登录 |
config/secrets.js | API密钥、密码、数据库地址等 |
controllers/api.js | /api 路由控制器,包括所有api示例 |
controllers/contact.js | 联系表单的控制器 |
controllers/home.js | 主页(index)的控制器 |
controllers/user.js | 用户账号管理的控制器 |
models/User.js | Mongoose中用户的schema与model |
public/ | 静态资源 (fonts, css, js, img) |
public/js/application.js | 指定客户端JS依赖 |
public/js/main.js | 你所编写的客户端JS |
public/css/styles.less | 你的App的主样式表 |
public/css/themes/default.less | 一些Bootstrap默认样式 |
views/account/ | 账号管理模板 |
views/api/ | API示例模板 |
views/partials/flash.jade | 错误、信息与成功的提示 |
views/partials/navigation.jade | 导航栏部分的模板 |
views/partials/footer.jade | Footer部分的模板 |
views/layout.jade | 基础模板 |
views/home.jade | 主页模板 |
app.js | 主要的App文件 |
setup.js | 移除账号认证等的工具 |
注意:这里没有规定你应该如何处理你的视图,你可以将你的视图模板放在你喜欢的地方,只要记住更新extends ../layout
并且与控制器中的res.render()
路径一致。
使用包列表
Package | Description |
---|---|
async | 提供同步控制流的工具库 |
bcrypt-Node.JS | 哈希并盐化用户密码的库 |
cheerio | 提供服务器端处理web页面能力的库 |
clockwork | Clockwork SMS API库 |
connect-assets | 处理和编译LESS样式和JS文件的工具 |
connect-mongo | MongoDB连接Express的库 |
csso | connect-assets库的依赖 |
express | Node.js web框架 |
body-parser | Express 4.0 中间件 |
cookie-parser | Express 4.0 中间件 |
express-session | Express 4.0 中间件 |
morgan | Express 4.0 中间件 |
compression | Express 4.0 中间件 |
errorhandler | Express 4.0 中间件 |
method-override | Express 4.0 中间件 |
express-flash | 提供Express的快速提示 |
express-validator | Express的简单表单验证 |
fbgraph | Facebook Graph API 库 |
github-api | GitHub API 库 |
jade | Express的模板引擎 |
lastfm | Last.fm API 库 |
instagram-node | Instagram API 库 |
less | LESS编译器. 在connect-assets中使用. |
lusca | CSRF 中间件 |
mongoose | MongoDB ODM. |
node-foursquare | Foursquare API 库 |
node-linkedin | LinkedIn API 库 |
nodemailer | Node.js发送邮件的库 |
passport | node.js简单优雅的账号认证库 |
passport-facebook | Sign-in with Facebook 插件 |
passport-github | Sign-in with GitHub 插件 |
passport-google-oauth | Sign-in with Google 插件 |
passport-twitter | Sign-in with Twitter 插件 |
passport-instagram | Sign-in with Instagram 插件 |
passport-local | 本地登录的插件 |
passport-linkedin-oauth2 | Sign-in with LinkedIn 插件 |
passport-oauth | 设定你自己的OAuth1.0a与OAuth2.0策略 |
request | 简化的HTTP请求库 |
stripe | 官方 Stripe API 库 |
tumblr.js | Tumblr API 库 |
twilio | Twilio API 库 |
twit | Twitter API 库 |
lodash | 方便的JS工具库 |
uglify-js | connect-assets的依赖 |
validator | 在 controllers/api.js中与express-validator联合使用 |
mocha | 测试框架 |
chai | BDD/TDD 声明库 |
supertest | HTTP 声明库 |
multiline | 生成器使用的Multi-line 字符串 |
blessed | 生成器使用的互动式命令行界面 |
yui | Yahoo API 示例中使用 |
有用的工具与资源
- JSDB.io – 包含绝大多数JS库的数据库
- JS Recipes – 前端与后端的JS教程
- Jade Syntax Documentation by Example – 比jade官方的文档还要好
- HTML to Jade converter – 当你需要将从网上找来的HTML片段快速的应用到项目中时特别有用
- JavascriptOO – 一个常用JS库的示例、CDN以及教学视频的目录
- Favicon Generator – 生成各平台的favicon
推荐的设计资源
- Code Guide – 开发灵活、耐用、可持续的HTML与CSS的标准
- Bootsnipp – 配合Bootstrap使用的一些代码片段
- UIBox – HTML, CSS, JS, UI 组件
- Bootstrap Zero – 免费的Bootstrap主题
- Google Bootstrap – Google风格的Bootstrap主题
- Font Awesome Icons – 已经被包含在Hackathon Starter项目中,可以将这作为参考页面
- Colors – 更好的web页面配色
- Creative Button Styles – 各种各样非常棒的按钮
- Creative Link Effects – 漂亮的链接CSS效果
- Medium Scroll Effect – 像Medium一样的顶部背景图片渐隐效果
- GeoPattern – SVG background pattern generator.
- Trianglify – SVG low-poly background pattern generator.
推荐的Node.JS库
- Nodemon – 代码改动时自动重启Node.js服务
- geoip-lite – 根据IP地址库的地理位置定位
- Filesize.js – 格式化文件大小,如
filesize(265318); // "265.32 kB"
. - Numeral.js – 格式化并操作数字的库
- Node Inspector – 基于Chrome开发者工具的Node.js调试器
- node-taglib – 读取常用音频格式的meta-data的库
- sharp – 调整图片大小的库,支持JPEG, PNG, WebP 和 TIFF
推荐的客户端JS库
- Framework7 – 包含构建iOS7风格App完整特性的HTML框架
- InstantClick – 在鼠标指上时预下载,加快页面加载速度
- NProgress.js – 简练的进度显示条
- Hover – 非常棒的鼠标hover css3动画效果
- Magnific Popup – 响应式的jQuery弹出框插件
- jQuery Raty – 星星打分插件
- Headroom.js – 隐藏你的header直到你需要它
- X-editable – 直观的修改表单元素
- Offline.js – 探测用户是否在线
- Alertify.js – 可爱的弹出警告与浏览器对话框
- selectize.js – 可调整样式的select元素与input标签
- drop.js – 强大的JS与CSS库用于创建下拉菜单与其他浮动显示层
- scrollReveal.js – 提供滚动时的动画效果
高级Tips
- 当安装NPM包时,添加–save标签,它将自动添加到package.json文件中。如:npm install –save moment
- 当你需要多个同步工作,并当它们都完成后才渲染页面时,使用async.parallel() 。比如你需要爬取三个页面的数据,爬取完成后将结果填充到模板中。
- 想要从队列中寻找特定的对象?试试Lodash里的_.find 函数。比如,这段代码提供了检索Twitter token的能力:
- var token = _.find(req.user.tokens, { kind: 'twitter' });
FAQ
为什么我在提交表单时显示403错误?
你需要在表单中添加下面的隐藏元素,这是一项CSRF保护措施。
- input(type='hidden', name='_csrf', value=_csrf)
注意:CSRF现在支持白名单了,这意味着你可以提交一些URL链接,他们可以被CSRF忽略。
注意2:如需忽略的URL是动态的,可以用正则表达式匹配。
cluster_app.js是什么?
一个Node实例在单个线程中运行。为了充分利用多内核系统的性能,用户会希望启动一个Node的进程簇来处理负载。cluster模块让你能够简单创建共享服务器端口的子进程。
cluster_app.js是app.js的多进程版本,它能为每个被探测到的CPU创建一个进程。为了最大化的满足HTTP请求,这是一个很好的功能。但是,cluster模块仍处於实验阶段,因此请小心使用,确保你正确理解了它的意图和行为。要使用它,只需要运行node cluster_app.js
,它与app.js是完全分离的,无任何依赖关系。需要提醒的是,如果你用cluster_app.js替代app.js,你需要在package.json里做出声明。
什么是Rails 3.1风格的asset pipeline?
下面是你如何在HTML里定义静态文件,使用Jade或其他的模板引擎:
- link(href='/css/styles.css', rel='stylesheet')
- script(src='/js/lib/jquery-2.1.0.min.js')
- script(src='/js/lib/bootstrap.min.js')
- script(src='/js/main.js')
看起来足够简单?在开发环境下也行是这样。但如果当你将app部署到生产环境时,它们能够被自动的压缩到单个的文件呢?
- link(href='/css/styles.css', rel='stylesheet')
- script(src='/js/application.js')
当你引入的JS库越多,自动连接并压缩JS文件带来的好处就越大。connect-assets库能够让你简单的完成这一操作,只需两行代码:
- != css('styles') // expects public/css/styles.less
- != js('application') // expects public/js/application.js
你只需要记住在public/js/application.js中定义你的JS文件。语法从Rails中借鉴而来:
- //= require lib/jquery-2.1.0.min
- //= require lib/bootstrap.min
- //= require main
使用这个方法,当在开发模式时,它会加载各个独立的JS文件,而当部署到生产环境,它会自动形成单个JS文件。你可以看Sprockets-style concatenation 来了解更多。
我出现了MongoDB Connection Error,该如何修复它?
这是一个在app.js中自定义的错误信息,用来表示连接到MongoDB时出现了问题。
- mongoose.connection.on('error', function() {
- console.error('✗ MongoDB Connection Error. Please make sure MongoDB is running.');
- });
它提示你应该在启动app.js之前先启动MongoDB,你可以在这里下载MongoDB,也可以从包管理器来安装,如果你是Windows用户,可以按照在Windows上安装MongoDB的说明来做。
Tip:如果你一直连接着网络,也可以试着使用 MongoLab 或者 MongoHQ 等在线数据库服务,你只需要更新config/secrets.js中的db信息。
当我部署我的app时提示错误,为什么?
有可能是你没有在secrets.js中正确的设置数据库路径。当你在本地运行你的app时,数据库路径是localhost,但当你部署app时,你需要在网上找到一个运行的MongoDB,并将连接地址正确的填写在secrets.js中。你也可以申请MongoLab 或者 MongoHQ 等免费服务。
为什么采用Jade代替Handlebars模板引擎?
当我开始这个项目的时候我并不熟悉Handlebars,后来我开发了一些Ember.js apps并且熟悉了Handlebars的语法。Handlebars的确更简单一些,因为它就像HTML一样,但我并不后悔选择了Jade。理由有三, 第一因为Jade是Express的默认模板引擎,所以以前开发过Express应用的人已经对它很熟悉了;第二,我发现在Handlebars里 extends和block是必不可少的,它实际上并没有达到即开即用的程度,你仍然需要编写一些扩展函数;第三,客观的说,Jade看上去比 Handlebars更简洁干净,这点与其他非HAML风格的引擎相比也是一样。
为什么你在app.js里定义了所有的路由(route)?
一言以蔽之,为了简洁。也许有其他更好的方法,比如在这篇博文中 将app上下文按照概述传给每一个控制器,但我发现这种方法对初学者来说说容易搞混的。我花了大量时间来理解exports和 module.exports的概念,保证有一个单独的全局性app文件作为参考。这是我的背景想法。app.js对我来说是“app的心脏”,它应该成 为其他所有模型、控制器、路由等的参考。
我不需要一个绝对底部(sticky footer),我能删除它吗?
当然可以。不过不像一个常规footer,你还需要做一些额外的工作。首先,从styles.less里删除#wrap
和#footer
,以及html, body { height: 100%; }
。然后,从layout.jade中删除#wrap
和#footer
所在的行(顺便说下,如果Jade没有检测到class或id,它会默认其是一个div元素)。不要忘了调整#wrap
下面的缩进,本项目使用两个空格表示块级缩进。
我能够使用Sass代替LESS吗?
Yes you can!虽然你需要手动的转换现有的样式表到Sass,考虑到Sass和LESS的相似程度,这不会太难。然后你只需要重命名styles.less为styles.scss,connect-assets会自动的采用Sass预处理器。
你甚至可以同时的使用Sass和LESS,在layout.jade里分别指定了LESS和Sass样式表文件:
- != css('styles') # public/css/styles.less
- != css('my_sass_styles') # public/css/my_sass_styles.scss
注意:项目的package.json不包含Sass,所以你需要自己安装它,使用以下命令:
- npm install --save node-sass
迷你指南
这一部分将提供单一特定功能的细节解释。也许你很好奇这个项目是如何工作的,也许你已经迷失在代码当中,我希望它能给你一些指引。
定制HTML与CSS设计入门
HTML5 UP提供许多漂亮的模板,并且可以免费下载。
当你下载了一个zip文件,里面有index.html、images、css和js文件夹。那么,如何把它拿到Hackathon Starter里面来呢?Hackathon Starter使用Bootstrap CSS框架,但那些模板没有使用。将它们放到一起会出现很多意想不到的情况。
注意:使用定制模板的方法,你应当理解你不能重用我创建的所有视图:layout、主页、登录、注册、账号管理、联系页面。这些视图使用 Bootstrap栅格风格创建。你需要用新模板里的语法手动更新这些栅格。不过你也可以用另一种方法,在大多数界面使用Bootstrap,而在 landing page使用另一种风格的模板。
让我们从头开始,在这个例子里我将使用Escape Velocity 模板。
注意:为了简洁起见我将只考虑index.html,忽略left-sidebar.html、 no-sidebar.html和 right-sidebar.html。
将所有的js文件从html5up-escape-velocity/js移动到public/js,并将所有css文件从html5up- escape-velocity/css移动到public/css,最后将所有图片文件从html5up-escape-velocity /images移动到public/images,复制index.html里的代码,并将它们粘贴到HTML To Jade 进行转换。
创建一个新文件escape-velocity.jade,将转换到的Jade代码粘贴进去。将!!! 5
修改为doctype html
,这是Jade最近的一个改动之一,但是http://html2jade.aaron-powell.com 还没有跟进这个改动。
在controllers/home.js里创建一个新的控制器escapeVelocity:
- exports.escapeVelocity = function(req, res) {
- res.render('escape-velocity', {
- title: 'Landing Page'
- });
- };
然后在app.js里创建一个路由,我将它放在index控制器的后面::
- app.get('/escape-velocity', homeController.escapeVelocity);
重启服务器(如果你没有使用nodemon),然后你就可以在http://localhost:3000/escape-velocity 来查看新模板了。
我的讲解将在这里打住,如果你想在更多的页面使用这个模板,下面是你需要关注的Jade文件:
- layout.jade – 基本模板
- index.jade – 主页
- partials/navigation.jade – Bootstrap导航栏
- partials/footer.jade – 绝对底部(sticky footer)
你需要手动的将新模板分解为更小的部分。弄清楚模板的哪些部分你想在所有的页面中保留——那将是你的新layout.jade,其他页面将通过 block content来共享代码。如果有不清楚的地方,你可以使用已有的模板作为参考。
这是一个枯燥无味的过程,如果你下载的模板有一套新的栅格系统,那么需要更加谨慎了。这是我为什么使用Bootstrap的原因。很多人已经熟悉Bootstrap了,即使没用过学起来也很简单。你还可以从Themeforest购买一些漂亮的Bootstrap模板。然后你可以很方便的将它放到Hackathon Starter里。如果你需要完全的定制HTML与CSS,上面这些内容将帮助你。
快速提示是如何工作的?
快速提示(Flash messages)允许你在一个请求的结尾,以及当且仅当下一个请求之前显示一段信息。比如,当登录失败时,你可以输出一些警告信息,但一旦刷新页面或者 再次进入登录页面后,这个警告信息不应该再次出现,它只会被显示一次。本项目使用express-flash模块来显示快速提示,这个模块在我创建 Hackathon Starter项目时就被包含在connect-flash里面。有了express-flash你无需发送快速提示到每一个视图,它一开始就是可用的, 感谢express-flash。
使用快速提示需要两个步骤。使用下面的代码在你的控制器里创建一个快速提示:
- req.flash('errors', { msg: 'Error messages goes here' }
然后在你的视图里显示它们:
- if messages.errors
- .alert.alert-danger.fade.in
- for error in messages.errors
- div= error.msg
在第一步里,'errors'
是你定义的快速提示的名称,它应该和你的视图里的messages
里的属性名称相匹配。将警告信息放在if message.errors
以让它们出错时才显示。错误信息采用{ msg: 'Error messages goes here' }
的形式而不是像'Error messages goes here'
一样的字符串是为了一致性。express-validator模块会验证用户的输入,当发生错误时返回一个数组对象,每个对象都含有一个msg
属性。下面是express-validator所返回信息的一个示例:
- [
- { param: "name", msg: "Name is required", value: "<received input>" },
- { param: "email", msg: "A valid email is required", value: "<received input>" }
- ]
如果使用字符串,你会发现信息框里面是空的。上面的错误信息也可以应用在info或者success的场合。
partials/flash.jade是控制快速提示格式化显示的子模板。它使用一个叫做DRY的方法,将散落在各个视图的快速提示归到一处。
和导航栏与footer子模板一样,flash子模板被包含在layout.jade里。
- body
- #wrap
- include partials/navigation
- .container
- include partials/flash
- block content
- include partials/footer
如何创建一个新页面?
更正确的说法应该是“如何创建一个新路由”。主文件app.js包含所有的路由,每一个路由都伴随着一个callback函数,你会在某些路由里发 现3个以上的参数,在这种情况里,第一个参数仍然是URL字符串,中间的参数是中间件,你可以将它们想象成门禁,如果它阻止你前进,你将不能得到 callback函数。一个例子是需要认证的路由:
- app.get('/account', passportConf.isAuthenticated, userController.getAccount);
它的顺序总是从左到右。当用户访问/account页面,isAuthenticated中间件将检查用户是否认证:
- exports.isAuthenticated = function(req, res, next) {
- if (req.isAuthenticated()) {
- return next();
- }
- res.redirect('/login');
- };
如果认证检查通过,你通过呼叫return next()的方式让用户通过门禁。它会依次通过剩下的中间件直到最后一个参数,即callback函数,它通常在接受到GET请求时渲染页面,或在收到 POST请求时跳转页面。在这个例子里,你将被跳转到用户账号管理页面,如果认证没有通过,你将被跳转到登录页面。
- exports.getAccount = function(req, res) {
- res.render('account/profile', {
- title: 'Account Management'
- });
- };
Express.js里有app.get、app.post、app.put、app.delete四种HTTP动作。但在大多数情况你只需要前两种,除非你想创建一个RESTful API。如果你只想显示一个页面,使用GET,如果你想提交表单,使用POST。
下面是一个添加路由到你的app里的典型工作流。我们的目的是创建一个页面显示数据库里的书籍列表。
第一步:定义一个路由。
- app.get('/books', bookController.getBooks);
在express 4.0以上你可以这样定义你的路由:
- app.route('/books')
- .get(bookController.getBooks)
- .post(bookController.createBooks)
- .put(bookController.updateBooks)
- .delete(bookController.deleteBooks)
而下面是一个需要认证中间件的路由:
- app.route('/api/twitter')
- .all(passportConf.isAuthenticated)
- .all(passportConf.isAuthorized)
- .get(apiController.getTwitter);
- .post(apiController.postTwitter)
上面三种方式都是可接受的,你可以使用它们中的任何一种。我认为app.route里HTTP动词的这种链式写法非常干净优雅,不过缺点是当每个路由占一行的时候,你不能一眼看到所有的动作。
第二步:创建一个叫book.js的新控制器文件。
- /**
- * GET /books
- * List all books.
- */
- exports.getBooks = function(req, res) {
- Book.find(function(err, docs) {
- res.render('books', { books: docs });
- });
- };
第三步:将控制器加入到app.js里。
- var bookController = require('./controllers/book');
第四步:创建book.jade模板。
- extends layout
- block content
- .page-header
- h3 All Books
- ul
- for book in books
- li= book.name
到这里就完成了。
当然,你可以将1到3步合在一起放到app.js里:
- app.get('/books', function(req, res) {
- Book.find(function(err, docs) {
- res.render('books', { books: docs });
- });
- });
是的,这样更简单些,但当你在app.js中加入1000行代码时,浏览起来会变得比较麻烦。这个项目的初衷本来就是为了分解关注点,这样你可以与你的队员一起工作而不是忙碌于解决冲突。
上面的内容就是一切了,Express.js非常易于使用。大部分时间其实用在处理其他API,让它们来干真正的工作,比如查询数据库的Mongoose,使用websocket收发信息的socket.io,发送邮件的 Nodemailer,表单验证的express-validator 库,以及处理web页面的Cheerio 等。
如何在Hackathon Starter里使用Socket.io?
Dan Stroot曾提交了一个非常棒的pull以在Hackathon Starter里添加一个实时的仪表盘。但我认为它违反了非特定化的原则,因此并未接受。但你仍然可以在Hackathon Starter里使用Socket.io。下面是一般的操作步骤。
- npm install socket.io --save
将var app = express();
替换为下面的代码:
- var app = express();
- var http = require('http');
- var server = http.createServer(app);
- var io = require('socket.io').listen(server);
将下面的代码添加到app.js的末尾:
- io.configure(function() {
- io.set('transports', ['websocket']);
- });
- io.sockets.on('connection', function(socket) {
- socket.emit('greet', { hello: 'Hey, Mr.Client!' });
- socket.on('respond', function(data) {
- console.log(data);
- });
- socket.on('disconnect', function() {
- console.log('Socket disconnected');
- });
- });
最后,将
- app.listen(app.get('port'), function() {
改为
- server.listen(app.get('port'), function() {
后端的工作到这里就完成了。
你有两种方式将前端部分的js加到app中,一种是直接加到layout.jade中,将下面的代码加到head块中。
- script(src='/socket.io/socket.io.js')
- script.
- var socket = io.connect(window.location.href);
- socket.on('greet', function (data) {
- console.log(data);
- socket.emit('respond', { message: 'Hello to you too, Mr.Server!' });
- });
注意代码中socket.io的路径,你并不需要实际的在项目中包含socket.io.js,它将在运行时自动生成。
另一种方法是将js部分加入到独立的的main.js文件中,并将它们包含在jQuery的ready函数下。
- $(document).ready(function() {
- // Place JavaScript code here...
- var socket = io.connect(window.location.href);
- socket.on('greet', function (data) {
- console.log(data);
- socket.emit('respond', { message: 'Hello to you too, Mr.Server!' });
- });
- });
到这里所有工作就完成了。
这里有一个实时仪表盘的在线演示。你可以查看这里它是如何添加到工程里的。
Mongoose Cheatsheet
查询所有用户:
- User.find(function(err, users) {
- console.log(users);
- });
通过email查询用户:
- var userEmail = 'example@gmail.com';
- User.findOne({ email: userEmail }, function(err, user) {
- console.log(user);
- });
查询5个最近的用户账号:
- User
- .find()
- .sort({ _id: -1 })
- .limit(5)
- .exec(function(err, users) {
- console.log(users);
- });
从所有文档中查询指定列的总数:
假设每个用户有一个叫做votes的列,你希望统计所有用户的votes总数。一个笨办法是循环查找所有的文档并手动的将结果加起来。另一个方法是使用MongoDB Aggregation Framework 来代替:
- User.aggregate({ $group: { _id: null, total: { $sum: '$votes' } } }, function(err, votesCount) {
- console.log(votesCount.total);
- });
应用部署(略过)
本部分介绍了应用MongoLab、Heroku、OpenShift等在线服务来部署应用,由于网络环境的不同,在国内可能难以用到,所以不予翻译。
(正文完)
译者注:翻译完成后觉得Hackathon Starter的确是个好东西,不过就像这篇文章所说的,每个开发者都需要有自己的Project Starter,这个Hackathon Starter也不适合直接拿来用,吃透它,将它本地化才是最好的做法。