本快速入門指南將教您如何使用 gulp 建置 TypeScript,然後將 Browserify、terser 或 Watchify 加入 gulp 管線。本指南也會說明如何使用 Babel 功能,方法是使用 Babelify。
最小專案
讓我們從一個新目錄開始。我們先將其命名為 proj
,但您可以將其變更為您想要的任何名稱。
shell
mkdir projcd proj
首先,我們將專案結構設定如下
proj/ ├─ src/ └─ dist/
TypeScript 檔案會從 src
資料夾開始,執行 TypeScript 編譯器,最後會在 dist
中結束。
讓我們建立腳手架
shell
mkdir srcmkdir dist
初始化專案
現在我們會將這個資料夾轉換成 npm 套件。
shell
npm init
你會收到一系列提示。你可以使用預設值,但入口點除外。對於你的入口點,請使用 ./dist/main.js
。你隨時可以返回並在為你產生的 package.json
檔案中變更這些內容。
安裝我們的相依性
現在我們可以使用 npm install
來安裝套件。首先在全球安裝 gulp-cli
(如果你使用 Unix 系統,你可能需要在這個指南中的 npm install
指令前面加上 sudo
)。
shell
npm install -g gulp-cli
然後在專案的開發相依性中安裝 typescript
、gulp
和 gulp-typescript
。 Gulp-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
gulpnode 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
gulpnode 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
,以使用 tsify 外掛程式呼叫 Browserify,而非 gulp-typescript。很方便的是,它們都允許我們將相同的選項物件傳遞給 TypeScript 編譯器。
呼叫 bundle
之後,我們使用 source
(我們的 vinyl-source-stream 別名)將我們的輸出套件命名為 bundle.js
。
透過執行 gulp,然後在瀏覽器中開啟 dist/index.html
來測試頁面。你應該會在頁面上看到「Hello from TypeScript」。
請注意,我們已將 debug: true
指定給 Browserify。這會導致 tsify 在捆綁的 JavaScript 檔案中發出原始碼對應表。原始碼對應表可讓你直接在瀏覽器中除錯原始 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);
這裡基本上有三個變更,但它們需要您重新整理您的程式碼。
- 我們將我們的
browserify
執行個體包裝在對watchify
的呼叫中,然後保留結果。 - 我們呼叫
watchedBrowserify.on('update', bundle);
,以便 Browserify 在您的 TypeScript 檔案之一變更時執行bundle
函式。 - 我們呼叫
watchedBrowserify.on('log', fancy_log);
以記錄至主控台。
(1)和(2)一起表示我們必須將對 browserify
的呼叫移出 default
工作。而且我們必須為 default
函式提供一個名稱,因為 Watchify 和 Gulp 都需要呼叫它。使用(3)新增記錄是選用的,但對於除錯您的設定非常有用。
現在當您執行 Gulp 時,它應該會開始執行並持續執行。請嘗試變更 main.ts
中 showHello
的程式碼並儲存它。您應該會看到類似這樣的輸出
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
本身只有一個呼叫,而呼叫 buffer
和 sourcemaps
是為了確保原始碼對應持續運作。這些呼叫會提供一個單獨的原始碼對應檔案,而不是像以前一樣使用內嵌原始碼對應。現在您可以執行 Gulp 並檢查 bundle.js
是否已縮小成無法辨識的混亂狀態
shell
gulpcat dist/bundle.js
Babel
首先安裝 Babelify 和 Babel 預設值 ES2015。與 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 的輸出非常類似,適用於如此簡單的腳本。