使用Typescript和ES模块发布Node模块的方法


Posted in Javascript onMay 25, 2020

本文主要介绍了使用Typescript和ES模块发布Node模块的方法,分享给大家,具体如下:

使用Typescript和ES模块发布Node模块的方法

TypeScript已经成为一种非常流行的JavaScript语言,这是有原因的。它的类型系统和编译器能够在您的软件运行之前的编译时捕获各种bug,并且附加的代码编辑器功能使它成为一个非常适合开发人员的高效环境。

但是,当你想用TypeScript编写一个库或包,同时又想用JavaScript来发布,这样你的最终用户就不必手动编译你的代码,会发生什么?我们如何使用现代的JavaScript功能(如ES模块)来编写,同时又能获得TypeScript的所有好处?

本文旨在解决所有这些问题,并为你提供一个设置,使你可以放心地编写和共享TypeScript库,并为包装的使用者提供轻松的体验。

入门

我们要做的第一件事是建立一个新项目。在本教程中,我们将创建一个基本的数学程序包——不是一个服务于任何实际目的的程序包——因为它将让我们演示所有我们需要的TypeScript,而不会偏离程序包的实际功能。

首先,创建一个空目录并运行 npm init -y 创建一个新项目。这将创建你的 package.json 并为你提供一个空项目以供处理:

$ mkdir maths-package
$ cd maths-package
$ npm init -y

现在,我们可以添加第一个也是最重要的依赖项:TypeScript!

$ npm install --save-dev typescript

安装TypeScript后,可以通过运行 tsc --init 初始化TypeScript项目。 tsc 是“ TypeScript编译器”的缩写,是TypeScript的命令行工具。

为确保你运行我们刚刚在本地安装的TypeScript编译器,应在命令前加上 npx。npx是个很棒的工具,它将在node_modules 文件夹中查找你提供的命令,因此,通过在命令前面加上前缀,可以确保我们使用的是本地版本,而不是你可能已安装的TypeScript的任何其他全局版本。

$ npx tsc --init

这将创建一个 tsconfig.json 文件,该文件负责配置我们的TypeScript项目。您会看到该文件具有数百个选项,其中大多数选项已被注释掉(TypeScript支持 tsconfig.json 文件中的注释)。我已将文件缩减为仅启用的设置,如下所示:

{
 "compilerOptions": {
  "target": "es5",
  "module": "commonjs",
  "strict": true,
  "esModuleInterop": true,
  "forceConsistentCasingInFileNames": true
 }
}

我们需要对此配置进行一些更改,以使我们能够使用ES模块发布程序包,因此,让我们现在来看一下这些选项。

配置tsconfig.json 选项

如果您正在寻找所有可能的 tsconfig 选项的完整列表,可以在TypeScript网站上找到此方便的参考。

让我们从 target 开始,这定义了你将在浏览器中提供代码的JavaScript支持级别。如果您必须使用一组较旧的浏览器,这些浏览器可能不具有所有最新和最强大的功能,则可以将其设置为 ES2015。如果您确实需要最大的浏览器覆盖范围,TypeScript甚至将支持 ES3

我们将在此处针对该模块使用 ES2015,但可以随时进行相应更改。例如,如果我为自己建立一个快速的辅助项目,并且只关心尖端的浏览器,那么我很高兴将其设置为 ES2020

选择模块系统

接下来,我们必须决定将用于该项目的模块系统。请注意,这不是我们要编写的模块系统,而是TypeScript的编译器在输出代码时将使用的模块系统。

发布模块时我喜欢做的事情是发布两个版本:

  • 带有ES模块的现代版本,以便捆绑工具可以巧妙地将未使用的代码tree?shake ,因此支持ES模块的浏览器只需导入文件
  • 使用CommonJS模块的版本(如果在Node中工作,你将习惯使用 require 代码),因此较早的构建工具和Node.js环境可以轻松运行该代码

稍后我们将介绍如何使用不同的选项捆绑两次,但是现在,让我们将TypeScript配置为输出ES模块。我们可以通过将 module 设置设置为 ES2020 来实现。

现在,你的 tsconfig.json 文件应如下所示:

{
 "compilerOptions": {
  "target": "ES2015",
  "module": "ES2020",
  "strict": true,
  "esModuleInterop": true,
  "forceConsistentCasingInFileNames": true
 }
}

编写一些代码

在讨论捆绑代码之前,我们需要写一些代码!让我们创建两个小模块,它们既导出函数,又为导出所有代码的模块提供一个主 entry 文件。

我喜欢将所有TypeScript代码放在 src 目录中,因为这意味着我们可以直接将TypeScript编译器指向它,因此,我将使用以下代码创建 src/add.ts

export const add = (x: number, y:number):number => {
 return x + y;
}

我也将创建 src/subtract.ts

export const subtract = (x: number, y:number):number => {
 return x - y;
}

最后,src/index.ts 将导入我们所有的API方法并再次导出它们:

import { add } from './add.js'
import { subtract } from './subtract.js'
export {
 add,
 subtract
}

这意味着,用户可以通过导入只需要的东西来获取我们的功能,也可以通过获取所有的东西来获取。

import { add } from 'maths-package';

import * as MathsPackage from 'maths-package';

请注意,在 src/index.ts 中,我的导入包含文件扩展名。如果只想支持Node.js和构建工具(例如webpack),则不需要这样做,但是如果要支持支持ES模块的浏览器,则需要文件扩展名。

使用TypeScript进行编译

让我们看看是否可以让TypeScript编译我们的代码。我们需要先对 tsconfig.json 文件进行一些调整,然后才能执行以下操作:

{
 "compilerOptions": {
  "target": "ES2015",
  "module": "ES2020",
  "strict": true,
  "esModuleInterop": true,
  "forceConsistentCasingInFileNames": true,
  "outDir": "./lib",
 },
 "include": [
  "./src"
 ]
}

我们进行了两项更改:

  • compilerOptions.outDir ——这告诉TypeScript将我们的代码编译到一个目录中。在这种情况下,我已经告诉它命名该目录 lib,但是您可以根据需要命名它。
  • include ——告诉TypeScript我们希望在编译过程中包含哪些文件。在我们的例子中,我们所有的代码都位于src 目录中,因此我将其传入。这就是为什么我喜欢将所有TS源文件保存在一个文件夹中的原因,这使配置变得非常容易

让我们来试一试,看看会发生什么吧! 我发现在调整我的TypeScript配置时,最适合我的方法是调整、编译、检查输出,然后再调整。不要害怕尝试这些设置,看看它们如何影响最终结果。

要编译TypeScript,我们将运行 tsc 并使用 -p 标志(“project”的缩写)告诉它 tsconfig.json 的位置:

npx tsc -p tsconfig.json

如果你有任何类型错误或配置问题,将在此处显示。如果没有,您应该什么也看不到——但是请注意,你有一个新的 lib 目录,其中有文件!TypeScript编译时不会将任何文件合并在一起,而是将每个模块转换成对应的JavaScript。

让我们看一下输出的三个文件:

使用Typescript和ES模块发布Node模块的方法

// lib/add.js
export const add = (x, y) => {
  return x + y;
};

// lib/subtract.js
export const subtract = (x, y) => {
  return x - y;
};

// lib/index.js
import { add } from './add.js';
import { subtract } from './subtract.js';
export { add, subtract };

它们看起来和我们的输入非常相似,但没有我们添加的类型注释。这是可以预期的:我们在ES模块中编写了我们的代码,并告诉TypeScript也要以这种形式输出。如果我们使用了比ES2015更新的任何JavaScript功能,TypeScript会将它们转换为ES2015友好的语法,但是在我们的案例中,我们没有使用它,因此TypeScript在很大程度上仅保留了所有内容。

该模块现在可以发布到npm上供其他用户使用,但是我们有两个问题需要解决:

  • 我们不会在代码中发布任何类型信息。这不会对我们的用户造成破坏,但这是一个错过的机会:如果我们也发布我们的类型信息,那么使用支持TypeScript的编辑器的人或用TypeScript编写应用程序的人将获得更好的体验。
  • Node还不支持开箱即用的ES模块。发布CommonJS版本也很好,所以Node不需要额外的工作。ES模块支持将出现在Node 13和更高的版本中,但是要赶上生态系统还需要一段时间。

发布类型定义

我们可以通过要求TypeScript在写代码的同时发出一个声明文件来解决类型信息问题。这个文件的结尾是 .d.ts,它将包含关于我们代码的类型信息。将它看作源代码,除了不包含类型和实现之外,它只包含类型。

让我们在 tsconfig.json 中添加 "declaration": true(在 "compilerOptions" 部分中),然后再次运行 npx tsc -p tsconfig.json

提示:我想在我的 package.json 文件中添加一个脚本来进行编译,因此无需输入以下内容:

"scripts": {
  "tsc": "tsc -p tsconfig.json"
 }

然后我可以运行 npm run tsc 来编译我的代码。

现在,您将看到每个JavaScript文件(例如 add.js )旁边都有一个等效的 add.d.ts 文件,如下所示:

使用Typescript和ES模块发布Node模块的方法

"scripts": {
  "tsc": "tsc -p tsconfig.json"
 }

因此,现在当用户使用我们的模块时,TypeScript编译器将能够选择所有这些类型。

发布到CommonJS

难题的最后一部分是还将TypeScript配置为输出使用CommonJS的代码版本。为此,我们可以制作两个 tsconfig.json 文件,一个针对ES模块,另一个针对CommonJS。不过,我们可以让CommonJS配置扩展我们的默认设置并覆盖 modules 设置,而不是复制所有配置。

让我们创建 tsconfig-cjs.json

{
 "extends": "./tsconfig.json",
 "compilerOptions": {
  "module": "CommonJS",
  "outDir": "./lib/cjs"
 },
}

重要的是第一行,这意味着此配置默认情况下会继承 tsconfig.json 的所有设置。这很重要,因为你不需要在多个JSON文件之间同步设置。

然后覆盖需要更改的设置。我相应地更新模块,然后将 outDir 设置更新到 lib/cjs ,这样我们就可以输出到lib 中的子文件夹。

此时,我还更新了 package.json 中的 tsc 脚本:

"scripts": {
 "tsc": "tsc -p tsconfig.json && tsc -p tsconfig-cjs.json"
}

现在,当我们运行 npm run tsc 时,我们将编译两次,并且我们的lib目录将如下所示:

使用Typescript和ES模块发布Node模块的方法

这个有点乱,让我们通过更新 tsconfig 中的 outDir 选项来将ESM输出更新到 lib/esm

接下来,我们将设置 module 属性。这是应该链接到我们软件包的ES模块版本的属性。支持此功能的工具将能够使用此版本的软件包。因此,应将其设置为 ./lib/esm/index.js

接下来,我们将 files entry 添加到 package.json 中。在这里,我们定义了发布模块时应包括的所有文件。我喜欢使用这种方法来明确定义要在最终模块中推送到npm的文件。

这样我们就可以减小模块的大小。例如,我们不会发布 src 文件,而是发布 lib 目录。如果你在 files entry 中提供目录,则默认情况下会包含其所有文件和子目录,因此你不必全部列出。

提示:如果要查看模块中将包含哪些文件,请运行 npx pkgfiles 以获得列表。

现在,我们的 package.json 中包含以下三个附加字段:

"main": "./lib/cjs/index.js",
 "module": "./lib/esm/index.js",
 "files": [
  "lib/"
 ],

还有最后一步。因为我们要发布 lib 目录,所以需要确保在运行 npm publishlib 目录是最新的。npm文档中有一节是关于如何做到这一点的——我们可以使用 prepublishOnly 脚本。当我们运行 npm publish 时,该脚本将自动为我们运行:

"scripts": {
 "tsc": "tsc -p tsconfig.json && tsc -p tsconfig-cjs.json",
 "prepublish": "npm run tsc"
},

注意,还有一个名为 prepublish 的脚本,这使选择哪个稍微有些混乱。npm文档提到了这一点:不推荐使用prepublish ,如果只想在发布时运行代码,则应使用prepublishOnly

这样,运行 npm publish 将运行我们的TypeScript编译器并在线发布模块!我将该软件包发布在 @ jackfranklin/maths-package-for-blog-post 下,虽然我不建议你使用它,但是你可以浏览文件并查看。我还将所有代码都上传到了CodeSandbox中,因此您可以根据需要下载或破解它。

结束

就是这样!我希望这篇教程已经告诉你,使用TypeScript上手和运行TypeScript并不像最初看起来那么困难,只要稍加调整,就可以让TypeScript输出你可能需要的多种格式,而不需要太多麻烦。

到此这篇关于使用Typescript和ES模块发布Node模块的方法的文章就介绍到这了,更多相关Typescript和ES发布Node模块内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
javascript sudoku 数独智力游戏生成代码
Mar 27 Javascript
JavaScript 匿名函数(anonymous function)与闭包(closure)
Oct 04 Javascript
JS图片无缝滚动(简单利于使用)
Jun 17 Javascript
Jquery检验手机号是否符合规则并根据手机号检测结果将提交按钮设为不同状态
Nov 26 Javascript
基于jQuery实现音乐播放试听列表
Apr 14 Javascript
jQuery获取元素父节点的方法
Jun 21 Javascript
Javascript中this绑定的3种方法与比较
Oct 13 Javascript
详解用node-images 打造简易图片服务器
May 08 Javascript
基于react组件之间的参数传递(详解)
Sep 05 Javascript
jQuery模拟12306城市选择框功能简单实现方法示例
Aug 13 jQuery
JS前端广告拦截实现原理解析
Feb 17 Javascript
Js类的构建与继承案例详解
Sep 15 Javascript
js 动态校验开始结束时间的实现代码
May 25 #Javascript
使用 Opentype.js 生成字体子集的实例代码详解
May 25 #Javascript
Node.js API详解之 repl模块用法实例分析
May 25 #Javascript
微信小程序仿抖音视频之整屏上下切换功能的实现代码
May 24 #Javascript
如何使用vue slot创建一个模态框的实例代码
May 24 #Javascript
使用React代码动态生成栅格布局的方法
May 24 #Javascript
ES6对象操作实例详解
May 23 #Javascript
You might like
php中文字母数字验证码实现代码
2008/04/25 PHP
使用PHP实现二分查找算法代码分享
2011/06/24 PHP
学习PHP Cookie处理函数
2016/08/09 PHP
PHP simplexml_load_string()函数实例讲解
2019/02/03 PHP
Javascript中的常见排序算法
2007/03/27 Javascript
Firebug 字幕文件JSON地址获取代码
2009/10/28 Javascript
Jquery iframe内部出滚动条
2010/02/11 Javascript
JQuery 动画卷页 返回顶部 动画特效(兼容Chrome)
2010/02/15 Javascript
js/html光标定位的实现代码
2013/09/23 Javascript
JavaScript获取当前日期是星期几的方法
2015/04/06 Javascript
js+html5实现canvas绘制镂空字体文本的方法
2015/06/05 Javascript
JS获取IMG图片高宽的简单实例
2016/05/17 Javascript
js控制台输出的方法(详解)
2016/11/26 Javascript
js将字符串中的每一个单词的首字母变为大写其余均为小写
2017/01/05 Javascript
详解JSONObject和JSONArray区别及基本用法
2017/10/25 Javascript
js replace 全局替换的操作方法
2018/06/12 Javascript
vue.js使用watch监听路由变化的方法
2018/07/08 Javascript
vue 源码解析之虚拟Dom-render
2019/08/26 Javascript
JavaScript find()方法及返回数据实例
2020/04/30 Javascript
[03:34]2014DOTA2西雅图国际邀请赛 淘汰赛7月15日TOPPLAY
2014/07/15 DOTA
Python递归遍历列表及输出的实现方法
2015/05/19 Python
Django自定义插件实现网站登录验证码功能
2017/04/19 Python
Python用户推荐系统曼哈顿算法实现完整代码
2017/12/01 Python
Python使用matplotlib绘图无法显示中文问题的解决方法
2018/03/14 Python
python使用正则表达式来获取文件名的前缀方法
2018/10/21 Python
谈谈Python中的while循环语句
2019/03/10 Python
Python PyPDF2模块安装使用解析
2020/01/19 Python
python同义词替换的实现(jieba分词)
2020/01/21 Python
利用python控制Autocad:pyautocad方式
2020/06/01 Python
小车司机岗位职责
2013/11/25 职场文书
初三化学教学反思
2014/01/23 职场文书
人事助理自荐信
2014/02/02 职场文书
学生会工作感言
2015/08/07 职场文书
任命书格式模板
2015/09/22 职场文书
唤醒紫霞仙子,携手再游三界!大话手游X《大话西游》电影合作专属剧情任务
2022/04/03 其他游戏
Redis入门基础常用操作命令整理
2022/06/01 Redis