Gulp

本快速入门指南将教您如何使用 gulp 构建 TypeScript,以及如何将 BrowserifyterserWatchify 添加到 gulp 流中。本指南还展示了如何使用 Babelify 添加 Babel 功能。

我们假设您已经在使用带有 npmNode.js

最小化项目

让我们从创建一个新目录开始。我们暂时将其命名为 proj,但您可以根据需要更改名称。

shell
mkdir proj
cd proj

首先,我们将按以下方式构建项目结构

proj/
├─ src/
└─ dist/

TypeScript 文件将从 src 文件夹开始,经过 TypeScript 编译器处理,最终生成到 dist 文件夹中。

让我们搭建起这个结构

shell
mkdir src
mkdir dist

初始化项目

现在我们将此文件夹转换为一个 npm 包。

shell
npm init

系统会提示您回答一系列问题。除入口点外,您可以使用默认值。对于入口点,请输入 ./dist/main.js。您随时可以在生成的 package.json 文件中返回并更改这些设置。

安装依赖项

现在我们可以使用 npm install 来安装包。首先全局安装 gulp-cli(如果您使用的是 Unix 系统,可能需要在本指南的 npm install 命令前加上 sudo)。

shell
npm install -g gulp-cli

然后在项目的开发依赖中安装 typescriptgulpgulp-typescriptGulp-typescript 是用于 TypeScript 的 gulp 插件。

shell
npm install --save-dev typescript gulp@4.0.0 gulp-typescript

编写一个简单的示例

让我们编写一个 Hello World 程序。在 src 中,创建文件 main.ts

ts
function hello(compiler: string) {
console.log(`Hello from ${compiler}`);
}
hello("TypeScript");

在项目根目录 proj 中,创建文件 tsconfig.json

{
"": ["src/main.ts"],
"": true,
"": "es5"
}
}

创建 gulpfile.js

在项目根目录中,创建文件 gulpfile.js

js
var gulp = require("gulp");
var ts = require("gulp-typescript");
var tsProject = ts.createProject("tsconfig.json");
gulp.task("default", function () {
return tsProject.src().pipe(tsProject()).js.pipe(gulp.dest("dist"));
});

测试最终应用

shell
gulp
node dist/main.js

该程序应该打印“Hello from TypeScript!”。

为代码添加模块

在进入 Browserify 之前,让我们先构建代码并添加模块。这是您在实际应用中更可能使用的结构。

创建一个名为 src/greet.ts 的文件

ts
export function sayHello(name: string) {
return `Hello from ${name}`;
}

现在更改 src/main.ts 中的代码,以从 greet.ts 导入 sayHello

ts
import { sayHello } from "./greet";
console.log(sayHello("TypeScript"));

最后,将 src/greet.ts 添加到 tsconfig.json

{
"": ["src/main.ts", "src/greet.ts"],
"": true,
"": "es5"
}
}

通过运行 gulp 然后在 Node 中测试,确保模块正常工作

shell
gulp
node dist/main.js

请注意,尽管我们使用了 ES2015 模块语法,但 TypeScript 发出了 Node 使用的 CommonJS 模块。在本教程中,我们将沿用 CommonJS,但您可以设置选项对象中的 module 来更改此行为。

Browserify

现在让我们将此项目从 Node 迁移到浏览器。为此,我们需要将所有模块打包到一个 JavaScript 文件中。幸运的是,这正是 Browserify 所做的。更好的是,它允许我们使用 Node 使用的 CommonJS 模块系统,这也是 TypeScript 默认的发出格式。这意味着我们的 TypeScript 和 Node 设置可以基本保持不变地迁移到浏览器。

首先,安装 browserify、tsify 和 vinyl-source-stream。tsify 是一个 Browserify 插件,像 gulp-typescript 一样,它提供了对 TypeScript 编译器的访问。vinyl-source-stream 允许我们将 Browserify 的文件输出适配回 gulp 可以理解的名为 vinyl 的格式。

shell
npm install --save-dev browserify tsify vinyl-source-stream

创建一个页面

src 中创建一个名为 index.html 的文件

html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello World!</title>
</head>
<body>
<p id="greeting">Loading ...</p>
<script src="bundle.js"></script>
</body>
</html>

现在更改 main.ts 以更新页面

ts
import { sayHello } from "./greet";
function showHello(divName: string, name: string) {
const elt = document.getElementById(divName);
elt.innerText = sayHello(name);
}
showHello("greeting", "TypeScript");

调用 showHello 会调用 sayHello 来更改段落的文本。现在将您的 gulpfile 更改为以下内容

js
var gulp = require("gulp");
var browserify = require("browserify");
var source = require("vinyl-source-stream");
var tsify = require("tsify");
var paths = {
pages: ["src/*.html"],
};
gulp.task("copy-html", function () {
return gulp.src(paths.pages).pipe(gulp.dest("dist"));
});
gulp.task(
"default",
gulp.series(gulp.parallel("copy-html"), function () {
return browserify({
basedir: ".",
debug: true,
entries: ["src/main.ts"],
cache: {},
packageCache: {},
})
.plugin(tsify)
.bundle()
.pipe(source("bundle.js"))
.pipe(gulp.dest("dist"));
})
);

这添加了 copy-html 任务,并将其添加为 default 的依赖项。这意味着每当运行 default 时,必须先运行 copy-html。我们还将 default 更改为调用 Browserify 与 tsify 插件,而不是 gulp-typescript。方便的是,它们都允许我们将相同的选项对象传递给 TypeScript 编译器。

在调用 bundle 后,我们使用 source(我们对 vinyl-source-stream 的别名)将我们的输出包命名为 bundle.js

通过运行 gulp 并然后在浏览器中打开 dist/index.html 来测试页面。您应该会在页面上看到“Hello from TypeScript”。

请注意,我们为 Browserify 指定了 debug: true。这会导致 tsify 在打包的 JavaScript 文件中发出源映射 (source maps)。源映射允许您在浏览器中调试原始的 TypeScript 代码,而不是打包后的 JavaScript。您可以通过打开浏览器的调试器并在 main.ts 中设置断点来测试源映射是否正常工作。当您刷新页面时,断点应该会暂停页面,并允许您调试 greet.ts

Watchify、Babel 和 Terser

既然我们正在使用 Browserify 和 tsify 打包代码,我们可以通过 Browserify 插件为构建添加各种特性。

  • Watchify 会启动 gulp 并保持运行,每当您保存文件时都会进行增量编译。这使您能够在浏览器中保持编辑-保存-刷新的循环。

  • Babel 是一个非常灵活的编译器,它将 ES2015 及更高版本转换为 ES5 和 ES3。这使您能够添加 TypeScript 不支持的广泛且自定义的转换。

  • Terser 会压缩您的代码,从而减少下载时间。

Watchify

我们将从 Watchify 开始,以提供后台编译。

shell
npm install --save-dev watchify fancy-log

现在将您的 gulpfile 更改为以下内容

js
var gulp = require("gulp");
var browserify = require("browserify");
var source = require("vinyl-source-stream");
var watchify = require("watchify");
var tsify = require("tsify");
var fancy_log = require("fancy-log");
var paths = {
pages: ["src/*.html"],
};
var watchedBrowserify = watchify(
browserify({
basedir: ".",
debug: true,
entries: ["src/main.ts"],
cache: {},
packageCache: {},
}).plugin(tsify)
);
gulp.task("copy-html", function () {
return gulp.src(paths.pages).pipe(gulp.dest("dist"));
});
function bundle() {
return watchedBrowserify
.bundle()
.on("error", fancy_log)
.pipe(source("bundle.js"))
.pipe(gulp.dest("dist"));
}
gulp.task("default", gulp.series(gulp.parallel("copy-html"), bundle));
watchedBrowserify.on("update", bundle);
watchedBrowserify.on("log", fancy_log);

这里基本上有三处更改,但它们需要您稍微重构一下代码。

  1. 我们将 browserify 实例包装在对 watchify 的调用中,并保留结果。
  2. 我们调用了 watchedBrowserify.on('update', bundle);,这样每当您的任何 TypeScript 文件发生变化时,Browserify 就会运行 bundle 函数。
  3. 我们调用了 watchedBrowserify.on('log', fancy_log); 来向控制台记录日志。

合起来,(1) 和 (2) 意味着我们必须将对 browserify 的调用移出 default 任务。并且我们必须给 default 的函数起一个名字,因为 Watchify 和 Gulp 都需要调用它。添加 (3) 中的日志记录是可选的,但对于调试您的设置非常有用。

现在,当您运行 Gulp 时,它应该会启动并保持运行。尝试更改 main.tsshowHello 的代码并保存。您应该看到类似于这样的输出

shell
proj$ gulp
[10:34:20] Using gulpfile ~/src/proj/gulpfile.js
[10:34:20] Starting 'copy-html'...
[10:34:20] Finished 'copy-html' after 26 ms
[10:34:20] Starting 'default'...
[10:34:21] 2824 bytes written (0.13 seconds)
[10:34:21] Finished 'default' after 1.36 s
[10:35:22] 2261 bytes written (0.02 seconds)
[10:35:24] 2808 bytes written (0.05 seconds)

Terser

首先安装 Terser。由于 Terser 的目的是混淆您的代码,我们还需要安装 vinyl-buffer 和 gulp-sourcemaps 以保持源映射正常工作。

shell
npm install --save-dev gulp-terser vinyl-buffer gulp-sourcemaps

现在将您的 gulpfile 更改为以下内容

js
var gulp = require("gulp");
var browserify = require("browserify");
var source = require("vinyl-source-stream");
var terser = require("gulp-terser");
var tsify = require("tsify");
var sourcemaps = require("gulp-sourcemaps");
var buffer = require("vinyl-buffer");
var paths = {
pages: ["src/*.html"],
};
gulp.task("copy-html", function () {
return gulp.src(paths.pages).pipe(gulp.dest("dist"));
});
gulp.task(
"default",
gulp.series(gulp.parallel("copy-html"), function () {
return browserify({
basedir: ".",
debug: true,
entries: ["src/main.ts"],
cache: {},
packageCache: {},
})
.plugin(tsify)
.bundle()
.pipe(source("bundle.js"))
.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(terser())
.pipe(sourcemaps.write("./"))
.pipe(gulp.dest("dist"));
})
);

请注意,terser 本身只有一次调用 —— 对 buffersourcemaps 的调用是为了确保源映射保持工作。这些调用为我们提供了一个单独的源映射文件,而不是像以前那样使用内联源映射。现在您可以运行 Gulp,并检查 bundle.js 是否被压缩成了不可读的混乱代码

shell
gulp
cat dist/bundle.js

Babel

首先安装 Babelify 和用于 ES2015 的 Babel 预设。像 Terser 一样,Babelify 会混淆代码,因此我们需要 vinyl-buffer 和 gulp-sourcemaps。默认情况下,Babelify 将仅处理扩展名为 .js.es.es6.jsx 的文件,因此我们需要将 .ts 扩展名添加为 Babelify 的选项。

shell
npm install --save-dev babelify@8 babel-core babel-preset-es2015 vinyl-buffer gulp-sourcemaps

现在将您的 gulpfile 更改为以下内容

js
var gulp = require("gulp");
var browserify = require("browserify");
var source = require("vinyl-source-stream");
var tsify = require("tsify");
var sourcemaps = require("gulp-sourcemaps");
var buffer = require("vinyl-buffer");
var paths = {
pages: ["src/*.html"],
};
gulp.task("copy-html", function () {
return gulp.src(paths.pages).pipe(gulp.dest("dist"));
});
gulp.task(
"default",
gulp.series(gulp.parallel("copy-html"), function () {
return browserify({
basedir: ".",
debug: true,
entries: ["src/main.ts"],
cache: {},
packageCache: {},
})
.plugin(tsify)
.transform("babelify", {
presets: ["es2015"],
extensions: [".ts"],
})
.bundle()
.pipe(source("bundle.js"))
.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(sourcemaps.write("./"))
.pipe(gulp.dest("dist"));
})
);

我们还需要让 TypeScript 的目标设为 ES2015。Babel 然后会从 TypeScript 发出的 ES2015 代码生成 ES5。让我们修改 tsconfig.json

{
"": ["src/main.ts"],
"": true,
"": "es2015"
}
}

Babel 的 ES5 输出对于这样一个简单的脚本应该与 TypeScript 的输出非常相似。

TypeScript 文档是一个开源项目。请发送 Pull Request 来帮助我们改进这些页面 ❤

此页面的贡献者
BKBowden Kelly (51)
OTOrta Therox (15)
DRDaniel Rosenwasser (3)
RCRyan Cavanaugh (2)
MFMartin Fischer (1)
19+

最后更新:2026 年 3 月 27 日