目录:https://segmentfault.com/a/1190000000349384

原文: https://segmentfault.com/a/1190000000361440

到目前为止我们讨论了BowerAngularJSGruntJSPhoneGap等JavaScript技术。今天是“30天学习30种新技术”挑战的第15天,我决定重返JavaScript,学习Meteor框架。虽然Meteor的文档相当好,但是它缺少为初学者准备的教程。我觉得教程的学习效果更好,因为教程可以帮助你快速上手一种技术。本文将介绍如何利用 Meteor 框架构建一个epoll应用。

Meteor是什么?

Meteor是新一代的开发即时web应用的开源框架,它能帮助你在最少的时间内完成开发。它的理念和AngularJS、BackboneJS等框架大不相同。当我们在backbone 和 angular 上工作时,客户端(Angular或Backbone)和REST后端通讯。我们可以用任何技术写 REST 后端,例如 Java、NodeJS、PHP。

Meteor使用DDP(分布式数据协议)在客户端和服务器间传送数据。客户端JavaScript开发者需要解决的首要问题是:向后端的数据库发起查询,发送数据到客户端,当数据库变动时,推送变动到客户端。DDP是解决这一问题的标准做法。

Meteor应用的后端基于Node和MongoDB。前端和后端的应用同时使用Meteor的API。未来开发者可以选择 MongoDB 之外的其他数据库。

为什么使用Meteor?

请阅读Meteor的七大原则

应用案例

本文中我们将搭建一个 epoll 应用,该应用允许用户发布问题并投票。这个应用可以做到:

  • 当用户访问/时,会看到一个问题列表。用户需要通过Twitter登录,以便投票或发布新问题。如下图所示,由于未登录,投票按钮不可用。

  • 当用户点击Sign in with Twitter之后,他将授权 epoll 应用使用他的账号。授权成功之后,用户可以投票或发布新问题。

GitHub仓库

今天的示例应用的代码可以从GitHub取得。

安装Meteor

开始使用Meteor很容易。如果你使用Mac或Linux,只需输入如下命令:

curl https://install.meteor.com | /bin/sh

Windows用户请参阅文档

创建Meteor应用

创建Meteor应用很容易。安装之后,运行create命令即可。

meteor create epoll

这将创建epoll目录,该目录下有一些模板文件。项目结构如下所示:

让我们解释下这个结构:

  1. meteor文件夹下保存meteor特定的文件。.gitignore忽略存储MongoDB数据库文件和应用文件的local文件夹。packages指明本应用所需的包。你可以把它们看成是npm包。Meteor以包的形式提供功能。本文中会使用一些包。release保存了meteor版本。本文使用的版本是0.6.6.3

  2. epoll.css决定应用的CSS样式。

  3. epoll.html是应用的HTML标记。目前meteor只支持handlebars模板引擎,不过未来可能支持其他模板引擎

  4. epoll.js是meteor应用的核心。epoll.js同时部署在服务器段和客户端。这允许开发者一次编写、两端使用。meteor创建的epoll.js模板如下所示:

if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to epoll.";
}; Template.hello.events({
'click input' : function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}

Meteor.isServerMeteor.isClient区分了服务器端和客户端的代码。

meteor命令可以运行应用:

cd epoll
meteor

可以通过 http://localhost:3000 访问应用。点击按钮后,在chrome developer tools中你可以看到You pressed the button.信息。

修改epoll.js的欢迎部分:

Template.hello.greeting = function () {
return "The Missing Meteor Tutorial!!!";
};

变动会自动应用,页面也会自动刷新。

MongoDB在哪?

前面提到Meteor使用MongoDB来存储数据。当我们安装meteor的时候,它同时会下载最新版的MongoDB。我们可以看到,MongoDB安装在<user.home>/.meteor目录。使用ps -ef可以找到MongoDB的安装位置。

; ps -ef|grep mongo

501  1704  1687   0  2:22PM ttys001    0:09.28 /Users/shekhargulati/.meteor/tools/0b2f28e18b/mongodb/bin/mongod --bind_ip 127.0.0.1 --smallfiles --nohttpinterface --port 3002 --dbpath /Users/shekhargulati/day15/epoll/.meteor/local/db

在我的机子上,MongoDB运行于3002端口,以避免和其他默认运行于27017端口的MongoDB冲突。

智能的Meteor包管理

前面提到Meteor以包的形式实现功能。这些包在浏览器和服务器上都能使用。运行以下命令可以得知Meteor支持的所有包:

meteor list

使用meteor addmeteor remove命令来添加删除包。

添加Twitter Bootstrap包

我们将使用Twitter Bootstrap作为用户界面的风格。

meteor add bootstrap

注意,Meteor包不一定是最新版。

添加Twitter授权包

在我们的应用中,用户需要首先通过Twitter授权才能投票或添加问题。Meteor提供了accounts-ui包,可以为我们的应用添加登录组件:

meteor add accounts-ui

然后我们添加授权提供者。在这个应用中,我们使用Twitter,不过我们其实也可以使用facebook、github、google、weibo或meetup。

meteor add accounts-twitter

添加包之后,我们需要更新下epoll.html,添加Twitter登录按钮:

<head>
<title>Epoll : Share your opinion online, anywhere, anytime</title>
</head> <body> <div class="navbar navbar-static-top navbar-inverse"> <div class="navbar-inner">
<div class="container">
<a class="brand" href="/">Epoll</a>
<ul class="nav pull-right">
<li>
{{loginButtons}}
</li>
</ul>
</div>
</div> </div> <div class="container" id="main">
{{> banner}}
</div>
</body> <template name="banner">
<div class="container">
<div class="row">
<div class="span6">
<div class="well">
<h4>Sign in using Twitter to submit new questions or to vote on existing questions.</h4>
{{loginButtons}}
</div>
</div>
</div>
</div>
</template>

然后调整一下样式,增加下面的代码到epoll.css

/* CSS declarations go here */
.login-display-name{color: white }
.login-button{background-color: white}
#main {
padding-top:20px;
}

应用会自动更新,你会见到这样的页面:

现在点击Configure Twitter Login,会要求我们输入 twitter 应用的相关信息:

按照提示配置之后,我们可以使用twitter登录了。

授权之后我们可以登录应用。使用完毕之后,我们可以登出。

MongoDB会在用户集合内创建新用户。我们可以使用mongo命令连接数据库查看:

; ~/.meteor/tools/0b2f28e18b/mongodb/bin/mongo --port 3002

MongoDB shell version: 2.4.6
connecting to: 127.0.0.1:3002/test
> show dbs
local 0.03125GB
meteor 0.0625GB
> use meteor
switched to db meteor > show collections
meteor_accounts_loginServiceConfiguration
system.indexes
users
> db.meteor_accounts_loginServiceConfiguration.find()
{ "service" : "twitter", "consumerKey" : "xxx", "secret" : "xxx", "_id" : "xxx" }
>
>
> db.users.find().pretty()
{
"createdAt" : ISODate("2013-11-11T18:03:23.488Z"),
"_id" : "xx",
"services" : {
"twitter" : {
"id" : "66993334",
"screenName" : "shekhargulati",
"accessToken" : "xxx-xxx",
"accessTokenSecret" : "xxx",
"profile_image_url" : "http://pbs.twimg.com/profile_images/378800000254412405/e4adcf8fb7800c3e5f8141c561cb57e4_normal.jpeg",
"profile_image_url_https" : "https://pbs.twimg.com/profile_images/378800000254412405/e4adcf8fb7800c3e5f8141c561cb57e4_normal.jpeg",
"lang" : "en"
},
"resume" : {
"loginTokens" : [
{
"token" : "xxx",
"when" : ISODate("2013-11-11T18:03:23.489Z")
}
]
}
},
"profile" : {
"name" : "Shekhar Gulati"
}
}
>

定义应用层次

Meteor创建的模板应用有一个问题,客户端和服务器段的epoll.js代码是一样的。任何人的都可以使用浏览器的开发工具查看epoll.js

如果我们不想将服务器端的特有代码发送到客户端,我们可以使用clientserver目录来分隔代码。

cd epoll
mkdir client server

在两个目录下分别创建epollclient.jsepollserver.js文件。

client/epollclient.js内存放客户端代码:

Template.hello.greeting = function () {
return "The Missing Meteor Tutorial!!!";
}; Template.hello.events({
'click input' : function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});

服务器端代码存放在server/epollserver.js

Meteor.startup(function () {
// code to run on server at startup
});
``` 然后删除`epoll.js`: ```sh
rm -f epoll.js

移除insecure

每一个Meteor应用预装了insecure包。这个应用让用户端可以在数据库上实施一切操作。对于原型开发这很有用,但是通常不适合生产环境。

meteor remove insecure

发布问题

现在我们添加一个功能,已登录的用户可以提交新问题。

<head>
<title>Epoll : Share your opinion online, anywhere, anytime</title>
</head> <body> <div class="navbar navbar-static-top navbar-inverse"> <div class="navbar-inner">
<div class="container">
<a class="brand" href="/">Epoll</a>
<ul class="nav pull-right">
<li>
{{loginButtons}}
</li>
</ul>
</div>
</div> </div> <div class="container" id="main">
{{#if currentUser}}
{{> addquestion}}
{{/if}}
{{#unless currentUser}}
{{> banner}}
{{/unless}}
</div>
</body> <template name="banner">
<div class="container">
<div class="row">
<div class="span6">
<div class="well">
<h4>Sign in using Twitter to submit new questions or to vote on existing questions.</h4>
{{loginButtons}}
</div>
</div>
</div>
</div>
</template>
<template name="addquestion"> <textarea rows="3" class="input-xxlarge" name="questionText" id="questionText" placeholder="Add Your Question"></textarea>
<br/>
<input type="button" class="btn-info add-question" value="Add Question"/>
</template>

仅当用户登录的时候才会渲染addQuestion模板。如果用户登出,则不会见到添加新问题的文本框。

我们需要同时更新客户端和服务器端的代码以便实现这一功能。

client/epollclient.js中加入:

Template.addquestion.events({
'click input.add-question' : function(event){
event.preventDefault();
var questionText = document.getElementById("questionText").value;
Meteor.call("addQuestion",questionText,function(error , questionId){
console.log('added question with Id .. '+questionId);
});
document.getElementById("questionText").value = ""; }
});

以上代码中:

  1. 我们首先将点击input事件绑定到add-question类。

  2. 接着我们阻止默认的点击事件,从DOM中获取问题文本。

  3. 然后我们调用Meteor服务器的方法addQuestion。由服务器负责插入、更新、删除数据等有风险的操作。客户端看不到实现,也无法私自修改数据。

现在我们需要修改server/epollserver.js。我们首先定义一个名为Questions的新集合。然后我们会操作这个集合。Meteor使用minimongo作为API接口。参阅Meteor.Collection.documentation查看minimongo支持的所有操作。

Questions = new Meteor.Collection("questions");

Meteor.startup(function () {
// code to run on server at startup
}); Meteor.methods({
addQuestion : function(questionText){
console.log('Adding Question');
var questionId = Questions.insert({
'questionText' : questionText,
'submittedOn': new Date(),
'submittedBy' : Meteor.userId()
});
return questionId;
}
});

现在访问我们的应用然后提交一个新问题:

查看下MongoDB中的数据

> db.questions.find().pretty()
{
"questionText" : "Is Sachin Tendulkar the greatest batsman of all time?",
"submittedOn" : ISODate("2013-11-11T18:23:02.541Z"),
"submittedBy" : "Jnu6oXoAZ2um57rZ8",
"_id" : "nhqvgDcZqgZgLdDB7"
}

问题列表

我们接下来要实现的功能是问题列表。用户不需登录,就可以看到所有问题的列表。

main div中加入:

{{> questions}}

然后添加问题模板:

<template name="questions">
<h2>All Questions</h2>
{{#each items}}
{{> question}}
{{/each}}
</template> <template name="question">
<div>
<p class="lead">
{{questionText}}
<a class="btn btn-small btn-success yes {{#unless currentUser}}disabled{{/unless}}" href="#"><i class="icon-thumbs-up"></i> Yes {{yes}}</a> <a class="btn btn-small btn-danger no {{#unless currentUser}}disabled{{/unless}}" href="#"><i class="icon-thumbs-down"></i> No {{no}}</a>
</p>
</div>
</template>

注意我们使用了unless来确保用户未登录的情况下应用disabled css。

为了获取所有问题,我们需要在客户端使用Question集合来获取所有文本。在client/epollclient.js添加如下代码:

Questions = new Meteor.Collection("questions");

Template.questions.items = function(){
return Questions.find({},{sort:{'submittedOn':-1}});
};

实现投票功能

最后我们需要实现投票功能。我们上面已经在html文件中加入了相关的模板代码,下面我们在client/epollclient.js加入如下代码:

Template.question.events({

    'click': function () {
Session.set("selected_question", this._id);
}, 'click a.yes' : function (event) {
event.preventDefault();
if(Meteor.userId()){
var questionId = Session.get('selected_question');
console.log('updating yes count for questionId '+questionId);
Meteor.call("incrementYesVotes",questionId); }
}, 'click a.no': function(){
event.preventDefault();
if(Meteor.userId()){
var questionId = Session.get('selected_question');
console.log('updating no count for questionId '+questionId);
Meteor.call("incrementNoVotes",questionId);
}
}
});

上面的代码实现了:

  1. 绑定点击事件到问题模板。点击任意问题的时,在session中设置questionId。session提供了一个客户端的全局对象,你可以在里面存储任意的键值对。

  2. 当用户点击Yes按钮时,我们会从session中取得选中的questionId,然后在服务器端调用incrementYesVotes方法。我们使用Meteor.userId()来确保用户已经登录了。

  3. 当用户点击No按钮时,我们在服务器端调用incrementNoVotes函数。

最后我们在server/epollserver.js加入incrementYesVotesincrementNoVotes函数。我们使用Meteor的集合更新功能来增加计数器。

incrementYesVotes : function(questionId){
console.log(questionId);
Questions.update(questionId,{$inc : {'yes':1}});
}, incrementNoVotes : function(questionId){
console.log(questionId);
Questions.update(questionId,{$inc : {'no':1}});
}

这样每次用户点击yes或no按钮之后,计数器会更新。你可以访问 http://localhost:3000 试验一番。

部署Meteor应用

部署Meteor应用有很多种方法。我们可以在Meteor提供的测试服务器上部署,也可以部署到OpenShift。

如果你打算部署到OpenShift上,请参阅Ryan这篇博客

运行以下命令可以部署到Meteor测试服务器:

meteor deploy epoll

应用可以通过 http://epoll.meteor.com/ 访问。

今天就是这些了。欢迎继续反馈。

《30天学习30种新技术》-Day 15:Meteor —— 从零开始创建一个 Web 应用的更多相关文章

  1. 一起学习造轮子(二):从零开始写一个Redux

    本文是一起学习造轮子系列的第二篇,本篇我们将从零开始写一个小巧完整的Redux,本系列文章将会选取一些前端比较经典的轮子进行源码分析,并且从零开始逐步实现,本系列将会学习Promises/A+,Red ...

  2. 一起学习造轮子(一):从零开始写一个符合Promises/A+规范的promise

    本文是一起学习造轮子系列的第一篇,本篇我们将从零开始写一个符合Promises/A+规范的promise,本系列文章将会选取一些前端比较经典的轮子进行源码分析,并且从零开始逐步实现,本系列将会学习Pr ...

  3. 一起学习造轮子(三):从零开始写一个React-Redux

    本文是一起学习造轮子系列的第三篇,本篇我们将从零开始写一个React-Redux,本系列文章将会选取一些前端比较经典的轮子进行源码分析,并且从零开始逐步实现,本系列将会学习Promises/A+,Re ...

  4. Spring MVC 学习笔记2 - 利用Spring Tool Suite创建一个web 项目

    Spring MVC 学习笔记2 - 利用Spring Tool Suite创建一个web 项目 Spring Tool Suite 是一个带有全套的Spring相关支持功能的Eclipse插件包. ...

  5. maven学习3,如何创建一个web项目

      Maven学习 (三) 使用m2eclipse创建web项目   1.首先确认你的eclipse已经安装好m2eclipse的环境,可以参照上两篇Maven学习内容 2.新建一个maven的项目 ...

  6. Go学习【02】:理解Gin,搭一个web demo

    Go Gin 框架 说Gin是一个框架,不如说Gin是一个类库或者工具库,其包含了可以组成框架的组件.这样会更好理解一点. 举个 下面的示例代码在这:github 利用Gin组成最基本的框架.说到框架 ...

  7. 使用IntelliJ IDEA 15和Maven创建Java Web项目

    转自:https://blog.csdn.net/myarrow/article/details/50824793博文链接!

  8. 使用IntelliJ IDEA 15和Maven创建Java Web项目(转)

    1. Maven简介 相对于传统的项目,Maven 下管理和构建的项目真的非常好用和简单,所以这里也强调下,尽量使用此类工具进行项目构建, 它可以管理项目的整个生命周期. 可以通过其命令做所有相关的工 ...

  9. 使用IntelliJ IDEA 15和Maven创建Java Web项目(转)

    转自:https://blog.csdn.net/myarrow/article/details/50824793 1. Maven简介 相对于传统的项目,Maven 下管理和构建的项目真的非常好用和 ...

随机推荐

  1. JsRender (js模板引擎)

    最近学习了一下Jsrender模板渲染工具,非常不错,功能比较强大,官网说他是“简单直观 功能强大 可扩展的 快如闪电”确实如此.总结一下!! jsRender 三个最重要的概念:模板.容器和数据. ...

  2. 07.VUE学习之解决phpstorm不识别ECMASCRIPT6语法的问题

    此时已经识别:

  3. linux 基本指令 归类

    今天 我们来学习一下 最最基础的linux 指令,在我看来 linux的操作就是 增 删 改 查 这四个字. 1 查询 操作用户 woami 2查询登录用户 who am i 2 pwd //查询当前 ...

  4. 2 > 1 and 3 < 4 or 4 > 5 and 2 < 1

    a,b,c,d,e=1,2,3,4,5 m = b >a and c < d n = d > e and b < a y = m or n info = ''' m is %s ...

  5. Codeforces Round #464 (Div. 2) C. Convenient For Everybody

    C. Convenient For Everybody time limit per test2 seconds memory limit per test256 megabytes Problem ...

  6. wcf第三方客户端与wcf服务之间调用入门

    Wcf服务与我们的客户端如何建立联系的呢.本文简单记录一下 1.创建我们的wcf服务程序. 第一个wcf服务库是创建我们的wcf库,运行时会单独来托管我们的程序,而非托管在iis下. 第二个wcf服务 ...

  7. 1717: [Usaco2006 Dec]Milk Patterns 产奶的模式

    1717: [Usaco2006 Dec]Milk Patterns 产奶的模式 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1469  Solved: ...

  8. 将FragmentManger事务添加到返回栈中

    FragmentManger事务添加或替换的 Fragment 后,这时点击 Back 键,程序并不会返回添加之前的状态. 我们可以使用 Transaction 对象的 addToBackStack( ...

  9. Python+Selenium练习篇之9-清除文本方法

    在前面的基础篇的最后一篇,我们用到了输入字符和点击按钮这样的操作.用send_keys()来输入字符串到文本输入框这样的页面元素,用click()来点击页面上支持点击的元素.有时候,我们需要清除一个文 ...

  10. jquery怎样获取html页面中的data-xxx

    $(this).attr("data-id") // will return the string "123"or .data() (if you use ne ...