前言

JavaScript 早期是没有 Modular 和 Type (类型) 的. 随着这几年的普及, 几乎有维护的 Library 都有 Modular 和 Type 了.

但万一遇到没有 Modular 或者 Type 的 LIbrary 时, 要如何 import 和类型安全的调用它们呢?

这篇就是要讲讲这些麻烦事儿. 以前做 Angular 开发的时候也写过一篇相关的文章, 但那篇是基于 Angular 的, 这篇则底层一点, 只用了 TypeScript 和 esbuild

esbuild 介绍

它是一个 JavaScript / TypeScript 打包工具. 它的特点就是 TMD 的快

Webpack 需要 40秒 打包的内容, 它只需要 0.33秒, 为什么它这么快呢? 可以看这篇 下一个时代的打包工具 esbuild, 主要是利用了 ES Module 和 Go 语言.

但同时它也牺牲了很多东西, 可以看这篇 Esbuild 为什么那么快, 比如: 不支持 ts 类型检查, 无法 transpile 到 es5, 没有 AST 树做扩展等等.

vite,snowpack,Webpack esbuild-loader都是基于它.

esbuild get started 看这篇就可以了, 超级简单的

The Limitation with TypeScript

参考: Docs – Features that need a type system are not supported

先讲一下 esbuild 对 TypeScript 的一些局限.

1. 不支持 tsconfig.json 中的 emitDecoratorMetadata,  这个是配合反射 (reflect-metadata) 用的. 复杂项目会是好帮手

2. 不支持 declaration, output 无法输出 .d.ts, 做 library 的话不适合用 esbuild

所以如果你遇到以上情况, 那就不能用 esbuild 了 (esbuild-loader 也是不能用)

搭环境

npm i typescript -g
npm i esbuild -g
mkdir work-with-js-library
cd work-with-js-library
yarn init
tsc --init
yarn add typescript --dev
yarn add esbuild --dev

for Yarn 3 的话, 请改成 yarn init -2

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="./bundle.js" defer></script>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>

package.json

{
"scripts": {
"esbuild": "esbuild index.ts --bundle --outfile=bundle.js --watch"
}
}

p.s 如果有 setup esbuild.js config 的话, 就不使用上面这个 scripe 了, 往下看

for Yarn 3 的话, 不能直接使用 esbuild command

先安装 esbuild plugin

更新: 20-02-2023 从 v0.15.0 以后 native 就直接支持 PnP 了. 不需要在安装 plugin (安装反而会有 bug, 比如 TS + PnP + esbuild + RxJS 会 error)

yarn add @yarnpkg/esbuild-plugin-pnp --dev

再搞一个 esbuild.js config file (虽然 native 支持 PnP 后, 我们不必在安装 plugin 同时也就没有必要弄 config 了, 但 config 在真实开发还是需要的, 比如 file path 等等还是得 config 配置.)

const esbuild = require("esbuild");
const { pnpPlugin } = require("@yarnpkg/esbuild-plugin-pnp"); esbuild
.context({
entryPoints: ["./index.ts"],
minify: process.env.NODE_ENV === "production",
bundle: true,
outfile: "./bundle.js",
sourcemap: true,
plugins: [pnpPlugin()],
})
.then((ctx) => {
ctx.watch();
});

运行 command 要加 yarn 在前面.

yarn node esbuild.js

Import JavaScript library that have modular and @type

这个是最常见的一种情况, 像 jquery, lodash. 它们都有 modular, 虽然没有直接提供 Type Declarations, 但是在 npm 大家庭 里面能找到.

安装模块和 @types

yarn add jquery
yarn add @types/jquery --dev yarn add lodash
yarn add @types/lodash --dev

tsconfig 什么都不需要调, 因为 typesRoot 默认就会去 node_modules/@types 里面找类型.

index.ts

import $ from 'jquery';
import _ from 'lodash'; $('h1').css('color', 'red');
_.forEach([1, 2, 3], (index) => $('body').append($(`<h1>${index}</h1>`)));

直接 import 就能调用了. jquery 和 lodash 都是 export default.

效果

npm run esbuild > Open with Live Server

Import no modular and no type JavaScript library

这种 Library 通常已经没用人维护了. 它的使用方式往往是叫你在 html 放 <script>

然后通过 window['library'].anyFunction() 或者全局变量 library.anyFunction() 去调用.

global-js-library.js

window['globalJsLibrary'] = {
myVar: 'globalJsLibrary myVar',
myFunction: () => {
console.log('globalJsLibrary myFunction called');
},
};
const globalJsLibrary = window['globalJsLibrary']; Object.defineProperty(String.prototype, 'firstLetterUppercase', {
value() {
return this.charAt(0).toUpperCase() + this.substring(1);
},
});

没有 modular 也没有类型, 直接通过 window 和全局变量曝露接口.

首先做一个 Type Declarations

global-js-library.d.ts

interface Window {
globalJsLibrary: {
myVar: string;
myFunction: () => void;
};
} interface String {
firstLetterUppercase(): string;
} declare var globalJsLibrary: Window['globalJsLibrary'];

这个文件要放在哪里呢?

by right, 应该放到 ./types 里面, 然后在 tsconfig.json 设置

{
"compilerOptions": {
"typeRoots": ["./node_modules/@types", "./src/types"]
}
}

typeRoots 的默认是 node_modules/@types, override 了记得要补回去.

顺便说一下 types 是指在 typeRoots 里面再选出某一些 type 而已. 比如

"types": ["jquery", "global-js-library"]

但是我发现, 即使不设置 typeRoots, 而且不管 .d.ts 放到哪里, 最终都是可以跑的. 懒得去研究了.

我就放在 .js 隔壁

index.ts

import 'global-js-library';

console.log(globalJsLibrary.myVar);
console.log(window.globalJsLibrary.myVar);
console.log('hello world'.firstLetterUppercase());

有了类型, 就可以调用到了.

注意它的 import 路径是和 jquery, lodash 一样的, 放 module name.

我这个 .js 文件没有放到 node_modules 里面, 但大部分情况这类 library 还是会有 npm 下载的 (除非真的太旧了).

没有在 node_modules 就不可以直接通过 module name import, 因为它不知道 source 在哪里.

这时需要去 tsconfig.json 加上 paths

{
"compilerOptions": {
"paths": {
"global-js-library": ["./global-js-library.js"]
}
}
}

接着 npm run esbuild > Run with Live Server 就看见效果了.

Import no type JavaScript library

上面的例子比较极端, 连 modular 都没有. 但大部分 library 都是有的, 顶多没有类型而已.

这个例子教你如果写各种类型 export 和如何 import 它们.

no-type-library.js

es2020 版本

export const myVar = 'myVar';
export function myFunction() {
console.log('myFunction called');
}
export default class {
constructor() {
this.name = 'Derrick';
}
}

UMD 版本

(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.myFunction = exports.myVar = void 0;
exports.myVar = 'myVar';
function myFunction() {
console.log('myFunction called');
}
exports.myFunction = myFunction;
class default_1 {
constructor() {
this.name = 'Derrick';
}
}
exports.default = default_1;
});

注: esbuild 只支持 UMD 和 ES Module, 2 种 modular 规范而已. 其它的比如: AMD, System 都是不支持的哦. Github Issue – Support AMD as input format

no-type-library.d.ts

declare module 'no-type-library' {
export const myVar = 'myVar';
export function myFunction(): void;
export default class {
name: string;
}
}

index.ts

import myClass, { myFunction, myVar } from 'no-type-library';
myFunction();
console.log('myVar', myVar);
console.log('myClass', new myClass());

tsconfig.json

{
"compilerOptions": {
"paths": {
"no-type-library": ["./no-type-library.js"]
}
}
}

如果 .js 在 node_modules 里就不需要 paths

npm run esbuild > Run with Live Server 就看见效果了.

Multiple Entry Points

在做 Web Worker 单元测试时, 需要使用 esbuild 的多路径.

参考:

Docs – Entry points

Github – WebWorker support?

每一个 entryPoints 都会输出一个 file 在 outdir. 上面这个例子就会创建出 /bundle/index.js 和 /bundle/worker.js

如果 index.ts 内有 import 其它 ts file 那么会被 bundle 起来. worker.ts 内有 import ts 也是会打包. 注: 它没有 shared 的概念, 两条打包路径是独立的

但如果是用 importScripts('xxx.js') 则不会 bundle 哦, 另外, importScripts 需要 declare

declare function importScripts(...urls: string[]): void;

修改 index.html 路径

引入 worker.js

 

TypeScript – Work with JavaScript Library (using esbuild)的更多相关文章

  1. A javascript library providing cross-browser, cross-site messaging/method invocation. http://easyxdm.net

    easyXDM - easy Cross-Domain Messaging easyXDM is a Javascript library that enables you as a develope ...

  2. 使用TypeScript如何提升JavaScript编程效果?

    TypeScript是个什么鬼?和JavaScript有什么关系? TypeScript是由微软开发的一种可快速入门的开源的编程语言,是JavaScript的一个超集,且向这个语言添加了可选的静态类型 ...

  3. Dynamices CRM JS 类库 神器 XrmServiceToolkit - A Microsoft Dynamics CRM 2011 & CRM 2013 JavaScript Library

    XrmServiceToolkit - A Microsoft Dynamics CRM 2011 & CRM 2013 JavaScript Library http://xrmservic ...

  4. Raphaël—JavaScript Library

    Raphaël-JavaScript Library What is it? Raphaël is a small JavaScript library that should simplify yo ...

  5. 使用Typescript来写javascript

    使用Typescript来写javascript 前几天尝试使用haxejs来写javascript,以获得静态类型带来的益处.虽然成功了,但很快发现将它与angularjs一起使用,有一些不太顺畅的 ...

  6. a Javascript library for training Deep Learning models

    w强化算法和数学,来迎接机器学习.神经网络. http://cs.stanford.edu/people/karpathy/convnetjs/ ConvNetJS is a Javascript l ...

  7. JavaScript 工具库:Cloudgamer JavaScript Library v0.1 发布

    JavaScript 工具库:Cloudgamer JavaScript Library v0.1 发布   研究了一年多的js,也差不多写一个自己的js库了.我写这个不算框架,只是一个小型的js工具 ...

  8. jQuery JavaScript Library v3.2.1

    /*! * jQuery JavaScript Library v3.2.1 * https://jquery.com/ * * Includes Sizzle.js * https://sizzle ...

  9. A JavaScript library for reading EXIF meta data from image files.

    exif-js/exif-js: JavaScript library for reading EXIF image metadata https://github.com/exif-js/exif- ...

  10. eval5: TypeScript编写的JavaScript解释器

    eval5是基于TypeScript编写的JavaScript解释器,100%支持ES5语法. 项目地址:https://github.com/bplok20010/eval5 使用场景 浏览器环境中 ...

随机推荐

  1. Swift开发基础02-流程控制

    if-slse let age = 4 if age >= 22 { print("Get married") } else if age >= 18 { print( ...

  2. 2023 NOIP 游记

    \(\text{Day -INF}\) 提高 \(135\) 卡线进 \(\text{NOIP}\). 集训两天成绩:\(50 \to 135\). \(\text{Day 1}\) 开赛 \(13\ ...

  3. Prometheus 基于Python Django实现Prometheus Exporter

    基于Python Django实现Prometheus Exporter 需求描述 运行监控需求,需要采集Nginx 每个URL请求的相关信息,涉及两个指标:一分钟内平均响应时间,调用次数,并且为每个 ...

  4. 记一次eclipse导入的JavaEE项目无法连接数据库的排查

    1.问题描述 Eclipse导入了一个JavaEE项目 在虚拟机环境中新建了一个数据库 数据库可以使用本地客户端工具正常连接 导入的JavaEE项目修改了数据源配置后无法启动 相同的数据源配置通过在I ...

  5. 【Git】Git拉取失败,报错超出内存,内存分配失败

    报错信息: Microsoft Windows [版本 6.1.7601] 版权所有 (c) 2009 Microsoft Corporation.保留所有权利. C:\Users\Administr ...

  6. 【MySQL】Windows-5.7.30 解压版 下载安装

    1.Download 下载 mysql官网: https://dev.mysql.com/ 找到download点击进入下载页面: https://dev.mysql.com/downloads/ 找 ...

  7. Apache DolphinScheduler社区又一PMC获推选通过!

    PROFILE 姓名:程鑫 公司:阿里云 职位:开发工程师 Github ID: rickchengx 从事领域:大数据调度系统开发 兴趣爱好:健身 推举理由 他于2022年8月2日开始了他的Dolp ...

  8. 【VMware vCenter】一次性说清楚 vCenter Server 的 CLI 部署方式。

    VMware vCenter Server 是 VMware vSphere 解决方案的核心组件,用于管理多个 ESXi 主机并实现更多高级功能特性(如 HA.DRS 以及 FT 等),相信大家已经非 ...

  9. MPTCP(三) : 在内核中启用MPTCP相关模块

    在内核中启用MPTCPv1相关的模块 1. 简介 本文所涉及的均为MPTCPv1版本的MPTCP,采用的内核版本为5.18.19 需要注意的是 虽然MPTCP官方文档中提到在5.6之后的linux内核 ...

  10. 安卓如何设置开机启动某个程序?init.rc给你搞定

    一.如何设置开机启动某个程序? 1.需求描述 最近有个项目需要在Android开机启动之后,自动执行一个C语言编写的程序:pengd 该程序运行时需要修改网络ip地址及其他网络操作,所以需要root权 ...