22、TypeScript 生产环境构建与运行全指南

TypeScript 生产环境构建与运行全指南

在开发 TypeScript 应用时,将其在生产环境中顺利构建和运行是至关重要的。本文将详细介绍如何在浏览器和服务器环境中完成这一目标,涵盖了编译版本选择、库支持、源映射、模块化、错误监控等多个方面。

1. 谨慎使用 Polyfill

如果你计划在浏览器中运行应用程序,要注意避免因包含不必要的 polyfill 而使 JavaScript 包体积过大。因为目标平台可能已经支持了部分你正在填充的特性。建议使用 Polyfill.io 这样的服务,仅加载用户浏览器所需的 polyfill。

添加 polyfill 后,需要在 tsconfig.json lib 字段中告知 TSC 你的环境支持这些特性。例如,如果你填充了所有 ES2015 特性以及 ES2016 的 Array.prototype.includes ,可以使用以下配置:

{ 
  "compilerOptions": { 
    "lib": ["es2015", "es2016.array.includes"] 
  }
}

如果在浏览器中运行代码,还需要启用 DOM 类型声明:

{ 
  "compilerOptions": { 
    "lib": ["es2015", "es2016.array.include", "dom"] 
  }
}

要获取支持的库的完整列表,可以运行 tsc --help

2. 启用源映射

源映射是将转译后的代码与生成它的源代码关联起来的一种方式。大多数开发工具(如 Chrome DevTools)、错误报告和日志框架以及构建工具都支持源映射。在构建过程中使用源映射可以更轻松地调试生成的 JavaScript 代码。

一般来说,在开发环境中使用源映射是个好主意,并且在浏览器和服务器环境的生产环境中也可以提供源映射。但需要注意的是,如果你依赖代码的模糊性来保证一定的安全性,那么在生产环境的浏览器中不要提供源映射。

3. 项目引用

随着应用程序的增长,TSC 进行类型检查和编译代码的时间会越来越长。为了解决这个问题,TSC 提供了项目引用功能,它可以显著加快编译时间,尤其是增量编译时间。对于包含数百个文件或更多文件的项目,项目引用是必不可少的。

使用项目引用的步骤如下:
1. 拆分项目 :将 TypeScript 项目拆分为多个子项目。每个子项目是一个包含 tsconfig.json 和 TypeScript 代码的文件夹。尽量将经常一起更新的代码放在同一个文件夹中。
2. 配置子项目的 tsconfig.json :在每个子项目文件夹中创建一个 tsconfig.json ,至少包含以下内容:

{ 
  "compilerOptions": { 
    "composite": true, 
    "declaration": true, 
    "declarationMap": true, 
    "rootDir": "." 
  }, 
  "include": [ 
    "./**/*.ts" 
  ], 
  "references": [ 
    { 
      "path": "../myReferencedProject", 
      "prepend": true 
    } 
  ]
}
- `composite`:告诉 TSC 这个文件夹是一个更大的 TypeScript 项目的子项目。
- `declaration`:告诉 TSC 为该项目生成 `.d.ts` 声明文件。
- `declarationMap`:告诉 TSC 为生成的类型声明构建源映射。
- `references`:子项目依赖的其他子项目的数组。
- `prepend`:将引用的子项目生成的 JavaScript 和源映射与当前子项目生成的合并。
- `rootDir`:明确指定该子项目应相对于根项目进行编译。
  1. 创建根 tsconfig.json :引用那些尚未被其他子项目引用的子项目:
{ 
  "files": [], 
  "references": [ 
    {"path": "./myProject"}, 
    {"path": "./mySecondProject"} 
  ]
}
  1. 编译项目 :使用 --build 标志告诉 TSC 考虑项目引用:
tsc --build # 或简写为 tsc -b

使用项目引用时需要注意以下几点:
- 克隆或重新获取项目后,使用 tsc -b 重新构建整个项目,以重新生成任何缺失或过时的 .d.ts 文件。或者,将生成的 .d.ts 文件提交到版本控制中。
- 不要在项目引用中使用 noEmitOnError: false ,TSC 会始终将该选项硬编码为 true
- 手动确保一个子项目不会被多个其他子项目重复前置。否则,该子项目会在编译输出中出现多次。

4. 使用 extends 减少 tsconfig.json 的样板代码

为了让所有子项目共享相同的编译器选项,可以在根目录创建一个 “基础” 的 tsconfig.json ,子项目的 tsconfig.json 可以继承它:

{ 
  "compilerOptions": { 
    "composite": true, 
    "declaration": true, 
    "declarationMap": true, 
    "lib": ["es2015", "es2016.array.include"], 
    "rootDir": ".", 
    "sourceMap": true, 
    "strict": true, 
    "target": "es5" 
  }
}

然后,更新子项目的 tsconfig.json 以继承这个基础配置:

{ 
  "extends": "../tsconfig.base", 
  "include": [ 
    "./**/*.ts" 
  ], 
  "references": [ 
    { 
      "path": "../myReferencedProject", 
      "prepend": true 
    } 
  ]
}
5. 错误监控

TypeScript 会在编译时警告你代码中的错误,但你还需要一种方法来了解用户在运行时遇到的异常,以便在编译时尝试预防这些异常(或者至少修复导致运行时错误的 bug)。可以使用 Sentry 或 Bugsnag 等错误监控工具来报告和整理运行时异常。

6. 在服务器上运行 TypeScript

要在 NodeJS 环境中运行 TypeScript 代码,只需将代码编译为 ES2015 JavaScript(如果针对的是旧版 NodeJS 版本,则编译为 ES5),并将 tsconfig.json module 标志设置为 commonjs

{ 
  "compilerOptions": { 
    "target": "es2015", 
    "module": "commonjs" 
  }
}

这样,ES2015 的 import export 调用将分别编译为 require module.exports ,代码可以在 NodeJS 上直接运行,无需进一步打包。

如果你使用了源映射,需要将源映射提供给 NodeJS 进程。可以从 NPM 安装 source-map-support 包,并按照其设置说明进行操作。大多数进程监控、日志记录和错误报告工具(如 PM2、Winston 和 Sentry)都内置了对源映射的支持。

7. 在浏览器中运行 TypeScript

在浏览器中编译和运行 TypeScript 比在服务器上要复杂一些。

首先,选择要编译的模块系统:
- 如果要发布一个供他人使用的库(例如在 NPM 上),建议使用 umd 格式以最大化与各种模块打包工具的兼容性。
- 如果只是自己使用代码而不发布到 NPM,编译格式取决于你使用的模块打包工具。例如,Webpack 和 Rollup 与 ES2015 模块配合使用效果最佳,而 Browserify 需要 CommonJS 模块。具体配置如下:
- 如果你使用 SystemJS 模块加载器,将 module 设置为 systemjs
- 如果你使用支持 ES2015 的模块打包工具(如 Webpack 或 Rollup),将 module 设置为 es2015 或更高版本。
- 如果你使用支持 ES2015 的模块打包工具且代码使用了动态导入,将 module 设置为 esnext
- 如果你正在构建一个供其他项目使用的库,并且在 tsc 之后不进行其他构建步骤,将 module 设置为 umd 以最大化与不同加载器的兼容性。
- 如果你使用 CommonJS 打包工具(如 Browserify),将 module 设置为 commonjs
- 如果你计划使用 RequireJS 或其他 AMD 模块加载器,将 module 设置为 amd
- 如果你希望顶级导出在 window 对象上全局可用,将 module 设置为 none 。但如果你的代码处于模块模式,TSC 会尝试将其编译为 commonjs

接下来,配置构建管道将所有 TypeScript 代码编译为一个 JavaScript 文件(通常称为 “包”)或一组 JavaScript 文件。虽然 TSC 可以使用 outFile 标志为小项目完成此任务,但该标志仅限于生成 SystemJS 和 AMD 包。因此,建议使用更强大的打包工具,如 Webpack、Browserify、Babel、Gulp 或 Grunt,并使用相应的 TypeScript 插件:
- Webpack: ts-loader
- Browserify: tsify
- Babel: @babel/preset-typescript
- Gulp: gulp-typescript
- Grunt: grunt-ts

为了优化 JavaScript 包的加载速度,可以遵循以下建议:
- 保持代码模块化,避免代码中的隐式依赖。
- 使用动态导入来懒加载初始页面加载不需要的代码。
- 利用打包工具的自动代码分割功能。
- 制定测量页面加载时间的策略。
- 保持生产构建与开发构建尽可能相似。

最后,在将 TypeScript 代码部署到浏览器时,需要有一个策略来填充缺失的浏览器特性。可以是一组标准的 polyfill,也可以根据用户浏览器支持的特性动态加载。

8. 发布 TypeScript 代码到 NPM

将 TypeScript 代码编译为可供其他 TypeScript 和 JavaScript 项目使用的格式很简单。编译为 JavaScript 供外部使用时,需要注意以下最佳实践:
- 生成源映射,以便调试自己的代码。
- 编译为 ES5,以便他人可以轻松构建和运行你的代码。
- 注意编译的模块格式(如 UMD、CommonJS、ES2015 等)。
- 生成类型声明,以便其他 TypeScript 用户可以使用你的代码的类型。

具体步骤如下:
1. 编译 TypeScript 代码为 JavaScript 并生成相应的类型声明。配置 tsconfig.json 以最大化与流行的 JavaScript 环境和构建系统的兼容性:

{
  "compilerOptions": { 
    "declaration": true, 
    "module": "umd", 
    "sourceMaps": true, 
    "target": "es5" 
  }
}
  1. .npmignore 中排除 TypeScript 源代码,避免包体积过大;在 .gitignore 中排除生成的工件,避免污染 Git 仓库:
# .npmignore 
*.ts # 忽略 .ts 文件 
!*.d.ts # 允许 .d.ts 文件

# .gitignore 
*.d.ts # 忽略 .d.ts 文件 
*.js # 忽略 .js 文件

如果你遵循推荐的项目布局,将源文件放在 src/ 中,生成的文件放在 dist/ 中, .ignore 文件会更简单:

# .npmignore 
src/ # 忽略源文件

# .gitignore 
dist/ # 忽略生成的文件
  1. 在项目的 package.json 中添加 types 字段,指示它包含类型声明,并添加一个脚本在发布前构建包:
{ 
  "name": "my-awesome-typescript-project", 
  "version": "1.0.0", 
  "main": "dist/index.js", 
  "types": "dist/index.d.ts", 
  "scripts": { 
    "prepublishOnly": "tsc -d" 
  }
}

现在,当你使用 npm publish 将包发布到 NPM 时,NPM 会自动将你的 TypeScript 代码编译为可供 TypeScript 和 JavaScript 用户使用的格式。

9. 三斜杠指令

TypeScript 有一个鲜为人知、很少使用且大多过时的特性,即三斜杠指令。这些指令是特殊格式的 TypeScript 注释,用于向 TSC 提供指令。这里介绍两种常见的三斜杠指令:

types 指令

当从模块导入内容时,根据导入的内容,TypeScript 在将代码编译为 JavaScript 时并不总是需要生成 import require 调用。如果导入的内容仅在类型位置使用,TypeScript 不会为该导入生成 JavaScript 代码,这称为导入省略。

但如果导入整个模块用于副作用,编译时会生成 JavaScript 代码。例如:

// global.ts
type MyGlobal = number 

// app.ts
import './global'

编译后:

// app.js
import './global'

如果你发现自己编写了这样的导入语句,可以先确保该导入确实需要使用副作用,或者尝试重写代码以更明确地导入值或类型。如果这些方法都不适用,并且你想继续使用全模块导入但避免生成 JavaScript 导入或 require 调用,可以使用 types 三斜杠指令:

/// <reference types="./global" />
amd-module 指令

当将 TypeScript 代码编译为 AMD 模块格式时,TypeScript 默认会生成匿名 AMD 模块。可以使用 amd-module 三斜杠指令为生成的模块命名。例如:

/// <amd-module name="LogService" /> 
export let LogService = {  
  log() { 
    // ... 
  }
}

编译后:

/// <amd-module name='LogService' />
define('LogService', ['require', 'exports'], function(require, exports) { 
  exports.__esModule = true 
  exports.LogService = { 
    log() { 
      // ...
    } 
  }
})

在编译为 AMD 模块时,使用 amd-module 指令可以使代码更容易打包和调试。如果可能的话,也可以切换到更现代的模块格式,如 ES2015 模块。

综上所述,通过合理使用上述技术和方法,可以在生产环境中高效地构建、运行和发布 TypeScript 应用程序,同时提高开发效率和代码质量。

TypeScript 生产环境构建与运行全指南

10. 总结

本文全面涵盖了在生产环境中构建和运行 TypeScript 应用程序所需的关键知识,无论是在浏览器还是服务器环境。下面通过表格总结关键要点:
| 要点 | 详细说明 |
| — | — |
| Polyfill 使用 | 避免不必要的 polyfill 增加包体积,使用 Polyfill.io 按需加载,在 tsconfig.json lib 字段声明支持特性 |
| 源映射 | 关联转译代码与源代码,开发和生产环境可用,但注意安全问题 |
| 项目引用 | 拆分项目,配置子项目和根项目的 tsconfig.json ,使用 --build 编译,注意相关使用限制 |
| extends 使用 | 创建基础 tsconfig.json ,子项目继承以减少样板代码 |
| 错误监控 | 使用 Sentry 或 Bugsnag 等工具监控运行时异常 |
| 服务器运行 | 编译为 ES2015 或 ES5,设置 module commonjs ,使用 source-map-support 处理源映射 |
| 浏览器运行 | 选择合适的模块系统,使用强大的打包工具和对应插件,优化包加载速度,填充缺失浏览器特性 |
| 发布到 NPM | 编译为 ES5,生成源映射和类型声明,配置 .npmignore .gitignore package.json |
| 三斜杠指令 | types 指令避免全模块导入生成 JavaScript 代码, amd-module 指令为 AMD 模块命名 |

以下是整个 TypeScript 项目构建和运行的流程图:

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px

    A([开始]):::startend --> B{选择运行环境}:::decision
    B -->|服务器| C(编译为 ES2015/ES5, module: commonjs):::process
    B -->|浏览器| D(选择模块系统):::process
    C --> E(使用 source-map-support 处理源映射):::process
    D --> E1{是否发布库}:::decision
    E1 -->|是| F(使用 umd 格式):::process
    E1 -->|否| G(根据打包工具选择格式):::process
    F --> H(使用打包工具编译):::process
    G --> H
    H --> I(优化包加载速度):::process
    I --> J(填充缺失浏览器特性):::process
    C --> K(错误监控):::process
    J --> K
    K --> L(项目模块化: 项目引用):::process
    L --> M(配置 tsconfig.json: extends 减少样板):::process
    M --> N(发布到 NPM: 编译、配置文件):::process
    N --> O([结束]):::startend
    E --> K

通过遵循这些步骤和建议,开发者可以确保 TypeScript 应用程序在生产环境中高效、稳定地运行,同时提高开发效率和代码的可维护性。在实际应用中,根据项目的具体需求和规模,灵活运用这些技术,不断优化项目构建和运行流程。例如,对于小型项目,可能不需要使用项目引用;而对于大型项目,项目引用和合理的模块化则是提高编译效率的关键。同时,持续关注 TypeScript 和相关工具的更新,及时采用新的特性和优化方法,将有助于提升项目的整体质量和性能。

希望本文能为开发者在 TypeScript 项目的生产环境部署中提供有价值的参考,帮助大家顺利完成项目的构建、运行和发布任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值