npm类库最佳实践

多年前我一直有一个困扰,那就是ts(js)的类库到底要怎么写?
习惯了古老语言AS3的人,会想当然的用命名空间来作为自己类库的标识,比如’com.adobe’之类,但是看了Laya和Egret的代码会发现也是一模一样的思路,那就是给自己设定一个全局的命名空间(Laya.Sprite or Egret.Sprite),虽然TypeScript本身就是支持命名空间的,但是实际上在一些标准类库中(指的是那些国外的开源软件,大家约定的一样),并不是这么写的。他们的类库用起来是这样导包的:

1
import { Scene } from 'THREE';

而用Laya和Egret的核心库,则是全局的不用导包,这到底有什么区别呢?(这里不讨论umd,module等js的历史,像裹脚布一样又臭又长)。
区别是Laya和Egret的核心代码,其实是挂载在window上的,所以可以直接全局的调用。而标准的js类库中并不是,是按需导入的。按需导入的好处就是可以在编译层面(比如利用webpack)来实现只编译自己需要的代码,而不是像Egret和Laya那样一股脑全包含进去。

标准库的写法里还有个区别,那就是导包的方式:

1
2
import { Scene } from 'THREE.JS';
import { Scene } from '@xxxlib/utils/scene';

看到区别没,第二种写法其实就是懒得在根目录写一个index.ts的导出文件,不写很正常,因为真的很繁琐,需要每新增一个ts文件就写一行。如果你的类库应用的场景比较少,可以这么做,但是如果你的类库用到好多个项目中,而且一旦改动涉及到的工程还蛮多的,建议还是采用第一个方式,我们目前的项目就遇到了这个问题,当架构师发现之前的一些包目录结构已经不满足现在的代码结构需要,需要重新组织目录的时候,各个项目都要跟着一起改动。如果用第一种方式就不会有这个问题。
所以谨慎、标准起见,写npm类库最好的姿势是(用typescript):

  • 在代码的每个文件夹下创建index.ts文件,并将当前目录需要导出的代码文件设置为导出。根目录的index.ts目录用来导出各个目录模块。
  • 使用ES6语法,不要namespace,不要module