使用 npm 包

搜索包

您可以使用官方搜索网站 npmjs.com,或者查看根据包质量(代码质量、维护状态、开发速度、流行度等)排序的结果 npms.io。还有一些网站搜索特定类型的包,例如 js.coachReactReact Native 部分。

客户端上的 npm

browserifywebpack 这样的工具旨在在客户端提供类似 Node 的环境,以便许多 npm 包(即使最初是为服务器设计的)可以无修改地运行。在大多数情况下,您可以从客户端文件导入 npm 依赖项,就像在服务器上一样。

当创建一个新的应用程序时,Meteor 会安装 meteor-node-stubs npm 包来帮助提供这种客户端浏览器兼容性。如果您正在将应用程序升级到 Meteor 1.3,则可能需要手动运行 meteor npm install --save meteor-node-stubs

meteor-node-stubs npm 包提供了 Node 内置模块(如 pathbufferutil 等)的浏览器友好实现。Meteor 的模块系统避免实际捆绑任何存根模块(及其依赖项),如果它们未使用,因此保留 meteor-node-stubs 作为依赖项没有任何成本。换句话说,除非您确实知道自己在做什么,否则请保留 meteor-node-stubs 安装。

安装 npm 包

npm 包在项目根目录的 package.json 文件中配置。如果您创建了一个新的 Meteor 项目,则会为您创建一个这样的文件。如果没有,您可以运行 meteor npm init 来创建一个。

要将包安装到您的应用程序中,您需要使用 --save 标志运行 npm install 命令

meteor npm install --save moment

这将更新您的 package.json 中关于依赖项的信息,并将包下载到应用程序的本地 node_modules 目录中。通常,您不会将 node_modules 目录检入源代码控制,并且您的团队成员在依赖项更改时运行 meteor npm install 以获取最新版本。

meteor npm install

如果该包只是一个开发依赖项(即用于测试、代码风格检查或类似用途),则应使用 --save-dev。这样,如果您有任何类型的构建脚本,它可以执行 npm install --production 并避免安装它不需要的包。

有关 npm install 的更多信息,请查看 官方文档

Meteor 自带了 npm,因此您可以键入 meteor npm 而无需担心自行安装它。如果您愿意,也可以使用全局安装的 npm 来管理您的包。

使用 npm 包

要在应用程序中的文件中使用 npm 包,您需要 import 包的名称

import moment from 'moment';

// this is equivalent to the standard node require:
const moment = require('moment');

这将从包中导入默认导出到符号 moment 中。

您还可以使用解构语法从包中导入特定函数

import { isArray } from 'lodash';

您还可以从包中导入其他文件或 JS 入口点

import { parse } from 'graphql/language';

一些 Meteor 应用程序包含本地 Meteor 包(在应用程序树的 packages/ 目录中定义的包);这是 Meteor 拥有完整 ECMAScript 支持之前的较旧建议。如果您的应用程序以这种方式布局,您也可以从您的本地 Meteor 包中 requireimport 应用程序中安装的 npm 包。

从 npm 导入样式

使用 Meteor 的任何 受支持的 CSS 预处理器,您可以使用相对路径和绝对路径导入 NPM 提供的其他样式文件到您的应用程序中。但是,这仅适用于顶级应用程序,在 Atmosphere 包内将不起作用。

使用 {} 语法从 npm 包中导入具有绝对路径的样式,例如使用 Less

@import '{}/node_modules/npm-package-name/button.less';

从 npm 包中导入具有相对路径的样式

@import '../../node_modules/npm-package-name/colors.less';

如果安装了 ecmascript 包,您还可以从 JavaScript 文件直接导入 CSS 以控制加载顺序

import 'npm-package-name/stylesheets/styles.css';

当从 JavaScript 文件导入 CSS 时,该 CSS 不会与 Meteor 构建工具处理的其余 CSS 捆绑在一起,而是放在应用程序的 <head> 标记中,位于 <style>...</style> 标记内,在主要连接的 CSS 文件之后。

使用 npm 中的其他资源构建

Meteor 还支持将其他资源(例如字体)构建到您的应用程序中,这些资源位于您的 node_modules 目录中,方法是从 /public/private 目录中符号链接到这些资源。例如,font-awesome 是一个非常流行的字体库,它提供了许多基于字体的图标。随着库的开发,新图标会经常出现,如果您将整个 font-awesome 代码库复制到您自己的应用程序和 git 存储库中,则很难管理所有更新。相反,请使用以下方法包含这些字体

cd /public
ln -ls ../node_modules/font-awesome/fonts ./fonts

当使用 meteor build 命令时,通过应用程序的 /public/private 目录中的符号链接提供的任何资源都将复制到 Meteor 应用程序包中。

重新编译 npm 包

默认情况下,Meteor 不会重新编译安装在 node_modules 中的包。但是,可以通过 package.json 文件中的 meteor.nodeModules.recompile 配置对象来实现特定 npm 包的编译(例如,以支持包作者忽略的旧版浏览器)。

例如

{
  "name": "your-application",
  ...
  "meteor": {
    "mainModule": ...,
    "testModule": ...,
    "nodeModules": {
      "recompile": {
        "very-modern-package": ["client", "server"],
        "alternate-notation-for-client-and-server": true,
        "somewhat-modern-package": "legacy",
        "another-package": ["legacy", "server"]
      }
    }
  }
}

meteor.nodeModules.recompile 配置对象的键是 npm 包名称,值指定应使用 Meteor 编译器插件系统为哪些包重新编译,就像这些包是 Meteor 应用程序的一部分一样。

例如,如果 npm 包使用 const/let 语法或箭头函数,这对现代代码和服务器代码来说都没问题,但您可能希望在构建旧版包时重新编译该包。为此,请将 "legacy"["legacy"] 指定为包属性的值,类似于上面的 somewhat-modern-package。这些字符串和字符串数组与 package.js 文件中 api.addFiles(files, where) 的第二个参数具有相同的含义。

此配置与将应用程序目录符号链接到 node_modules/ 的作用几乎相同,但没有任何符号链接:https://forums.meteor.com/t/litelement-import-litelement-html/45042/8?u=benjamn

meteor.nodeModules.recompile 配置目前仅适用于应用程序 node_modules/ 目录(不适用于 Meteor 包中的 Npm.depends 依赖项)。重新编译的包必须是应用程序的直接依赖项。

Meteor 将编译公开的代码,就像它是应用程序的一部分一样,使用您安装的任何编译器插件。您可以使用 .babelrc 文件或任何其他通常用于配置应用程序代码编译的技术来影响此编译。

npm Shrinkwrap

package.json 通常会编码版本范围,因此每个 npm install 命令有时会导致不同的结果,如果在此期间发布了新版本。为了确保您和您的团队的其他成员使用每个包的完全相同的版本,最好在对 package.json 进行任何依赖项更改后使用 npm shrinkwrap

# after installing
meteor npm install --save moment
meteor npm shrinkwrap

这将创建一个包含每个依赖项的确切版本的 npm-shrinkwrap.json 文件,您应该将此文件检入源代码控制。为了获得更高的精确度(给定版本的包的内容 *可以* 更改),并避免在部署期间依赖 npm 服务器,您应该考虑使用 npm shrinkpack

异步回调

许多 npm 包依赖于异步、基于回调或基于 Promise 的编码风格。由于多种原因,Meteor 目前构建在一种看起来同步但仍然非阻塞的风格之上,使用 Fibers

全局 Meteor 服务器上下文以及每个方法和发布都会初始化一个新的 fiber,以便它们可以并发运行。许多 Meteor API(例如集合)依赖于在 fiber 内部运行。它们还依赖于一个内部 Meteor 机制,该机制跟踪服务器“环境”状态,例如当前正在执行的方法。这意味着您需要初始化自己的 fiber 和环境才能在 Meteor 应用程序中使用异步 Node 代码。让我们看一个一些无法正常工作的代码示例,使用 node-github 存储库 中的代码示例

// Inside a Meteor method definition
updateGitHubFollowers() {
  github.user.getFollowingFromUser({
    user: 'stubailo'
  }, (err, res) => {
    // Using a collection here will throw an error
    // because the asynchronous code is not in a fiber
    Followers.insert(res);
  });
}

让我们看看解决此问题的几种方法。

`Meteor.bindEnvironment`

在大多数情况下,将回调包装在 Meteor.bindEnvironment 中将可以解决问题。此函数既将回调包装在一个 fiber 中,又执行一些工作以维护 Meteor 的服务器端环境跟踪。以下是使用 Meteor.bindEnvironment 的相同代码

// Inside a Meteor method definition
updateGitHubFollowers() {
  github.user.getFollowingFromUser({
    user: 'stubailo'
  }, Meteor.bindEnvironment((err, res) => {
    // Everything is good now
    Followers.insert(res);
  }));
}

但是,这并非在所有情况下都能奏效 - 由于代码是异步运行的,因此我们无法在方法返回值中使用从 API 获取的任何内容。我们需要一种不同的方法,它将异步 API 转换为看起来同步的方法,这将允许我们返回值。

`Meteor.wrapAsync`

许多 npm 包采用接受 (err, res) 参数的回调的约定。如果您的异步函数符合此描述,如上所示,您可以使用 Meteor.wrapAsync 将其转换为使用返回值和异常而不是回调的 fiber 化 API,如下所示

// Setup sync API
const getFollowingFromUserFiber =
  Meteor.wrapAsync(github.user.getFollowingFromUser, github.user);

// Inside a Meteor method definition
updateGitHubFollowers() {
  const res = getFollowingFromUserFiber({
    user: 'stubailo'
  });

  Followers.insert(res);

  // Return how many followers we have
  return res.length;
}

如果你想重构它并创建一个完全基于 Fiber 的 GitHub 客户端,你可以编写一些逻辑来循环遍历所有可用的方法,并对它们调用Meteor.wrapAsync,创建一个具有相同结构但具有更多 Meteor 兼容 API 的新对象。

Promise

最近,很多 npm 包都开始使用 Promise 而不是回调作为其 API。这意味着你实际上从异步函数中获得了一个返回值,但它只是一个空壳,真实值稍后才会填充。

好消息是,Promise 可以与新的 ES2015 async/await 语法(从 Meteor 1.3 开始在ecmascript 包中可用)一起使用,在客户端和服务器端都能以自然且同步的方式使用。

如果你将函数声明为async(最终意味着它本身返回一个 Promise),那么你就可以使用await关键字等待其他 Promise 内部。这使得串行调用基于 Promise 的库变得非常容易。

async function sendTextMessage(user) {
  const toNumber = await phoneLookup.findFromEmail(user.emails[0].address);
  return await client.sendMessage({
    to: toNumber,
    from: '+14506667788',
    body: 'Hello world!'
  });
}

Shrinkpack

Shrinkpack 是一种工具,它可以为你提供比单独使用npm shrinkwrap更可靠和可重复的构建。

它本质上是将每个 npm 依赖项的内容的 tarball 复制到你的应用程序源代码库中。这本质上是npm-shrinkwrap.json文件(shrinkwrap 创建)的一个更强大的版本,因为它意味着你的应用程序的 npm 依赖项可以在无需或依赖 npm 服务器可用或可靠的情况下进行组装。这对于可重复的构建很有用,尤其是在部署时。

要使用 shrinkpack,首先全局安装它

npm install -g shrinkpack

然后在 shrinkwrap 之后直接使用它

meteor npm install --save moment
meteor npm shrinkwrap
shrinkpack

然后你应该将生成的node_shrinkwrap/目录检入源代码控制,但确保它被你的文本编辑器忽略。

注意:虽然这对具有大量 npm 依赖项的项目来说是一个好主意,但它不会影响 Atmosphere 依赖项,即使它们本身具有直接的 npm 依赖项。

在 GitHub 上编辑
// 搜索框