返回介绍

Day 15:Meteor - 从零开始创建一个 Web 应用

发布于 2025-01-31 20:47:30 字数 17488 浏览 0 评论 0 收藏 0

到目前为止我们讨论了 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/ 访问。

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

原文: Day 15: Meteor - Building a Web App From Scratch in Meteor
翻译整理: Segmentfault

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文