阅读更多
引用
我多次看到大家在这个问题上产生困惑,甚至经验丰富的 JavaScript 开发者都可能错过它的一些微妙之处。所以我认为应该写这么一个简短的教程。

假设有一个 JavaScript 模块想发布在 npm 中,它既能在 Node 中运行,又能在浏览器中运行。这会产生一个问题!这个特定的模块对于 Node 和浏览器的运行,会有一点不同的实现。

这种情况相当常见,因为这 Node 和浏览器之间存在许多微小的环境差异。如何正确实现相当棘手,尤其是想在针对浏览的实现中极尽可能地减少依赖库的时候。

构建一个 JS 包
来写一个很小的,称为 base64-encode-string 的 JavaScript 包。它的作用是将输入的字符串以 base64 编码之后输出。

对浏览器来说,使用内置的 btoa 函数很容易就能实现:
module.exports = function (string) {
  return btoa(string);
}

但 Node 中没有 btoa 函数,所以我们要创建一个  Buffer,然后调用 buffer.toString():
module.exports = function (string) {
  return Buffer.from(string, 'binary').toString('base64');
};


两种方法都能输出正确的 base64 编码,比如:
var b64encode = require('base64-encode-string');
b64encode('foo');    // Zm9v
b64encode('foobar'); // Zm9vYmFy

现在我们需要一些方法来检验它是运行在浏览器中还是运行在 Node 中,然后我们才能调用正确的版本。Browserify 和 Webpack 都定义了 process.browser,在浏览器中它返回 true,而在 Node 中它返回 false。所以我们很容易做到:
if (process.browser) {
  module.exports = function (string) {
    return btoa(string);
  };
} else {
  module.exports = function (string) {
    return Buffer.from(string, 'binary').toString('base64');
  };
}

我们把文件命名为 index.js,键入 npm publish,然后一切都搞定了。但这种方法存在一个巨大的性能问题。

index.js 中包含了对 Node 内建的 process 和 Buffer 的引用,Browserify 和 Webpack 都会在打包的时候自动包含相应的 polyfill(引1,引2)。

虽然这个模块只有 9 行,但 Browserify 和 Webpack 最小化并打包出来有 24.7KB(7.6KB min+gz)。在浏览器中只需要 btoa 就能解决的问题居然需要引用这么大的东西

超爱“browser” 选项
如果在 Browserify 和 Webpack 的文件中寻找解决办法,最终会找到 node-browser-resolve。这涉及到package.json 中的 "browser" 选项。它定义在为浏览器构建模块时的行为。

使用这个技术,需要在 package.json 中添加:
{
  /* ... */
  "browser": {
    "./index.js": "./browser.js"
  }
}

然后将两个函数分拆到 index.js 和 browser.js 两个文件中:
// index.js
module.exports = function (string) {
  return Buffer.from(string, 'binary').toString('base64');
};

// browser.js
module.exports = function (string) {
  return btoa(string);
};

这之后,Browserify 和 Webpack 会产生更合适的结果:Browserify 最小只有 511 字段(315 min+gz),Webpack 则是 550 字节(297 min+gz)。

这个包发布到 npm 之后,在 Node 中 运行 require('base64-encode-string') 都是引用 Node 版本,而使用 Browerify 或 Webpack 则会引用浏览器版本。成功!

对于 Rollup 来说会更复杂一点。Rollup 用户需要使用 rollup-plugin-node-resolve 并在选项中设置 browser 为 ture。

对于 jspm 来说就很不幸了,它不支持 “browser” 选项。不过 jspm 用户可以通过如下方法绕过去:require('base64-encode-string/browser') 或者 jspm install npm:base64-encode-string -o "{main:'browser.js'}"。

另外,包作者可以在 package.json 中指定“jspm”选项。

高级技巧
直接使用“browser”的方法很好,但对于大型项目来说,package.json 和代码的耦合就很尴尬了。例如,package.json 很快会变成下面这个样子:
{
  /* ... */
  "browser": {
    "./index.js": "./browser.js",
    "./widget.js": "./widget-browser.js",
    "./doodad.js": "./doodad-browser.js",
    /* etc. */
  }
}

你每需要一个浏览器模块,就必须创建两个单独的文件,然后在 “browser” 选项中添加一行来关联它们。还得小心不要写错什么!

而且你会发现自己需要将部分代码提取为单独的模块,因为你不想使用 if (process.browser) {} 来进行检查。当这些 *-browser.js 文件逐渐积累起来,就会使代码导航越来越困难。

解决这个问题有几个不同的解决方案。我个人喜欢使用 Rollup 来作为构建工具,它会自动将一个代码库中的代码拆分成 index.js 和 browser.js 文件,节约空间和时间。

想这样做需要安装 rollup 和 rollup-plugin-replace,然后定义 rollup.cofnig.js 文件:
import replace from 'rollup-plugin-replace';
export default {
  entry: 'src/index.js',
  format: 'cjs',
  plugins: [
    replace({ 'process.browser': !!process.env.BROWSER })
  ]
};

(我们会使用 process.env.BROWSER 来切换针对浏览器的构建和针对 Node 的构建。)
接下来,创建 src/index.js 文件,它包含一个单独的函数,其中用到了 process.browser 条件:
export default function base64Encode(string) {
  if (process.browser) {
    return btoa(string);
  } else {
    return Buffer.from(string, 'binary').toString('base64');
  }
}

然后在 package.json 中添加 prepublish 步骤,用于生成文件:
{
  /* ... */
  "scripts": {
    "prepublish": "rollup -c > index.js && BROWSER=true rollup -c > browser.js"
  }
}

生成的文件相当简单而且易读:
// index.js
'use strict';

function base64Encode(string) {
  {
    return Buffer.from(string, 'binary').toString('base64');
  }
}

module.exports = base64Encode;

// browser.js
'use strict';

function base64Encode(string) {
  {
    return btoa(string);
  }
}

module.exports = base64Encode;

你会发现 Rollup 根据需要自动将 process.browser 转变为 true 或 false,然后去掉无用的代码。因此在针对浏览器的生成结果中不会引用 process 或 Buffer。

这种技术让你可以在代码中任意使用 process.browser 条件,发布出来的结果总是两个小文件,一个 index.js,一个 browser.js。在 Node 环境只有 Node 相关的代码,而在浏览器环境则只有浏览器相关的代码。

你还可以配置 Roolup 生成 ES 模块构建、IIFE 构建,或 UMD 构建。比如我的 marky 项目就是一个拥有多个 Rollup 构建目标的简单库。

本文描述的项目(base64-encode-string) 已经发布到 npm 了,你可以去深入了解它。源代码在 GitHub 上。

原文:How to write a JavaScript package for both Node and the browser
译者:Viyi
来自: oschina
1
0
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 怎样写一个能同时用于Node和浏览器的JavaScript包?

    假设你有一个JavaScript的模块想要发布到npm上,它是同时适用于Node和浏览器的。但是请注意!这个特殊的模块在 Node版本和浏览器版本上的实现有着细微的区别。 这种情况出现得实在频繁,因为在Node和浏览器间有着很...

  • pdfkit:用于Node和浏览器JavaScript PDF生成库

    用于Node和浏览器JavaScript PDF生成库。 描述 PDFKit是用于Node和浏览器的PDF文档生成库,可轻松创建复杂的多页可打印文档。 该API包含可链接性,并且包括低级功能以及用于高级功能的抽象。 PDFKit API设计得很简单...

  • prototype-js:用于Node.js和浏览器JavaScript标准库的扩展

    原型用于Node.js和浏览器JavaScript标准库的扩展(自库过渡到ES6以来,浏览器支持处于未经测试的状态,但可能仍然有效)。 最值得注意的是,此模块通过类,继承,mixin和接口提供了更好的OOP支持,而没有运行时开销...

  • id3:适用于Node和浏览器JavaScript ID3标签解析器

    它可以解析浏览器或Node中的ID3v1和ID3v2标签。 可以从本地磁盘(仅节点),同源URL和File实例(HTML5 File API)读取文件。 用法 安装: $ npm i -S id3js AJAX 您可以通过URL解析远程MP3的ID3标签: < ...

  • CmisJS:用于节点和浏览器的CMIS javascript库

    节点和浏览器的CMIS类型脚本/ JavaScript库,现代浏览器不依赖 1.x中的重大API更改 在版本1.x中,所有连接到存储库的CmisSession方法都返回一个Promise 您可以在以下位置找到旧版本的文档: : 安装 npm npm ...

  • crypto:用于 Node.js 和浏览器的简化加密库

    一组加密和散列模块,可与 Node.js、require.js 或直接在浏览器中使用。 模块通常取自其他开源项目(参见文件头),然后更新以改进规范支持(大量单元测试)和性能。 模块 哈希函数 是一种获取任意数据并返回固定...

  • buzy用于node和浏览器的异步队列管理器

    buzy用于node和浏览器的异步队列管理器

  • 松弛:Node用于节点和浏览器的松弛API客户端

    已针对Node和浏览器进行了测试完全支持 完美的对称性:JS方法签名与Web API文档匹配选择您的异步冒险:所有方法都可以接受Node风格的错误或返回Promise 选择加入将token应用于所有方法的OO样式类实例经过良好测试,...

  • upjs用于Node和浏览器的应用程序配套库

    用于Node和浏览器的应用程序配套库,提供开箱即用的认证,日志记录,消息传递等解决方案

  • 适用于浏览器和Node.js的通用LocalStorage。-JavaScript开发

    localstorage-ponyfill用于浏览器和Node.js的通用LocalStorage ponyfill库。 安装使用npm安装:npm install localstorage-ponyfill用法自动(浏览器或Node.js)自动localstorage-ponyfill用于浏览器和Node.js的通用...

  • PDFKit 一个适用于Node和浏览器的JavaScript PDF生成库-javascript

    PDFKit 一个用于 Node 和浏览器的 JavaScript PDF 生成库。 描述 PDFKit 是一个用于 Node 和浏览器的 PDF 文档生成库,可以轻松创建复杂的多页可打印文档。 API 包含可链接性,包括低级功能以及高级功能的抽象。 ...

  • NEM-sdk:用于 Node.js 和浏览器的 NEM 开发工具包

    NEM-sdk 用于 Node.js 和浏览器的 NEM 开发工具包特征: 轻松集成在命名空间中组织创建与 Nano 钱包客户端兼容的钱包简单交易马赛克交易加密、未加密和十六进制消息创建和审核Apostilles 创建和验证签名助手和格式化...

  • jsObjDB:Javascript 数据库 - 用于浏览器或 nodeio

    jsObjDB 在浏览器和Node.js/io.js 中工作。 功能包括: 灵活的查询 索引 复杂的命令链 也像数组一样的游标 对查询/索引的数组和子对象支持 加入 灵活的更新机制 异步和同步支持 插入、更新插入、更新、删除 ...

  • validateatorjs:受Laravel的Validator启发,用于浏览器和Node.jsJavaScript数据验证库。

    validateatorjs库使浏览器和Node.js中JavaScript数据验证都非常容易。 该库的灵感来自 。 为什么要使用validator.js? 在浏览器和Node中均可使用。 可读和声明性的验证规则。 多语言支持的错误消息。 CommonJS /...

  • js_project_skeleton:用于Node.js和浏览器JavaScript``通用''库开发的项目框架

    用于Node.js和浏览器JavaScript``通用''库开发的项目框架警告:目前,该解决方案应被认为适合研究和实验,在生产应用中使用之前,需要进一步的代码和安全性审查。用法只需克隆此webpack.config.js ,删除.git ,然后...

  • shopgun-js-sdk:浏览器和Node.js中用于JavaScript的ShopGun SDK

    SDK同时使用了JavaScript和CSS,因此您需要在浏览器中加载两个资源: < link href =" https://d21oefkcnoen8i.cloudfront.net/sgn-sdk-3.4.0.min.css " rel =" stylesheet " type =" text/css " /> < ...

  • axios:基于Promise的HTTP客户端,用于浏览器和node.js

    基于Promise的HTTP客户端,用于浏览器和node.js 新的axios docs网站: 目录 特征 从浏览器发出 从node.js发出请求 支持 API 拦截请求和响应 转换请求和响应数据 取消请求 JSON数据的自动转换 客户端支持以防 ...

  • common-js:用于浏览器和Node.js环境的共享JavaScript实用程序

    一个名为的配套库包含专用于Node.js环境的实用程序(即不适合在Web浏览器中使用)。 发展 文献资料 该代码由。 这将用作正式文档的基础(即将推出)。 包装经理 该库已作为作为NPM的公共模块发布。 npm install @...

  • 基于Java的SaaS OA协同办公毕设(源码+使用文档)

    系统概述 SaaS OA协同办公系统通常包括以下几个关键组件: 用户界面(UI):提供用户交互界面,用于任务管理、日程安排、文档共享等。 后端服务:处理业务逻辑,如用户认证、数据管理、服务集成等。 数据库:存储用户数据、任务数据、文档数据等。 服务层:提供业务逻辑服务,如权限管理、工作流程等。 集成API:与其他系统集成,如邮件服务、短信服务等。 主要功能 用户认证与管理:用户登录、权限分配、用户资料管理。 任务管理:创建、分配、跟踪和归档任务。 日程管理:安排会议、提醒事件、查看日历。 文档管理:上传、下载、共享和版本控制文档。 协同工作:实时编辑文档、团队讨论、任务协作。 技术架构 Java:作为主要的编程语言。 Spring Boot:用于快速开发基于Java的后端服务。 Apache Shiro或Spring Security:用于安全和认证。 Thymeleaf或JSF:用于构建Java Web应用的用户界面。 数据库:如MySQL、PostgreSQL或MongoDB。 开发优势 实用性:解决企业日常办公需求,提高工作效率。 技术先进:使用当前流行的Java技术栈和框架。

  • 虎年春节送祝福微信小程序源码下载/新版UI/支持多种流量主

    虎年春节送祝福微信小程序源码下载,新版UI支持多种流量主,这是一款网友用以前发过的一款端午送祝福改的一款小程序。 里面的背景图包括祝福语都已经修改成与虎年相关的内容了,总体来说找的背景图还是可以的,不过有些地方和细节小编也给完善了一下。 然后小编测试的时候发现还没有流量主,所以小编也给加了几个流量主进去,到时候大家直接替换流量主的ID就可以了。 另外支持更多小程序推荐,拥有独立的推荐界面 PS:进入送祝福的按钮,部分机型是在老虎的帽子那里,部分是在金元宝那里

Global site tag (gtag.js) - Google Analytics