amd.ts 原生amd模块

tsconfig.json中支持outFile配置需要amd/system模块支持。

采用amd模块编译后的ts项目会引入define函数,其结构最简单,故以amd模块为例说明,结构如下:

// name 模块名
// dependencies 模块的依赖,字符串名称数组
// definition 模块的实现函数或着模块本身(如json模块)
define(name: string, dependencies: string[], definition)

因此只要最终文件的开头实现define函数,即可保证amd模块导出的文件可以独立运行。

基于以上要求,可以采取以下方案:

  • 创建amd.ts文件,并将其配置于tsconfig.json 的files配置中首位,即可确保执行顺序靠前;
  • amd.ts内部实现define函数。

define函数的算法如下:

  • 定义全局的模块缓存
  • 每次define调用时,对依赖项进行扫描,判断依赖项是否完成初始化,完成则直接触发definition函数,未完成则标记为待完成。

但是分析后发现以上逻辑无法解决滞后定义的模块被前置模块依赖的现象,因此对define函数继续进行以下优化:

  • define函数未完成则执行一个异步等待,遍历所有未完成初始化的模块,检测其依赖是否完成

但是依然可能存在交叉依赖现象,继续优化:

  • 采取递归方式获取一个模块的所有不重复向上依赖
  • 强制按照define执行顺序执行模块初始化

基本解决交叉依赖现象(webpack的算法)。

为什么说是基本解决呢,因为有一种交叉依赖的情况可能导致以上算法失效。即AB相互依赖,但是A中初始化模块导出时执行了B的初始化,但是B还未初始化。

目前这是一个无解的问题,毕竟不是ts没有真正的静态语言运行时环境。虽然tsc已经尽量按照依赖顺序进行编译,但对交叉依赖的情况依旧是简单的依靠字母顺序进行排序,所以只能请开发者自行养成良好的开发习惯,这是目前任何工具都解决不了的问题。

补充:如若用于node环境下,判断模块可用时多加一个require判断。

通过以上方案,即可以开发出一个通用的amd.ts模块。用纯ts+tsc即可编译为单个文件。