二、反应式编程,它是活的!

正如你在第 1 章安装和安装中学到的,Meteor 运行在反应式编程模型上。 这意味着您的客户端/浏览器不仅关心显示数据,而且还侦听数据的更改,以便能够对这些更改做出“反应”。 这些数据区域,你的浏览器寻找变化,是称为反应上下文

我们将认真启动我们的借出库应用,为未来章节奠定框架,并使用 Meteor 的内置响应上下文来跟踪和传播我们的应用的变化到所有正在收听的客户端。

在本章中,你将学习:

  • 创建您的第一个真正的应用
  • 使用响应式编程来跟踪和自动更新更改
  • 从多个浏览器窗口探索和测试对数据的更改

创建借阅图书馆

这个世界上有两种人。 那些记得他们把东西借给了谁的人,以及那些两次买了很多东西的人。 如果你是和 UPS 快递司机熟得很熟的人之一,这个应用很适合你!

使用 Meteor,我们将建立一个借阅图书馆。 我们会记录下我们所有的东西,还有借给谁的,这样下次我们不记得把线性压缩扳手放哪了,我们可以简单地查一下上次借给谁了,然后把它拿回来。

当那个朋友问:“你确定借给我了吗?”我们可以说:“是的,史蒂夫,我确定借给你了!” 我看你很喜欢你的数字有线电视,多亏了我慷慨地借给你线性压缩扳手。 你为什么不去找它,这样我也可以在家里享受数字有线电视带来的好处?!”

好吧,好吧,也许史蒂夫也忘了。 也许他是一个肮脏的骗子,他卖了你的扳手来支付他的油炸夹馅面包的习惯。 不管怎样,你已经有了自己的 Meteor 应用,证明你不会发疯。 如果他真的把它卖了,当你在他家看比赛的时候,你至少可以让他和你分享他的收藏。

创建基础应用

我们要做的第一件事是创建基础应用,然后我们可以对其进行扩展以满足我们的需求。

  1. 首先导航到应用文件夹。 这可以在任何地方,但如前所述,我们将在~/Documents/Meteor作为根文件夹:

    ``` $ cd ~/Documents/Meteor

    ```

  2. 现在我们创建出借库应用的基本文件夹结构:

    ``` $ meteor create LendLib

    ```

  3. As usual, we'll get instructions on how to get the application up and running. Let's go ahead and try that, just to make sure that everything was created properly:

    ``` $ cd LendLib $ meteor

    ```

    这将导航到出借库文件夹~/Documents/Meteor/LendLib并运行应用。

  4. Open a browser and navigate to http://localhost:3000/. You should see the following screen:

    Creating the base application

  5. Hello World 不适合,所以我们把它改成 Lending Library。 在你喜欢的编辑器中打开~/Documents/Meteor/LendLib/LendLib.html。 在顶部(第 9 行左右),您将看到模板 HTML 代码片段,它负责我们的欢迎语。 继续将Hello World改为Lending Library:

    <template name="hello"> <h1>Lending Library</h1> {{greeting}} <input type="button" value="Click" /> </template>

  6. Save that change, and the page will refresh:

    Creating the base application

    然而,欢迎消息不在 HTML 文件中。 如果你注意到,它是在一个名为 greeting 的模板函数中找到的:

    {{greeting}}

  7. 我们也来改变一下。 打开~/Documents/Meteor/LendLib/LendLib.js、,对问候模板功能进行如下更改:

    if (Meteor.isClient) { Template.hello.greeting = function () { return "my list."; };

  8. Save the change, and your page will update:

    Creating the base application

创建集合

好的,您只是对静态文件做了一些小的更改,但是我们真正想看到的是一些动态的、响应式的编程和一些实时的 HTML!

我们需要附加一个数据源:用于跟踪我们的项目的东西。 正常情况下,这将是一个相当大的过程,确实,但 Meteor 使它很容易,支持 Minimongo(一个轻版本的 MongoDB)开箱。

提示

要了解更多关于 NoSQL 数据库(特别是 MongoDB,Meteor 内部使用的默认数据库),你可以访问以下网站:

http://en.wikipedia.org/wiki/NoSQL

http://www.mongodb.org/

http://www.packtpub.com/books/all?keys=mongodb

让我们创建我们的集合。 在LendLib.js内,我们想添加以下内容作为第一行,然后保存更改:

var lists = new Meteor.Collection("Lists");

if (Meteor.isClient) {
…

这将在 MongoDB 中创建一个新的集合。 由于它出现在LendLib.js文件中的任何其他内容之前,因此客户端和服务器都可以查看该集合。 我们马上就会看到,它是持久的,一旦值被输入,任何访问页面的客户端都可以检索到它们。

要查看这个持久化对象,我们需要使用 web 页面的控制台。

有趣的浏览器控制台

浏览器控制台是一个调试工具,在大多数现代浏览器中默认可用,或通过插件作为插件。

提示

更多关于在 Chrome 中使用控制台的深入教程,请查看http://developer.chrome.com/extensions/tut_debugging.html。

  1. Since we're using Chrome, the console is available by default. In a browser window pointing to http://localhost:3000/ enter the shortcut key combination [command] + [option] + i or you can right-click anywhere on the page and select Inspect Element:

    Fun with the browser console

    这将打开我们的调试工具。 现在我们想进入控制台。

  2. Click on the Console icon found at the extreme right of the debugging menu bar:

    Fun with the browser console

    现在你将有一个闪烁的光标,你可以检查我们的新收藏了!

  3. Enter the following command in the console and hit Enter:

    ```

    lists

    ```

    你应该得到一个返回的对象,上面写着 Meteor Collection:

    Fun with the browser console

添加一些数据

这意味着我们的更改被接受了,并且我们有了一个新的持久化集合! 它是空白的,但让我们做点什么:

  1. Enter the following commands in the browser console to create a couple of sample categories:

    ```

    lists.insert({Category:"DVDs", items: {Name:"Mission Impossible",Owner:"me",LentTo:"Alice"}}); lists.insert({Category:"Tools", items: {Name:"Linear Compression Wrench",Owner:"me",LentTo: "STEVE"}}); ```

    在每个命令之后,你会得到一个 GUID(类似于f98c3355-18ce-47b0-82cc-142696322a06),这是 Meteor 告诉你项目被正确保存的方式。 作为自然怀疑论者,我们要验证一下。

  2. Enter the following command:

    ```

    lists.findOne({Category: "DVDs"}); ```

    您应该返回一个对象,它旁边有一个可扩展的图标。

  3. Click on that icon to expand, and you should have the following:

    Adding some data

    我们也可以通过输入命令lists.findOne({Category:"Tools"})来检查我们的工具集合,但我们不需要这样做。 这次我们相信"Meteor"是正确进入的。 但是,我们确实希望检查对象是否持久。

    刷新网页。 你的主机将清除,但我们输入的类别已保存在持久 Meteor 收集,所以我们可以再次检查,看看他们是否徘徊。

  4. Enter the following command in the console:

    ```

    lists.find({}).count();

    ```

    该命令查找lists集合中的所有记录,并给出总计数。 如果一切按照计划进行,你应该得到2的计数。

我们在路上了! 我们创建了两个类别,每个类别中都有一项。 我们还验证了lists集合正在从一个会话保存到另一个会话。 现在,让我们看看如何在页面中显示它。

用 HTML 显示集合

现在,我们将看到我们的集合在我们初始化项目时创建的 HTML 页面中开始工作。 这个页面将使用模板,它是响应式的,允许我们在不刷新页面的情况下立即显示对集合所做的更改。 这种响应式编程(其中页面的 DOM 无需刷新即可立即更新)称为Live HTML

提示

要了解更多关于 Live HTML 的信息,请参考 Meteor 文档,网址如下:

http://docs.meteor.com/#livehtml

  1. With ~/Documents/Meteor/LendLib/LendLib.html still open, locate the body tag, and add a new template declaration:

    <body> {{> hello}} <div id="categories-container"> {{> categories}} </div> </body>

    这将创建一个新的div,其中的内容由一个名为categoriestemplate partial填充。

  2. Now, at the very bottom of the page, let's add the skeleton for the categories template partial:

    ```

    ```

    这不会改变页面的外观,,但我们现在有一个template partial,我们可以列出我们的类别。

  3. 让我们输入我们的 section 标题:

    <template name="categories"> <div class="title">my stuff</div> </template>

  4. And now let's get our categories in there:

    ```

    my stuff

    ```

    这创建了类别div,然后我们可以遍历并列出所有类别。 如果我们只有一条记录要处理,代码将是这样的:

    ```

    {{Category}}

    ```

  5. But we need to wrap this into a loop (in this case, an #each statement) so we get all the categories:

    <template name="categories"> <div class="title">my stuff</div> <div id="categories"> {{#each lists}} <div class="category"> {{Category}} </div> {{/each}} </div> </template>

    注意,我们使用{{#each lists}}命令告诉模板“对于列表集合中的每条记录”,命令,然后使用{{Category}}命令“display the category”。

  6. Save these changes, and look at the web page:

    Displaying collections in HTML

    看起来没什么不同。 是的,我们有标题(my stuff),但我们刚刚创建的template类别在哪里?

    为了显示类别,我们还需要完成一个步骤。 目前,我们刚刚创建的模板没有指向任何东西。 换句话说,我们有一个列表集合,我们有一个模板,但是我们没有底层的 JavaScript 函数将它们连接在一起。 我们来解决这个问题。

    ~/Documents/Meteor/LendLib/LendLib.js中我们可以看到一些Template函数:

    ``` Template.hello.greeting = function () {...

    ...

    Template.hello.events = { ... ```

    这些代码块将 JavaScript 函数和对象连接到 HTML hellotemplate。 Meteor 的内置Template对象使这成为可能,我们将遵循相同的模式挂钩我们的类别template

  7. We want to declare to any listening client that the categories template has a lists collection. We do this by entering the following code, just below the Template.hello.events = {...} code block:

    ``` Template.hello.events = { ... };

    Template.categories.lists = function () { };

    ```

    提示

    模板声明必须在if (Meteor.isClient) {...}代码块中,因此客户端将选择更改,而服务器将忽略它。

  8. We've now declared the lists collection for all templates to use, and we can have the function return the results from a Meteor.Collection query. We do that using the find() command:

    Template.categories.lists = function () { return lists.find({}, {sort: {Category: 1}}); };

    该代码将查找lists集合中的每一条记录,并将按Category(名称)对结果进行排序。 保存这些更改,你将看到一个填充的类别列表:

    Displaying collections in HTML

清洁

我们正在快速接近一个工作应用,我们希望它看起来超级闪亮和干净。 让我们在我们的代码中做一些清理,并添加一些 CSS 到,使事情更易读:

  1. We don't need the greeting anymore. Let's get rid of that. Remove the following highlighted lines from LendLib.html and save the page:

    ```

    {{> hello}}
    {{> categories}}

    Lending Library

    {{greeting}} ```

    我们希望保留模板。 LendLib.js 中的 hello 声明,作为参考。 我们现在将它们注释掉,当它们不再需要时将它们删除:

    ``` /*

    Template.hello.greeting = function () { ... };

    Template.hello.events = { ... };

    */

    ```

  2. Now, let's add the Twitter Bootstrap Framework, which gives us a lot of style without much effort:

    1. 使用终端窗口,在/LendLib/:

      ``` $ mkdir ~/Documents/Meteor/LendLib/client

      ```

      中创建client文件夹 2. Download the latest Bootstrap framework at http://twitter.github.com/bootstrap/assets/bootstrap.zip and extract the archive into the ~/Documents/Meteor/LendLib/client folder.

      因为 Meteor 将读取和使用放入应用文件夹中的每个文件,我们想要消除冗余文件。 我们不必过分担心效率,但有些事情是可耻的,把那么多多余的代码放在那里就像欣赏《暮光之城》电影一样。

    2. 导航到引导文件夹:

      ``` $ cd ~/Documents/Meteor/LendLib/client/bootstrap

      ```

    3. Delete the unneeded files:

      ``` $ rm js/bootstrap.js $ rm css/bootstrap.css $ rm css/bootstrap-responsive.css

      ```

      提示

      如果你知道你在用 Bootstrap 做什么,你可以复制imagesmin.jsmin.css文件,而不是按照前面的说明。

    在所有这些改变之后,你的 UI 应该非常干净和简单:

    Cleaning up

  3. 让我们快速地使它更清晰和可读。 在LendLib.html中,让我们改变标题从div标签到h2标签:

    <template name="categories"> <h2 class="title">my stuff</h2>

  4. And let's turn categories into a pretty button group:

    <div id="categories" class="btn-group"> {{#each lists}} <div class="category btn btn-inverse"> {{Category}} </div> {{/each}}

    这给了我们一个清晰、整洁的页面:

    Cleaning up

制造反应

随着基本模板和集合的创建,以及 Meteor 将我们的lists集合放入反应上下文,我们现在可以继续观察反应编程模型的行动。

导航到我们的借阅库页面在http://localhost:3000/,并打开浏览器控制台窗口。

在控制台中输入以下命令:

> lists.insert({Category:"Fraggles"});

您将立即看到页面更新。 但是请注意,这一次,整个页面没有刷新! 这是因为在内部,Meteor 正在跟踪我们的响应上下文(在这种情况下,lists集合)的变化,而template在做出变化后立即被更新。

让我们再做一些修改。 再次输入相同的Fraggles命令:

> lists.insert({Category:"Fraggles"});

就像之前一样,一个新的Fraggles按钮立即出现:

Creating a re

但是我们现在有太多的 Fraggles 分类了。 有很多 Fraggles,但除非你是一些古怪的收藏家,你不需要两个类别。 所以让我们移除它们:

*```

lists.remove({Category:"Fraggles"})

该命令查找所有`Category = "Fraggles"`的记录并删除它们。

为所有收藏品添加一个收集条目可能会更好,所以让我们这样做:

lists.insert({Category:"Collectibles"})

如您所见,更改是立即进行的,不需要刷新页面。

# 多个客户端

好东西应该分享。 Meteor 做到了这一点,我们也将看到,响应式编程模型允许我们跨多个客户端实时分享更新。

与您的 Chrome 网页仍然打开`http://localhost:3000/`打开一个新的浏览器标签,并导航到同一页。

### 提示

如果你真的想变得更有趣,你可以在多个浏览器(Firefox、Opera 或 Safari)上进行同样的实验——每个会话都是实时的和响应的!

现在您打开了两个客户端,它们模拟不同的人在不同的位置、使用不同的计算机打开应用。 Meteor 的反应模型允许你对所有客户一视同仁,一个客户做出的改变会传播给所有其他客户。

盯着新的第二个浏览器,在浏览器#1 的控制台中输入以下命令:

lists.insert({Category:"Vinyl Records"})

```

你会注意到,更改会传播到两个浏览器,并且没有页面刷新:

Multiple clients

您可以随意进行任何额外的集合、删除或重命名,等等。 进行一些试验,并注意如何立即对每个侦听客户机进行这些更改。 Meteor 在一个非常强大的范例下运行,在下一个章节中,我们将能够确切地看到为什么这是一个对 web 应用开发如此重要和破坏性的变化。

小结

在本章中,你已经成功地为你的新 Meteor 应用创建了框架。 您已经亲眼看到了创建新项目的速度有多快,并且仅用几行代码就创建了一些主要的数据库和模板功能。 您已经看到了实时 HTML 和响应式编程的实际应用,现在可以进一步深入研究 Meteor 引擎了。 你已经征服了冰山一角,我的朋友。 休息一下,喝杯冷咖啡,准备迎接更多精彩的 Meteor 吧!*