作者简介 zqlu 蚂蚁金服·数据体验技术团队
TypeScript 作为一种有类型定义的 JavaScript 超集语言,用来写函数库除了给自己开发者自己带来如开发效率提升、静态检查等好处外,对库的使用方也能带来一下好处:
- 对于直接使用 JavaScript 的使用者,通过 TypeScript 的编译过程,可以生成直接使用的 JavaScript 代码,如 ES5 版本的 JavaScript 代码,对使用者的使用没有影响
- 对于使用 TypeScript 的开发者,通过 TypeScript 编译生成的定义文件,能极大提升使用者的使用体验
代码库在发布之后,使用者可能期望能以各种方式来使用库,如直接在浏览器中加载使用、通过 NodeJs 的 CommonJS 模块方式来引用代码库、或者直接通过 ES6 的 module 方式来引用。
在使用 TypeScript 编写代码库后,开发者可以不用关心代码库使用者的饮用方式,而直接使用 TypeScript 的模块机制来模块化编写代码库;而是在通过 TypeScript 的编译过程来生成
下面分别介绍以上几种不同使用情景下的编译过程。
目前前端开发库绝大部分都会发布到 npm 上,npm 作为 NodeJs 的包管理器,提供 CommonJs 的模块化代码是非常有必要的。
通过 tsconfig.json 来配置 CommonJS 模块代码的生成:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"outDir": "dist/cmjs",
"rootDir": "./src",
"declaration": true,
"sourceMap": true,
"lib": ["dom", "es6"]
}
}
通过以上配置, "module": "commonjs"
来申明编译生成目标代码模块为commonjs
,生成目标代码目录为:"outDir": "dist/cmjs"
。
同时在package.json
中,通过main
字段来申明 CommonJS 的入口文件:
{
"name": "myLib",
"version": "0.1.0",
"main": "dist/cmjs/index.js",
}
随着 ES6 标准的流行,以及各种打包工具对 ES6 模块的原生支持,如 webpack 的resolve.mainFields 配置,提供 ES6 模块代码能够让使用者享受 ES6 模块的一些特性。
同 CommonJS 一样,也可以通过配置 tsconfig.json 的方式来生成 ES6 模块代码,但一般在 tsconfig.json 中使用 commonjs 作为默认配置,所以可以在 package.json 中通过添加 script 来通过 TypeScript 编译器命令行参数来编译生成 ES6 模块代码:
{
"name": "myLib",
"script": {
"build:cmjs": "tsc -P tsconfig.json",
"build:es6": "tsc -P tsconfig.json --module ES6 --outDir dist/es6",
"build": "npm run build:cmjs; npm run build:es6"
}
}
其中 build:cmsj
编译生成 CommonJS 模块目标代码,build:es6
编译生成 ES6 模块目标代码。对于 ES6 模块的代码,通过在package.json
中的 module 字段来申明 ES6 模块代码的入口文件,以便能够识别 ES6 模块的模块加载器使用:
{
"name": "myLib",
"version": "0.1.0",
"main": "dist/cmjs/index.js",
"module": "dist/es6/index.js",
}
生成能够直接给浏览器使用的代码,能够方便使用者,不需要使用包管理器,直接在 html 文件中引用。如 React,可以直接在 HTML 文件中引入 dist/react.js 单独文件。对此,需要对模块化分布的代码按依赖合并大包,所以使用打包工具如 webpack 完全可以做到,这里不仔细介绍。
另外,还可以可以借助Browserify工具来将上述编译生成的 CommonJS 模块化代码整体打包成可供浏览器直接使用的代码。同样,通过简单的添加 package.json 的 script 来完成:
{
"name": "myLib",
"script": {
"build:cmjs": "tsc -P tsconfig.json",
"build:es6": "tsc -P tsconfig.json --module ES6 --outDir dist/es6",
"build:web": "browserify dist/cmjs/index.js --standalone myLib -o dist/web/bundle.js",
"build": "npm run build:cmjs; npm run build:es6"
}
}
语义化版本使用如:主版本号.次版本号.修订号 的版本格式,有详细严格的定义文档。遵循语义化版本号规则有利于使用者理解代码库的升级修改,共同遵循语义化版本号能使开发者和使用者共同获益。
此外,文档中还提到了一些常见的问题,如:
- 在函数库开发阶段,如果控制版本号?推荐是从 0.1.0 版本开始
- 如何判断发布 1.0.0 的时机?软件被应用在正式环境;有固定的 API 被使用者依赖;存在向下兼容问题的时候
- 对于公共 API,即使是最小但向下不兼容的改变都需要产生新的主版本号,岂不是很快就到了如 42.0.0 版?这是开发的责任感和前瞻性的问题,为什么有这么多不兼容更改:)
在 TypeScript 编写函数库后,需要更新版本时候,推荐使用npm version
命令来更新版本号,如:
-
更新修订号:
npm version patch
-
更新此版本号:
npm version minor
-
更新主版本号:
npm version major
执行上述命令,会增加相应的版本号保存到 package.json 的 version 字段中。在使用 git 做源码版本控制的时候,还可以添加-m 参数来自动成来提供一条版本更新 commit 记录和 tag 记录,如运行npm version patch -m "update version %s"
除了更新 package.json 中的 version 字段外,还会自动生成一条 commit 记录,commit message 中的%m
会被替换成新生成的版本号,此外还有自定生成 tag 记录。