Meteor教程:使用Meteor构建实时Web应用程序

本文概述

Meteor是用于Web和移动应用程序的完整JavaScript框架。自2011年以来, 它已经成为Meteor开发者的良好声誉, 因为它是理想的, 易于使用的快速原型解决方案。但是, 最近开发人员意识到Meteor不再只是用于原型制作:它已经准备好用于商业开发。凭借其提供的大量软件包, 其所依赖的坚实的mongoDB / node.js基础以及所提供的编码灵活性;通过Meteor, 可以轻松构建强大, 安全的实时Web应用程序, 处理从浏览器应用程序到服务器或数据库的所有内容。

Meteor教程

这份Meteor教程将引导你完成在Meteor中制作基本的Web应用程序的过程-一个简单的目录, 使用户可以登录和管理书籍列表。

为什么要使用Meteor?简短的答案是:”因为Meteor很有趣”。它使开发Web应用程序变得简单。它很容易学习, 并且比同步数据和服务页面的基础知识更能使你专注于应用程序的功能。

它还具有许多方便的内置行为。Meteor自动执行实时更新, 因此数据更改会立即显示在浏览器窗口中, 甚至应用程序本身的代码更改也将被”实时”推送到所有浏览器和设备。 Meteor具有内置的延迟补偿, 易于部署, 并且具有易于安装的”软件包”, 可以处理各种功能。

尽管它是一个相对较新的框架, 但许多创业公司已经在构建Meteor应用, 包括诸如Respondly和Telescope之类的相对大型的服务。

Meteor的安装和脚手架

在* nix系统上安装Meteor是一种方法:

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

尽管仍然没有官方支持, 但是他们的Windows预览版进展顺利。有传言称Windows支持将随版本1.1一起提供, 该版本将于2015年4月或5月发布。你可能希望从像Meteor这样的智能框架中进行引导, 引导应用程序需要调用一行命令:

meteor create book-list

这将创建一个名为” book-list”的目录, 并使用一些样板代码和相关代码填充该目录。要运行该应用程序, 请输入新创建的目录并执行:

meteor

在网络浏览器中打开http:// localhost:3000, 你将看到以下内容:

欢迎来到Meteor

你还可以在MeteorPad上查看我们应用程序的”版本0″, 该网站类似于JSFiddle for Meteor:图书清单:默认应用程序

Meteor将其视图存储在HTML文件中。如果打开” book-list.html”, 我们将看到:

<head>
  <title>book-list</title>
</head>

<body>
  <h1>Welcome to Meteor!</h1>

  {{> hello}}
</body>

<template name="hello">
  <button>Click Me</button>
  <p>You've pressed the button {{counter}} times.</p>
</template>

Meteor使用模板引擎” Blaze”来呈现来自这些HTML文件的响应。使用Handlebars.js(或其他类似的模板引擎)的人都应该熟悉双括号, 并且它们在此处具有类似的功能。 Blaze检查每对双括号内的表达式, 并用这些表达式产生的值替换每个表达式。

这个简单的示例程序只有两个双括号表达式:

  • 第一个” {{> hello}}”告诉Blaze包含一个名为” hello”的模板。该模板在文件底部的<template name =” hello”>部分中定义。

  • 第二个” {{counter}}”稍微复杂一些。要查看此”计数器”值的来源, 我们需要打开” book-list.js”:

if (Meteor.isClient) {
  // counter starts at 0
  Session.setDefault('counter', 0);

  Template.hello.helpers({
    counter: function () {
      return Session.get('counter');
    }
  });

  Template.hello.events({
    'click button': function () {
      // increment the counter when button is clicked
      Session.set('counter', Session.get('counter') + 1);
    }
  });
}

if (Meteor.isServer) {
  Meteor.startup(function () {
    // code to run on server at startup
  });
}

有些事情需要在这里解释。首先, 分为” if(Meteor.isClient)”和” if(Meteor.isServer)”。回想一下Meteor是一个全栈框架, 因此你编写的代码在服务器和客户端上都可以运行。这些条件允许我们限制:第一个块仅在客户端上运行, 第二个块仅在服务器上运行。

其次, 有一个” Session.setDefault”调用-这会在浏览器中初始化一个称为” counter”的会话变量。会话变量的行为有点像Meteor中的全局变量(无论好坏)。但是, 该会话变量未直接显示在” {{counter}}”中。取而代之的是, “计数器”表达式是”帮助程序”, 是在” Template.hello.helpers”部分中定义的。该帮助程序仅获取会话变量的值并返回它。

请注意, 助手是”反应性的”。这意味着, 只要会话变量发生更改, Meteor都会自动重新运行引用它的此辅助函数, 并且Blaze会自动使用新内容更新浏览器。

客户端代码还通过” Template.hello.events”监视事件。我们通过事件类型和选择器(在本例中为”单击按钮”)识别事件, 然后告诉Meteor该事件应该做什么。在这种情况下, 会话变量将增加, 这将重新运行帮助器功能, 然后重新呈现内容。

显示静态数据

所有这一切都很好, 但是这不是我们想要本教程使用的Meteor应用程序。

让我们开始调整此应用程序-我们将显示静态的硬编码书籍列表。现在, 我们将书籍列表存储在会话变量中。在” isClient”代码中, 我们将在呈现bookList模板后立即使用” Template.hello.rendered”设置会话变量:

  Template.hello.rendered = function() {
    Session.setDefault('books', [
      {title: "To Kill a Mockingbird", author: "Harper Lee"}, {title: "1984", author: "George Orwell"}, {title: "The Lord of the Rings", author: "J. R. R. Tolkien"}, {title: "The Catcher in the Rye", author: "J. D. Salinger"}, {title: "The Great Gatsby", author: "F. Scott Fitzgerald"}
    ]);
  };

然后, 我们在” hello”模板中使用新的帮助程序返回该会话变量:

  Template.hello.helpers({
    books: function () {
      return Session.get('books');
    }
  });

并通过” hello”模板中的变量插值将其显示在屏幕上:

<template name="hello">
  <h3>Here are your books:</h3>
  {{ books }}
</template>

你可以在Meteorpad上查看此代码的实际操作:图书清单:显示会话变量

首先要注意的是, Meteor服务器会自动检测到对我们代码库的更改, 将新代码推送到客户端, 并提示客户端重新加载。即使部署了应用程序, 我们也可以部署更改并通过热代码推送自动更新客户端。

自动部署变更

到目前为止, 这是我们得到的:

数据显示不正确

糟糕, 我们显示的数据有误。 Blaze在这里获得了准确性的要点(为什么, 是的, 它是一个对象数组), 但是如果我们想以一种有用的方式显示图书清单, 我们将不得不变得更加聪明。幸运的是, Blaze使使用” #each”指令处理数据数组变得非常容易:

 <h3>Here are your books:</h3>
  <UL>
    {{#each books}}
        <LI><i>{{title}}</i> by {{author}}</LI>
    {{/each}}
  </UL>

在Blaze中, “#each”的工作原理类似于Angular的” ng-repeat”指令-遍历数组结构, 将当前上下文设置为数组中的当前对象, 并在” {{#each …}}”。这是我们现在的书籍清单:

正确的数据显示

在MeteorPad上:正确显示会话变量

一些清理

在继续之前, 我们先整理一下代码。

Meteor为组织代码库留出了极大的空间。如你所见, 只有几条严格的规则:无论将HTML和JavaScript放在哪里, Meteor都能找到它。这种灵活性很好, 但这确实意味着你更有责任以有意义的方式来组织代码, 因此你不会因维护巨大的混乱状态而陷入困境。

首先, 让我们将” hello”模板重命名为有意义的东西, 例如” bookList”, 然后将样板HTML替换为:

<head>
  <title>book-list</title>
</head>

<body>
  {{> bookList}}
</body>

<template name="bookList">
  <h3>Here are some books:</h3>
  <UL>
    {{#each books}}
        <LI><i>{{title}}</i> by {{author}}</LI>
    {{/each}}
  </UL>
</template>

其次, 让我们将”客户端”和”服务器”部分分成单独的文件。在我们的应用程序目录中, 我们将设置一个” client”子目录和一个” server”子目录-Meteor会自动知道在客户端上运行” / client /”文件, 并在客户端上运行” / server /”文件服务器。将模板代码放在以该模板命名的JavaScript文件中是一种很好的约定, 因此让我们将客户端代码放入” client / bookList.js”中。我们可以将当前为空的服务器启动代码放在” server / startup.js”中。最后, 让我们将””模板代码移至” client / bookList.html”。

请注意, 即使进行了所有这些切换, Meteor仍将自动找到我们所有的HTML和JavaScript文件。只要文件位于” / client /”中的某个位置, Meteor就会知道要在客户端上运行该文件。只要文件位于” / server /”中的某个位置, Meteor就会知道要在服务器上运行它。再次, 由开发人员负责将事情保持井井有条。

因此, 现在我们的代码应如下所示:

book-list.html:

<head>
  <title>book-list</title>
</head>

<body>
  {{> bookList}}
</body>

client / bookList.html:

<template name="bookList">
  <h3>Here are some books:</h3>
  <UL>
    {{#each books}}
        <LI><i>{{title}}</i> by {{author}}</LI>
    {{/each}}
  </UL>
</template>

client / bookList.js:

Template.bookList.rendered = function() {
  Session.setDefault('books', [
    {title: "To Kill a Mockingbird", author: "Harper Lee"}, {title: "1984", author: "George Orwell"}, {title: "The Lord of the Rings", author: "J. R. R. Tolkien"}, {title: "The Catcher in the Rye", author: "J. D. Salinger"}, {title: "The Great Gatsby", author: "F. Scott Fitzgerald"}
  ]);
};

Template.bookList.helpers({
  books: function () {
    return Session.get('books');
  }
});

服务器/startup.js:

Meteor.startup(function () {
    // code to run on server at startup
});
 ~~~

Check it out on MeteorPad: [Initial Code Cleanup](http://meteorpad.com/pad/MwvMcsBAzfbWwEXp3/Book-List:%20Initial%20Code%20Cleanup)

Verify that everything's running correctly by checking the browser window and then we're good to move on to the next step.

## Using the Database in Meteor

The Meteor server runs on top of a MongoDB database. In this section of our tutorial, we will move the static list of books out of the session variable and into that database.

First, delete the Template.bookList.rendered code, so that we're no longer putting stuff into that session variable. Next, we should add that list of books to the database as fixture data when the server initializes. As you'd expect for MongoDB, Meteor stores data in "collections". So, we'll create a new collection for our books. To keep things simple we will name it "books". It turns out that both the client and the server will want to know about this collection, so we'll put this code in a new subfolder: "/lib/". Meteor knows automatically that files in "/lib/" run on the client and the server.

We'll create a file called "lib/collections/books.js", and give it just one line of code:

~~~ js
Books = new Meteor.Collection("books");

在指向http:// localhost:3000的浏览器窗口中, 转到开发人员控制台并检查” Books”的值。现在应该是Mongo收藏了!尝试运行” Books.find()。fetch()”, 你会得到一个空数组-这很有意义, 因为我们还没有添加任何书籍。我们可以尝试在控制台中添加项目:

Books.insert({title: "To Kill a Mockingbird", author: "Harper Lee"})

在控制台中添加内容非常繁琐。相反, 我们将进行设置, 以便在服务器启动时自动将灯具数据插入数据库。因此, 让我们回到” server / startup.js”, 并添加以下内容:

Meteor.startup(function () {
    if (!Books.findOne()) {
        Books.insert({title: "To Kill a Mockingbird", author: "Harper Lee"});
        Books.insert({title: "1984", author: "George Orwell"});
        Books.insert({title: "The Lord of the Rings", author: "J. R. R. Tolkien"});
        Books.insert({title: "The Catcher in the Rye", author: "J. D. Salinger"});
        Books.insert({title: "The Great Gatsby", author: "F. Scott Fitzgerald"});
    }
});

现在, 当服务器启动时, 如果没有数据, 我们将添加灯具数据。我们可以通过返回终端, 停止Meteor服务器并运行以下命令来验证这一点:

meteor reset

注意:你几乎不需要此命令, 因为它会重置-即清除-Meteor正在使用的数据库。如果你的Meteor应用程序在数据库中有任何用户数据, 则不应运行此命令。但是在这种情况下, 我们只会清除所有可用的测试数据。

现在, 我们将再次启动服务器:

meteor

在启动时, Meteor将运行启动例程, 查看数据库为空, 然后添加灯具数据。此时, 如果转到控制台并键入” Books.find()。fetch()”, 我们将获得之前拥有的五本书。

该步骤只剩下在屏幕上显示书籍。幸运的是, 这就像替换” return Session.get(‘books’);”一样简单。在”书籍”助手中包含以下内容:

return Books.find();

而且我们重新营业!该应用程序现在从数据库游标而不是从会话变量显示数据。

在MeteorPad上签出:移至数据库

安全问题

我首先说:”不要这样做”。

如果有人在浏览器中启动该应用程序, 转到控制台并输入” Books.remove({})”, 你认为会发生什么?

答案是:他们会清除收藏集。

因此, 这是一个非常大的安全问题-我们的用户对我们的数据库拥有过多的访问权限。任何客户端都可以访问整个数据库。不仅如此, 任何客户端都可以对整个数据库进行任何更改, 包括” .remove({})”数据清除。

这不好, 所以让我们对其进行修复。

Meteor使用所谓的”包”来添加功能。 Node.js是什么模块, Ruby是gem, 包是Meteor的捆绑功能。有用于各种事物的软件包。要浏览所有可用内容, 请查看mood.js。

我们使用”Meteor创建”制作的默认Meteor应用程序包括两个名为”自动发布”和”不安全”的软件包。第一个软件包使客户端可以自动访问整个数据库, 第二个软件包使用户可以对该数据库执行任何操作。

让我们删除那些。我们可以通过在app目录中运行以下命令来做到这一点:

meteor remove autopublish insecure

完成后, 你会看到屏幕上的书单数据消失了(因为你再也无法访问它了), 如果尝试调用” Books.insert”, 则会收到错误消息:”插入失败: 拒绝访问”。在MeteorPad上:安全性过大

如果你没有从此Meteor教程中获取任何其他信息, 请记住以下几点:在部署Meteor应用程序时, 请确保删除自动发布且不安全的软件包。Meteor有很多很好的安全预防措施, 但是如果你保留这两个软件包, 所有这些措施都是徒劳的。

那么, 如果Meteor有这种安全隐患, 为什么它们会自动包含这些软件包呢?原因是, 特别是对于初学者来说, 这两个软件包使入门变得更容易-你可以从浏览器的控制台轻松调试和调整数据库。但是, 最好是尽快放弃自动发布并确保其不安全。

发布和订阅

因此, 我们修复了这个巨大的安全漏洞, 但引入了两个问题。首先, 我们现在无权访问数据库。其次, 我们无法与数据库进行交互。

让我们在这里解决第一个问题。Meteor通过让服务器”发布”数据库的一个子集, 并使客户端”订阅”该发布, 来提供对数据库的安全访问。

首先, 让我们创建” /server/publications.js”:

Meteor.publish('books', function() {
  return Books.find({});
});

我们将创建” /client/subscriptions.js”:

Meteor.subscribe('books');

在MeteorPad上签出:发布和订阅

服务器”发布”可以访问所有数据的游标, 而客户端在另一端”订阅”它。客户端使用此订阅以所有游标的数据填充数据库的镜像副本。当我们访问” Books.find()。fetch()”时, 我们看到了所有五个对象, 并且看到它们像以前一样显示在屏幕上。

现在的区别是, 限制客户端可以访问的内容确实非常容易。尝试将发布” find()”切换到数据的子集:

Meteor.publish('books', function() {
  return Books.find({}, {limit:3});
});

现在, 客户只能看到五本书中的三本书, 而没有其他方法。这不仅对安全性大有裨益(我看不到其他人的银行帐户), 而且你可以用它来打包数据并避免客户过多。

添加新书

我们已经看到了如何以一种有限的, 安全的方式为客户提供对数据库的读访问权限。现在, 让我们看第二个问题:如何让用户更改数据库而不允许他们做自己想做的事情?摆脱不安全的软件包可以使客户完全无法访问-让我们尝试再次允许添加图书。在Meteor中, 我们通过向服务器添加”方法”来做到这一点。让我们在” /lib/collections/books.js”中添加一种方法, 即添加新书:

Meteor.methods({
  addBook: function(bookData) {
    var bookID = Books.insert(bookData);
    return bookID;
  }
});

如你所见, 这将使用” bookData”(在本例中为具有” title”和” author”字段的对象)并将其添加到数据库中。你的客户端重新加载后, 我们便可以从客户端调用此方法。你可以转到控制台并输入如下内容:

Meteor.call('addBook', {title: "A Tale of Two Cities", author: "Charles Dickens"})

和presto!你会在书单上看到另一本书。使用控制台非常笨拙, 因此, 让我们继续在” bookList”模板的末尾添加一个简单的表单, 使我们可以添加新书:

<form class="add-book">
	Title:<br>
	<input type="text" name="title">
	<br>
	Author:<br>
	<input type="text" name="author">
	<input type="submit" value="Add Book">
</form>

我们可以使用事件用例将其连接到JavaScript, 就像在原始测试应用程序中那样:

Template.bookList.events({
  "submit .add-book": function(event) {
    event.preventDefault(); // this prevents built-in form submission
    Meteor.call('addBook', {title: event.target.title.value, author: event.target.author.value})
  }
});

你可以在MeteorPad上看到此操作:方法

当我们不安全并自动发布时, 客户端可以访问和更改整个数据库。现在, 不安全和自动发布已不复存在, 但是有了发布, 订阅和方法, 客户端可以以受控方式访问数据库并与其进行交互。

附带说明:你还可以使用”允许和拒绝规则”解决Meteor中的安全问题。你可以在discovermeteor.com上找到有关这些内容的更多信息以及一些我更喜欢上述方法的原因。

用户认证

看起来好像我们刚刚回到起点, 但是有一个重要的区别:现在限制我们对数据库的访问确实很容易。要查看其工作原理, 请尝试将用户添加到此应用中。我们将在我们的应用程序中添加一个登录系统, 然后让所有客户不要使用一个系统范围的书籍清单, 而是让每个用户只能添加或阅读自己的书籍清单。

转到app目录, 并安装两个软件包:

meteor add accounts-ui accounts-password

那里。你刚刚将登录系统添加到了该应用中。现在, 我们只需要将登录UI添加到book-list.html。将此单行放在正文顶部:

{{> loginButtons}}

你应该在屏幕顶部看到一个登录提示:

登录提示

请注意, 如果你单击登录链接, 它将要求你提供电子邮件地址和密码。通过创建包含以下内容的” /client/config.js”, 我们可以将其切换为简单的用户名/密码登录系统:

Accounts.ui.config({
    passwordSignupFields: "USERNAME_ONLY"
});

此时, 你可以在控制台中键入” Meteor.userId()”, 它将返回” null”。你可以尝试单击链接来创建一个帐户。现在, 调用” Meteor.userId()”应返回一个ID字符串。服务器可以访问同一条信息(如” this.userId”), 因此, 使”添加图书”方法强制用户登录并包含userID字段很简单:

Meteor.methods({
  addBook: function(bookData) {
    if (this.userId) {
        bookData.userID = this.userId;
        var bookID = Books.insert(bookData);
        return bookID;
    }
  }
});

现在剩下的就是限制客户端, 只显示该用户添加的书籍。我们利用出版物的功能来缩小客户可以访问的范围:

Meteor.publish('books', function() {
    return Books.find({userID: this.userId});
});

现在, 出版物仅从该特定用户中查找书籍。我们甚至可以从Blaze表达式中以” {{currentUser}}”访问userId;并且我们可以将其与” {{#if}}”指令(完全按照你的想法执行)一起使用, 以仅在用户登录时显示数据:

<template name="bookList">
  {{#if currentUser}}
    <h3>Here are your books:</h3>
    <UL>
      {{#each books}}
          <LI><i>{{title}}</i> by {{author}}</LI>
      {{/each}}
    </UL>
    <form class="add-book">
    	Title:<br>
    	<input type="text" name="title">
    	<br>
    	Author:<br>
    	<input type="text" name="author">
    	<input type="submit" value="Add Book">
    </form>
  {{else}}
    <h3>Please log in to see your books</h3>
  {{/if}}
</template>

在MeteorPad上查看最终结果:用户

部署方式

现在, 我们可以将此Meteor应用程序部署到Internet。为此, 请转到终端上的应用程序目录并执行:

meteor deploy <your app's name>.meteor.com

确保用应用程序实例的实际简称替换” <你的应用程序名称>”。运行此命令将提示你设置Meteor帐户, 然后将你的新应用程序放置在Meteor的测试服务器上, 以便你可以在Internet上进行试用。

对于快速演示, 此meteor.com解决方案是你所需要的。Meteor团队尚未宣布对其服务器上的存储或带宽有任何明确的限制。唯一值得注意的限制是, 如果你的应用长时间不使用, 该网站将花费几秒钟的时间为下一个用户启动。

就是说, meteor.com并非旨在用于商业用途。但是, 当你投入生产时, 可以使用Modulus和Digital Ocean等平台即服务的公司来简化Meteor应用程序的部署。如果你想将Meteor应用程序部署到自己的服务器上, “Meteor升级”也使该过程变得简单。

下一步

恭喜你!为了学习Meteor, 你现在已经制作并部署了一个非常简单的实时Meteor网络应用程序。显然, 这只是进入整个特性和功能世界的很小的第一步。如果你喜欢到目前为止所看到的内容, 我强烈建议你使用David Turnbull撰写的《你的第一个Meteor应用程序》, 它会引导读者创建一个更复杂的应用程序, 并提供有关Meteor功能的更多信息。它可以作为Kindle书以低价获得, 也可以在Turnbull网站上免费获得PDF。

你还需要探索可用于Meteor的软件包。十分之九的答案是”如何在Meteor中执行<x>”?是”有一个包装”。 “如何为我的应用程序添加路由?”你使用铁路由器。 “如何提供RESTful API?”你使用RESTivus。 “如何包括单元测试?”你使用Velocity。 “如何添加架构验证?”你使用Collection2。通过查看所有可用的软件包, 你可以很容易在Atmosphere.js上迷路。

为什么不使用Meteor?

从本教程可以看到, Meteor编写应用程序既简单又有趣, 但是如果我不提缺点的话, 我将不屑一顾。

Meteor还相对不成熟。去年10月达到1.0, 这导致了一些问题。如果你想做些晦涩的事, 可能没有人为此功能编写过软件包。确实存在的软件包更有可能存在错误, 仅是因为它们存在的时间不足以解决所有问题。

Meteor的缩放比例也可能是未知的。有许多Meteor网站可以扩展到合理数量的用户, 但是很少有非常大的网站-拥有Facebook或LinkedIn的网站数量很少, 只有数以千万计的用户。

但是, 对于大多数应用程序来说, Meteor是一个完美的选择, 因为它有机会减少开发时间并参与一些伟大的事情的开始。

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?