本快速入门指南将教你如何使用 gulp 构建 TypeScript,然后将 Browserify、terser 或 Watchify 添加到 gulp 管道中。本指南还介绍了如何使用 Babelify 添加 Babel 功能。
¥This quick start guide will teach you how to build TypeScript with gulp and then add Browserify, terser, or Watchify to the gulp pipeline. This guide also shows how to add Babel functionality using Babelify.
¥We assume that you’re already using Node.js with npm.
最小项目
¥Minimal project
让我们从一个新目录开始。我们暂时将其命名为 proj
,但你可以将其更改为你想要的任何名称。
¥Let’s start out with a new directory.
We’ll name it proj
for now, but you can change it to whatever you want.
shell
mkdir projcd proj
首先,我们将按以下方式构建我们的项目:
¥To start, we’re going to structure our project in the following way:
proj/├─ src/└─ dist/
TypeScript 文件将在你的 src
文件夹中开始,通过 TypeScript 编译器运行并在 dist
中结束。
¥TypeScript files will start out in your src
folder, run through the TypeScript compiler and end up in dist
.
让我们把它搭建起来:
¥Let’s scaffold this out:
shell
mkdir srcmkdir dist
初始化项目
¥Initialize the project
现在我们将把这个文件夹变成一个 npm 包。
¥Now we’ll turn this folder into an npm package.
shell
npm init
你将收到一系列提示。你可以使用默认值,但你的入口点除外。对于你的入口点,请使用 ./dist/main.js
。你可以随时返回并在为你生成的 package.json
文件中更改这些内容。
¥You’ll be given a series of prompts.
You can use the defaults except for your entry point.
For your entry point, use ./dist/main.js
.
You can always go back and change these in the package.json
file that’s been generated for you.
安装我们的依赖
¥Install our dependencies
现在我们可以使用 npm install
来安装包了。首先全局安装 gulp-cli
(如果使用 Unix 系统,可能需要在本指南中的 npm install
命令前加上 sudo
前缀)。
¥Now we can use npm install
to install packages.
First install gulp-cli
globally (if you use a Unix system, you may need to prefix the npm install
commands in this guide with sudo
).
shell
npm install -g gulp-cli
然后在项目的开发依赖中安装 typescript
、gulp
和 gulp-typescript
。Gulp-typescript 是 TypeScript 的 gulp 插件。
¥Then install typescript
, gulp
and gulp-typescript
in your project’s dev dependencies.
Gulp-typescript is a gulp plugin for TypeScript.
shell
npm install --save-dev typescript gulp@4.0.0 gulp-typescript
写一个简单的例子
¥Write a simple example
让我们编写一个 Hello World 程序。在 src
中,创建文件 main.ts
:
¥Let’s write a Hello World program.
In src
, create the file main.ts
:
ts
function hello(compiler: string) {console.log(`Hello from ${compiler}`);}hello("TypeScript");
在项目根目录 proj
中,创建文件 tsconfig.json
:
¥In the project root, proj
, create the file tsconfig.json
:
{" ": ["src/main.ts"]," ": {" ": true," ": "es5"}}
创建一个 gulpfile.js
¥Create a gulpfile.js
在项目根目录中,创建文件 gulpfile.js
:
¥In the project root, create the file 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"));});
测试生成的应用
¥Test the resulting app
shell
gulpnode dist/main.js
该程序应打印 “Hello from TypeScript!“。
¥The program should print “Hello from TypeScript!“.
在代码中添加模块
¥Add modules to the code
在我们使用 Browserify 之前,让我们构建我们的代码并添加模块。这是你更有可能用于实际应用的结构。
¥Before we get to Browserify, let’s build our code out and add modules to the mix. This is the structure you’re more likely to use for a real app.
创建一个名为 src/greet.ts
的文件:
¥Create a file called src/greet.ts
:
ts
export function sayHello(name: string) {return `Hello from ${name}`;}
现在更改 src/main.ts
中的代码以从 greet.ts
导入 sayHello
:
¥Now change the code in src/main.ts
to import sayHello
from greet.ts
:
ts
import { sayHello } from "./greet";console.log(sayHello("TypeScript"));
最后,将 src/greet.ts
添加到 tsconfig.json
:
¥Finally, add src/greet.ts
to tsconfig.json
:
{" ": ["src/main.ts", "src/greet.ts"]," ": {" ": true," ": "es5"}}
通过运行 gulp
然后在 Node 中测试来确保模块工作:
¥Make sure that the modules work by running gulp
and then testing in Node:
shell
gulpnode dist/main.js
请注意,即使我们使用 ES2015 模块语法,TypeScript 也会触发 Node 使用的 CommonJS 模块。在本教程中,我们将坚持使用 CommonJS,但你可以在选项对象中设置 module
来更改它。
¥Notice that even though we used ES2015 module syntax, TypeScript emitted CommonJS modules that Node uses.
We’ll stick with CommonJS for this tutorial, but you could set module
in the options object to change this.
Browserify
现在让我们把这个项目从 Node 移动到浏览器。为此,我们希望将所有模块打包到一个 JavaScript 文件中。幸运的是,这正是 Browserify 所做的。更好的是,它让我们可以使用 Node 使用的 CommonJS 模块系统,这是默认的 TypeScript 触发的。这意味着我们的 TypeScript 和 Node 设置将基本不变地传输到浏览器。
¥Now let’s move this project from Node to the browser. To do this, we’d like to bundle all our modules into one JavaScript file. Fortunately, that’s exactly what Browserify does. Even better, it lets us use the CommonJS module system used by Node, which is the default TypeScript emit. That means our TypeScript and Node setup will transfer to the browser basically unchanged.
首先,安装 browserify、tsify 和 vinyl-source-stream。tsify 是一个 Browserify 插件,与 gulp-typescript 一样,可以访问 TypeScript 编译器。vinyl-source-stream 让我们将 Browserify 的文件输出调整回 gulp 理解的格式,称为 vinyl。
¥First, install browserify, tsify, and vinyl-source-stream. tsify is a Browserify plugin that, like gulp-typescript, gives access to the TypeScript compiler. vinyl-source-stream lets us adapt the file output of Browserify back into a format that gulp understands called vinyl.
shell
npm install --save-dev browserify tsify vinyl-source-stream
创建一个页面
¥Create a page
在 src
中创建名为 index.html
的文件:
¥Create a file in src
named 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
以更新页面:
¥Now change main.ts
to update the page:
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 更改为以下内容:
¥Calling showHello
calls sayHello
to change the paragraph’s text.
Now change your gulpfile to the following:
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
以使用 tsify 插件而不是 gulp-typescript 调用 Browserify。方便的是,它们都允许我们将相同的选项对象传递给 TypeScript 编译器。
¥This adds the copy-html
task and adds it as a dependency of default
.
That means any time default
is run, copy-html
has to run first.
We’ve also changed default
to call Browserify with the tsify plugin instead of gulp-typescript.
Conveniently, they both allow us to pass the same options object to the TypeScript compiler.
在调用 bundle
之后,我们使用 source
(我们对 vinyl-source-stream 的别名)来命名我们的输出包 bundle.js
。
¥After calling bundle
we use source
(our alias for vinyl-source-stream) to name our output bundle bundle.js
.
通过运行 gulp 然后在浏览器中打开 dist/index.html
来测试页面。你应该会在页面上看到 “Hello from TypeScript”。
¥Test the page by running gulp and then opening dist/index.html
in a browser.
You should see “Hello from TypeScript” on the page.
请注意,我们将 debug: true
指定给 Browserify。这会导致 tsify 在打包的 JavaScript 文件中触发源映射。源映射让你可以在浏览器中调试原始 TypeScript 代码,而不是打包的 JavaScript。你可以通过打开浏览器的调试器并在 main.ts
中放置一个断点来测试源映射是否正常工作。当你刷新页面时,断点应该暂停页面并让你调试 greet.ts
。
¥Notice that we specified debug: true
to Browserify.
This causes tsify to emit source maps inside the bundled JavaScript file.
Source maps let you debug your original TypeScript code in the browser instead of the bundled JavaScript.
You can test that source maps are working by opening the debugger for your browser and putting a breakpoint inside main.ts
.
When you refresh the page the breakpoint should pause the page and let you debug greet.ts
.
Watchify、Babel 和 Terser
¥Watchify, Babel, and Terser
现在我们将我们的代码与 Browserify 和 tsify 打包在一起,我们可以使用 browserify 插件将各种功能添加到我们的构建中。
¥Now that we are bundling our code with Browserify and tsify, we can add various features to our build with browserify plugins.
-
Watchify 启动 gulp 并保持运行,每当你保存文件时都会进行增量编译。这使你可以在浏览器中保持编辑-保存-刷新循环。
¥Watchify starts gulp and keeps it running, incrementally compiling whenever you save a file. This lets you keep an edit-save-refresh cycle going in the browser.
-
Babel 是一个非常灵活的编译器,可以将 ES2015 及更高版本转换为 ES5 和 ES3。这使你可以添加 TypeScript 不支持的大量自定义转换。
¥Babel is a hugely flexible compiler that converts ES2015 and beyond into ES5 and ES3. This lets you add extensive and customized transformations that TypeScript doesn’t support.
-
Terser 压缩你的代码,以减少下载时间。
¥Terser compacts your code so that it takes less time to download.
Watchify
我们将从 Watchify 开始提供后台编译:
¥We’ll start with Watchify to provide background compilation:
shell
npm install --save-dev watchify fancy-log
现在将你的 gulpfile 更改为以下内容:
¥Now change your gulpfile to the following:
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);
这里基本上有三个更改,但它们需要你稍微重构一下代码。
¥There are basically three changes here, but they require you to refactor your code a bit.
-
我们将
browserify
实例封装在对watchify
的调用中,然后保留结果。¥We wrapped our
browserify
instance in a call towatchify
, and then held on to the result. -
我们调用了
watchedBrowserify.on('update', bundle);
,这样 Browserify 就会在每次你的 TypeScript 文件发生变化时运行bundle
函数。¥We called
watchedBrowserify.on('update', bundle);
so that Browserify will run thebundle
function every time one of your TypeScript files changes. -
我们调用
watchedBrowserify.on('log', fancy_log);
登录到控制台。¥We called
watchedBrowserify.on('log', fancy_log);
to log to the console.
(1) 和 (2) 一起意味着我们必须将对 browserify
的调用从 default
任务中移出。我们必须为 default
的函数命名,因为 Watchify 和 Gulp 都需要调用它。使用 (3) 添加日志记录是可选的,但对于调试设置非常有用。
¥Together (1) and (2) mean that we have to move our call to browserify
out of the default
task.
And we have to give the function for default
a name since both Watchify and Gulp need to call it.
Adding logging with (3) is optional but very useful for debugging your setup.
现在,当你运行 Gulp 时,它应该会启动并保持运行。尝试更改 main.ts
中 showHello
的代码并保存。你应该看到如下所示的输出:
¥Now when you run Gulp, it should start and stay running.
Try changing the code for showHello
in main.ts
and saving it.
You should see output that looks like this:
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 来保持 sourcemaps 工作。
¥First install Terser. Since the point of Terser is to mangle your code, we also need to install vinyl-buffer and gulp-sourcemaps to keep sourcemaps working.
shell
npm install --save-dev gulp-terser vinyl-buffer gulp-sourcemaps
现在将你的 gulpfile 更改为以下内容:
¥Now change your gulpfile to the following:
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
本身只有一个调用 - 对 buffer
和 sourcemaps
的调用的存在是为了确保源映射继续工作。这些调用为我们提供了一个单独的源映射文件,而不是像以前那样使用内联源映射。现在你可以运行 Gulp 并检查 bundle.js
是否被压缩成一团乱七八糟的东西:
¥Notice that terser
itself has just one call — the calls to buffer
and sourcemaps
exist to make sure sourcemaps keep working.
These calls give us a separate sourcemap file instead of using inline sourcemaps like before.
Now you can run Gulp and check that bundle.js
does get minified into an unreadable mess:
shell
gulpcat dist/bundle.js
Babel
首先安装 Babelify 和 ES2015 的 Babel preset。与 Terser 一样,Babelify 会破坏代码,因此我们需要 vinyl-buffer 和 gulp-sourcemaps。默认情况下,Babelify 只会处理扩展名为 .js
、.es
、.es6
和 .jsx
的文件,因此我们需要将 .ts
扩展名作为选项添加到 Babelify。
¥First install Babelify and the Babel preset for ES2015.
Like Terser, Babelify mangles code, so we’ll need vinyl-buffer and gulp-sourcemaps.
By default Babelify will only process files with extensions of .js
, .es
, .es6
and .jsx
so we need to add the .ts
extension as an option to Babelify.
shell
npm install --save-dev babelify@8 babel-core babel-preset-es2015 vinyl-buffer gulp-sourcemaps
现在将你的 gulpfile 更改为以下内容:
¥Now change your gulpfile to the following:
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
:
¥We also need to have TypeScript target ES2015.
Babel will then produce ES5 from the ES2015 code that TypeScript emits.
Let’s modify tsconfig.json
:
{" ": ["src/main.ts"]," ": {" ": true," ": "es2015"}}
对于这样一个简单的脚本,Babel 的 ES5 输出应该与 TypeScript 的输出非常相似。
¥Babel’s ES5 output should be very similar to TypeScript’s output for such a simple script.