LayaBox---TypeScript---结构
Posted 格拉格拉
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LayaBox---TypeScript---结构相关的知识,希望对你有一定的参考价值。
目录
1.介绍
一般来讲,你组织声明文件的方式取决于库是如何被使用的。 在javascript中一个库有很多使用方式,这就需要你书写声明文件去匹配它们。本节将帮助你了解常见库的格式以及如何为每种格式书写正确的声明文件。
2.识别库的类型
识别库的类型是书写声明文件的第一步。
2.1全局库
全局库是指能在全局命名空间下访问的(例如:不需要使用任何形式的import
)。 许多库都是简单的暴露出一个或多个全局变量。 比如,如果你使用过 jQuery,$
变量可以被够简单的引用:
$(() => console.log('hello!'); );
你经常会在全局库的指南文档上看到如何在html里用脚本标签引用库:
<script src="http://a.great.cdn.for/someLib.js"></script>
目前,大多数流行的全局访问型库实际上都以UMD库的形式进行书写(见后文)。 UMD库的文档很难与全局库文档两者之间难以区分。 在书写全局声明文件前,一定要确认一下库是否真的不是UMD。
2.2从代码上识别全局库
全局库的代码通常都十分简单。 一个全局的“Hello, world”库可能是这样的:
function createGreeting(s)
return "Hello, " + s;
或这样:
window.createGreeting = function(s)
return "Hello, " + s;
当你查看全局库的源代码时,你通常会看到:
- 顶级的
var
语句或function
声明- 一个或多个赋值语句到
window.someName
- 假设DOM原始值像
document
或window
是存在的你不会看到:
- 检查是否使用或如何使用模块加载器,比如
require
或define
- CommonJS/Node.js风格的导入如
var fs = require("fs");
define(...)
调用- 文档里说明了如何去
require
或导入这个库
2.3模块化库
一些库只能工作在模块加载器的环境下。 比如,像
express
只能在Node.js里工作所以必须使用CommonJS的require
函数加载。
ECMAScript 2015(也就是ES2015,ECMAScript 6或ES6),CommonJS和RequireJS具有相似的导入一个模块的表示方法。 例如,对于JavaScript CommonJS (Node.js),有下面的代码:
var fs = require("fs");
对于TypeScript或ES6,import关键字也具有相同的作用:
import fs = require("fs");
你通常会在模块化库的文档里看到如下说明:
var someLib = require('someLib');
或
define(..., ['someLib'], function(someLib)
);
2.4从代码上识别模块化库
模块库至少会包含下列具有代表性的条目之一:
- 无条件的调用
require
或define
- 像
import * as a from 'b';
orexport c;
这样的声明- 赋值给
exports
或module.exports
它们极少包含:
- 对
window
或global
的赋值
许多流行的Node.js库都是这种模块化的,例如express,gulp和 request。
UMD模块是指那些既可以作为模块使用(通过导入)又可以作为全局(在没有模块加载器的环境里)使用的模块。 许多流行的库,比如 Moment.js,就是这样的形式。 比如,在Node.js或RequireJS里,你可以这样写:
import moment = require("moment");
console.log(moment.format());
然而在纯净的浏览器环境里你也可以这样写:
console.log(moment.format());
2.5识别UMD库
UMD模块会检查是否存在模块加载器环境。
(function (root, factory)
if (typeof define === "function" && define.amd)
define(["libName"], factory);
else if (typeof module === "object" && module.exports)
module.exports = factory(require("libName"));
else
root.returnExports = factory(root.libName);
(this, function (b)
如果你在库的源码里看到了typeof define
,typeof window
,或typeof module
这样的测试,尤其是在文件的顶端,那么它几乎就是一个UMD库。
UMD库的文档里经常会包含通过require
“在Node.js里使用”例子, 和“在浏览器里使用”的例子,展示如何使用 <script>
标签去加载脚本。
大多数流行的库现在都能够被当成UMD包。 比如 jQuery,Moment.js,lodash和许多其它的。
2.6模版
针对模块有三种可用的模块, module.d.ts, module-class.d.ts and module-function.d.ts.
使用module-class.d.ts如果模块能够使用new
来构造:
var x = require("bar");
// Note: using 'new' operator on the imported variable
var y = new x("hello");
3.模块插件或UMD插件
一个模块插件可以改变一个模块的结构(UMD或模块)。
例如,在Moment.js里, moment-range
添加了新的range
方法到monent
对象。
模版:使用module-plugin.d.ts模版。
4.全局插件
一个全局插件是全局代码,它们会改变全局对象的结构。 对于 全局修改的模块,在运行时存在冲突的可能。
比如,一些库往Array.prototype
或String.prototype
里添加新的方法。
4.1识别全局插件
var x = "hello, world";
// Creates new methods on built-in types
console.log(x.startsWithHello());
var y = [1, 2, 3];
// Creates new methods on built-in types
console.log(y.reverseAndSort());
模版:同上
5.全局修改的模块
当一个全局修改的模块被导入的时候,它们会改变全局作用域里的值。 比如,存在一些库它们添加新的成员到
String.prototype
当导入它们的时候。
这种模式很危险,因为可能造成运行时的冲突, 但是我们仍然可以为它们书写声明文件。
5.1识别全局修改的模块
全局修改的模块通常可以很容易地从它们的文档识别出来。 通常来讲,它们与全局插件相似,但是需要 require
调用来激活它们的效果。
// 'require' call that doesn't use its return value
var unused = require("magic-string-time");
/* or */
require("magic-string-time");
var x = "hello, world";
// Creates new methods on built-in types
console.log(x.startsWithHello());
var y = [1, 2, 3];
// Creates new methods on built-in types
console.log(y.reverseAndSort());
模版:使用global-modifying-module.d.ts模版。
6.使用依赖
6.1依赖全局库
如果你的库依赖于某个全局库,使用/// <reference types="..." />
指令:
/// <reference types="someLib" />
function getThing(): someLib.thing;
6.2依赖模块
如果你的库依赖于模块,使用import
语句:
import * as moment from "moment";
function getThing(): moment;
7.依赖UMD库
7.1从全局库
如果你的全局库依赖于某个UMD模块,使用/// <reference types
指令:
/// <reference types="moment" />
function getThing(): moment;
7.2从一个模块或UMD库
如果你的模块或UMD库依赖于一个UMD库,使用import
语句:
import * as someLib from 'someLib';
不要使用/// <reference
指令去声明UMD库的依赖!
8.补充说明
8.1防止命名空间冲突
⚠️注意:在书写全局声明文件时,允许在全局作用域里定义很多类型。 我们十分不建义这样做,当一个工程里有许多声明文件时,它会导致无法处理的命名冲突。
一个简单的规则是使用库定义的全局变量名来声明命名空间类型。 比如,库定义了一个全局的值 cats
,你可以这样写:
declare namespace cats
interface KittySettings
不要
// at top-level
interface CatsKittySettings
对于声明文件用户来说,保证了库在转换成UMD的时候没有任何的破坏式改变。
8.2 ES6模块插件的影响
一些插件添加或修改已存在的顶层模块的导出部分。 当然这在CommonJS和其它加载器里是允许的,ES模块被当作是不可改变的因此这种模式就不可行了。 因为TypeScript是能不预知加载器类型的,所以没没在编译时保证,但是开发者如果要转到ES6模块加载器上应该注意这一点。
8.3 ES6模块调用签名的影响
很多流行库,比如Express,暴露出自己作为可以调用的函数。 比如,典型的Express使用方法如下:
import exp = require("express");
var app = exp();
在ES6模块加载器里,顶层的对象(这里以exp
导入)只能具有属性; 顶层的模块对象 永远不能被调用。 十分常见的解决方法是定义一个 default
导出到一个可调用的/可构造的对象; 一会模块加载器助手工具能够自己探测到这种情况并且使用 default
导出来替换顶层对象。
以上是关于LayaBox---TypeScript---结构的主要内容,如果未能解决你的问题,请参考以下文章