十一、构建我们自己的包

在本章中,我们将学习如何构建自己的包。 编写包可以让我们创建可以在许多应用之间共享的封闭功能组件。 在本章的下半部分,我们将在 Meteor 的第三方包库 Atmosphere 上发布我们的应用,网址为https://atmospherejs.com

在本章中,我们将涵盖以下主题:

包装的结构

一个包是一个 JavaScript 文件包,它只向 Meteor 应用暴露特定的变量。除了在 Meteor 应用中,包文件将按照我们指定的加载顺序加载。

每个包都需要一个包含该包配置的package.js文件。 在这样的文件中,我们可以添加名称、描述和版本,设置加载顺序,并确定哪些变量应该暴露给应用。此外,我们可以为我们的包指定单元测试来测试它们。

package.js文件的示例如下:

Package.describe({
  name: "mrt:moment",
  summary: "Moment.js, a JavaScript date library.",
  version: "0.0.1",
  git: "https://..."
});

Package.onUse(function (api, where) {
  api.export('moment');

  api.addFiles('lib/moment-with-langs.min.js', 'client');
});

Package.onTest(function(api){
  api.use(["mrt:moment", "tinytest"], ["client", "server"]);
  api.addFiles("test/tests.js", ["client", "server"]);
});

我们可以按照自己的意愿在我们的包中构建文件和文件夹,但一个良好的基础是以下安排:

The structure of a package

  • tests:这个包含包的单元测试和tests.js文件
  • lib:该包含包使用的第三方库
  • README.md:这包含了如何使用包装的简单说明
  • package.js:这个包含包的元数据
  • myPackage.js:这些是包含包代码的一个或多个文件

为了测试一个包,我们可以使用 Meteor 的tinytest包,这是一个简单的单元测试包。 如果我们有测试,我们可以使用以下命令运行它们:

$ meteor test-packages <my package name>

这将启动 Meteor 应用在http://localhost:3000,运行我们的包测试。 要了解如何编写包,请参阅下一章。

创建我们自己的软件包

为了创建我们自己的包,我们将使用我们的ReactiveTimer对象,该对象是我们在第 9 章Advanced react中构建的:

  1. 我们进入终端,在应用的文件夹中运行以下命令:
  2. 这将创建一个名为packages的文件夹,其中有一个reactive-timer文件夹。 在reactive-timer文件夹中,Meteor 已经创建了一个package.js文件和一些示例包文件。
  3. 现在我们可以删除reactive-timer文件夹内的所有文件,除了package.js文件。
  4. 然后,我们将在第 9 章Advanced Reactivity中创建的my-meteor-blog/client/ReactiveTimer.js文件移动到新创建的reactive-timer包文件夹中。
  5. Lastly, we open the copied ReactiveTimer.js file and remove the following lines:

    timer = new ReactiveTimer(); timer.start(10);

    稍后,我们将在应用本身而不是包文件中实例化timer对象。

现在我们应该有一个带有默认的package.js文件和ReactiveTimer.js文件的简单文件夹。 快到了! 我们只需要配置我们的包,我们准备在我们的应用中使用它。

添加包元数据

为了添加包的元数据,我们打开名为package.js的文件,并添加以下代码行:

Package.describe({
  name: "meteor-book:reactive-timer",
  summary: "A simple timer object, which can re-run reactive functions based on an interval",
  version: "0.0.1",
  // optional
  git: "https://github.com/frozeman/meteor-reactive-timer"
});

这将向包中添加名称、描述和版本。

注意,包名的命名空间中有作者的名字。 这样,具有相同名称的包可以通过其作者的名字来区分。 在我们的例子中,我们选择了meteor-book,这不是一个真正的用户名。 要发布这个包,我们需要使用真正的 Meteor 开发者用户名。

Package.describe()函数之后是实际的包依赖项:

Package.onUse(function (api) {
  // requires Meteor core packages 1.0
  api.versionsFrom('METEOR@1.0');

  // we require the Meteor core tracker package
  api.use('tracker', 'client');

  // and export the ReactiveTimer variable
  api.export('ReactiveTimer');

  // which we find in this file
  api.addFiles('ReactiveTimer.js', 'client');
});

在这里,我们定义了这个包应该使用的 Meteor 核心包的版本:

  • With api.use(), we define an additional package (or packages) this package depends on. Note that these dependencies won't be accessible to the app itself, which uses this package.

    注释

    此外,还有api.imply(),这不仅使另一个包在包的文件中可用,而且还将其添加到 Meteor 应用本身,以便应用代码可以访问它。

  • If we use a third-party package, we must specify the minimum package version as follows:

    ``` api.use('author:somePackage@1.0.0', 'server');

    ```

    注释

    我们也可以传入第三个参数,{weak: true},以指定依赖包只会被使用,如果它已经被开发人员添加到应用。 这可以用于在出现其他包时增强一个包。

  • In the second parameter of the api.use() function, we can specify whether to load it on the client, server, or both, using an array:

    ``` api.use('tracker', ['client', 'server']);

    ```

    提示

    我们真的不需要导入Tracker包,因为它已经是 Meteor 的核心meteor-platform包的一部分(默认添加到任何 Meteor 应用); 我们这样做是为了举个例子。

  • We then use api.export('ReactiveTimer') to define which variable of the package should be exposed to the Meteor app using this package. Remember that we created the ReactiveTimer object inside the ReactiveTimer.js file using the following lines of code:

    ReactiveTimer = (function () { ... })();

    注释

    注意,我们没有使用var来创建变量。 通过这种方式,它可以在包的所有其他文件中访问,也可以暴露给应用本身。

  • Lastly, we tell the package system which files belong to the package, using api.addFiles(). We can have multiple calls of api.addFiles() one after the other. This order will then specify the loading order of the files.

    在这里,我们可以再次告诉 Meteor 加载文件的位置在客户端,服务器,或者两者都使用['client', 'server']

    在这种情况下,我们只在客户端提供ReactiveTimer对象,因为 Meteor 的响应函数只存在于客户端。

    注释

    如果你想查看api对象的完整方法列表,请查看 Meteor 的文档http://docs.meteor.com/#packagejs

添加包

复制一个包文件夹到my-meteor-blog/packages文件夹并不足以告诉 Meteor 使用包。 我们还需要采取其他步骤:

  1. 要添加这个包,我们需要从终端进入我们的应用的文件夹,退出任何当前正在运行的meteor实例,并运行以下命令:
  2. 然后我们需要在应用中实例化ReactiveTimer对象。为此,我们将以下代码添加到my-meteor-blog/main.js文件:
  3. 现在我们可以再次使用$ meteor启动 Meteor 应用,并在http://localhost:3000打开我们的浏览器。

我们应该看不到任何区别,因为我们只是用meteor-book:reactive-timer包中的ReactiveTimer对象替换了应用中已经存在的ReactiveTimer对象。

要查看计时器的运行情况,我们可以打开浏览器的控制台并运行以下代码片段:

Tracker.autorun(function(){
    timer.tick();
    console.log('timer run');
});

这应该每 10 秒记录一次timer run日志,向我们显示包正在实际工作。

向公众发布我们的软件包

向世界发布包非常容易,但是对于使用我们包的人来说,我们应该添加一个自述文件,这样他们就可以知道如何使用我们的包。

在我们之前创建的包文件夹中创建一个名为README.md的文件,并添加以下代码片段:

# ReactiveTimer

This package can run reactive functions in a given interval.
## Installation

    $ meteor add meteor-book:reactive-timer

## Usage

To use the timer, instantiate a new interval:

    var myTimer = new ReactiveTimer();

Then you can start an interval of 10 seconds using:

    myTimer.start(10);

To use the timer just call the following in any reactive function:

    myTimer.tick();

To stop the timer use:

    myTimer.stop();

正如我们所看到的,这个文件使用了 markdown 语法。 这样,它将在 GitHub 和http://atmospherejs.com上看起来很好,这是你可以浏览所有可用的 Meteor 包的网站。

有了这个自述文件,我们将使其他人更容易使用这个包,并欣赏我们的工作。

在线发布我们的软件包

保存自述文件后,可以将包推送到 GitHub 或任何其他在线 Git 存储库,并将存储库的 URL 添加到package.jsPackage.describe({git: …})变量中。 将代码保存在 GitHub 上可以保证它的安全,并允许其他人对其进行分叉和改进。 让我们执行以下步骤来在线推送我们的包:

  1. To publish our package, we can simply run the following command from inside the pages folder in the terminal:

    ``` $ meteor publish --create

    ```

    这将构建和捆绑的包,并上传到 Meteor 的包服务器。

  2. If everything goes fine, we should be able to find our package by typing the following command:

    ``` $ meteor search reactive-timer

    ```

    如下截图所示:

    Publishing our package online

  3. We can then show all of the information about the found package using the following command:

    ``` $ meteor show meteor-book:reactive-timer

    ```

    如下截图所示:

    Publishing our package online

  4. To use the package version from the Meteor server, we can simply move the packages/reactive-timer folder somewhere else, remove the package folder, and run $ meteor to start the app.

    现在 Meteor 不会在packages文件夹中找到任何同名的包,而是会在网上寻找那个包。 自从我们发布了它,它将被下载并在我们的应用中使用。

  5. 如果我们想要在应用中使用特定版本的包,我们可以在终端的应用文件夹中运行以下命令:

现在我们的包发布了,我们可以在 Atmosphere 上看到http://atmospherejs.com/meteor-book/reactive-timer,如下截图所示:

Publishing our package online

注释

注意,这只是一个包的示例,实际上从未发布过。 然而,在我的名字下可以在http://atmospherejs.com/frozeman/reactive-timer找到这个包的发布版本。

更新我们的包

如果我们想发布软件包的一个新版本,我们可以简单地增加package.js文件中的版本号,并在packages文件夹中使用以下命令发布一个新版本:

$ meteor publish

为了让我们的应用使用包的最新版本(只要我们没有指定一个固定版本),我们可以简单地从我们的应用的文件夹中运行以下命令:

$ meteor update meteor-book:reactive-timer

如果我们想更新所有的包,我们可以运行以下命令:

$ meteor update –-packages-only

小结

在本章中,我们用我们的ReactiveTimer对象创建了我们自己的包。 我们也了解到在 Meteor 的官方包装系统上发布一个包装是多么简单。

为了更深入地挖掘,请阅读以下资源中的文档:

你可以在https://www.packtpub.com/books/content/support/17713或在 GitHub 上https://github.com/frozeman/book-building-single-page-web-apps-with-meteor/tree/chapter11找到本章的代码示例。

这个代码示例只包含这个包,所以为了将它添加到应用中,请使用前一章的代码示例。

在下一章中,我们将讨论应用和包的测试。