更新
我本来想 skip 掉 bundler (webpack), 感觉单侧不需要搞那么复杂, 所以用了 TypeScript 自带的 bundle (outFile) + SystemJS. 谁知道这技术早就落后 n 年了. 真的是太久没有玩前端了.
想 bundle TypeScript 用 esbuild 是最好的, 又快又简单. 看这篇 TypeScript – Work with JavaScript Library (using esbuild).
前言
之前写的 TypeScript – Get Started 太简单了, 想依赖个 library 都做不到. 于是就有了这篇进阶版本.
这已经是尽可能不依赖 bundler (e.g. Webpack) 情况下, 让它跑起来了.
TypeScript 有基本的 bundler 功能, 配上 SystemJS 就可以做到模块化单元测试了.
参考
medium – TypeScript: Start a Browser-based Project Using the System.js
Youtube – SystemJS Explaination
SystemJS 介绍
在 JavaScript – Modular 有提到过 SystemJS, 前端模块 AMD (RequireJS) > UMD (SystemJS) > ES Module
它属于现阶段最好的过渡方案.
bundle 会把多个 .js 模块变成一个, 这时 ES Module 的 import 就失效了. 所以就要有另一个模块方案. SystemJS 主要就是干这些.
另外, ES Module import 只能写 path (importmap 目前许多 browser 都不支持), 但是我们更希望写名字, 比如 import $ from 'jquery',
SystemJS 的其中一个功能就是 importmap 的 polyfill.
启动项目
mkdir play-typescript cd play-typescript yarn init tsc --init
yarn add typescript --dev
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> <!-- SystemJS --> <script src="https://cdn.jsdelivr.net/npm/systemjs@6.12.1/dist/s.min.js" defer ></script> <script src="https://cdn.jsdelivr.net/npm/systemjs@6.12.1/dist/extras/named-register.min.js" defer ></script> <script src="https://cdn.jsdelivr.net/npm/systemjs@6.12.1/dist/extras/amd.min.js" defer ></script> <!-- importmap --> <script type="systemjs-importmap"> { "imports": { "lodash": "https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js", "jquery": "https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js" } } </script> <!-- bundle.js --> <script type="systemjs-module" src="./bundle.js"></script> </head> <body> <h1>Hello World</h1> </body> </html>
首先是 3 个 SystemJS 的 files
然后是项目依赖的 Library (jquery 和 lodash) importmap
最后是 TypeScript bundle 出来的 bundle.js
注: 都加了 defer 哦
tsconfig
{ "compilerOptions": { "target": "es2016", "module": "System", // 关键 "outFile": "./bundle.js", // 关键 "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true } }
关键就是 module 和 outFile
Typescript
先添加依赖 Library
yarn add jquery yarn add @types/jquery --dev yarn add lodash yarn add @types/lodash --dev
jquery 和 lodash 都没有默认的 .d.ts, 所以需要另外添加 @types
module.ts
import $ from "jquery"; import _ from "lodash"; export function myFunction() { $("h1").css("color", "blue"); _.forEach([1, 2, 3], (index) => $("body").append($(`<h1>${index}</h1>`))); }
index.ts
import { myFunction } from "./module";
myFunction();
运行 tsc and Open with Live Server
运行 command tsc
然后开启 Live Server
效果
坑
TypeScript bundle 出来的 .js 会包含所有的 module, 全部都被 System.register
而最后一个 System.register 会走位 entry point. 这个是 SystemJS 的规则.
视乎没有办法指定 entry point. 这个很麻烦丫. 于是我决定不用 SystemJS, 改用 esbuild bundler.