Gulp

    这里假设你已经在使用Node.js和了。

    我们首先创建一个新目录。 命名为proj,也可以使用任何你喜欢的名字。

    我们将以下面的结构开始我们的工程:

    1. proj/
    2. ├─ src/
    3. └─ dist/

    TypeScript文件放在src文件夹下,经过TypeScript编译器编译生成的目标文件放在dist目录下。

    下面让我们来创建这些文件夹:

    1. mkdir src
    2. mkdir dist

    现在让我们把这个文件夹转换成npm包:

    1. npm init

    你将看到有一些提示操作。 除了入口文件外,其余的都可以使用默认项。 入口文件使用./dist/main.js。 你可以随时在package.json文件里更改生成的配置。

    安装依赖项

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

    1. npm install -g gulp-cli

    然后安装typescriptgulpgulp-typescript到开发依赖项。 是TypeScript的一个Gulp插件。

    1. npm install --save-dev typescript gulp@4.0.0 gulp-typescript

    写一个简单的例子

    让我们写一个Hello World程序。 在src目录下创建main.ts文件:

    1. function hello(compiler: string) {
    2. console.log(`Hello from ${compiler}`);
    3. }
    4. hello('TypeScript');

    在工程的根目录proj下新建一个tsconfig.json文件:

    1. {
    2. "files": [
    3. "src/main.ts"
    4. ],
    5. "compilerOptions": {
    6. "noImplicitAny": true,
    7. "target": "es5"
    8. }
    9. }

    在工程根目录下,新建一个gulpfile.js文件:

    1. var gulp = require('gulp');
    2. var ts = require('gulp-typescript');
    3. var tsProject = ts.createProject('tsconfig.json');
    4. gulp.task('default', function () {
    5. return tsProject.src()
    6. .pipe(tsProject())
    7. .js.pipe(gulp.dest('dist'));
    8. });

    测试这个应用

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

    新建一个src/greet.ts文件:

    1. export function sayHello(name: string) {
    2. return `Hello from ${name}`;
    3. }

    更改src/main.ts代码,从greet.ts导入sayHello

    1. import { sayHello } from './greet';
    2. console.log(sayHello('TypeScript'));

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

    1. {
    2. "files": [
    3. "src/main.ts",
    4. "src/greet.ts"
    5. ],
    6. "compilerOptions": {
    7. "noImplicitAny": true,
    8. "target": "es5"
    9. }
    10. }

    确保执行gulp后模块是能工作的,在Node.js下进行测试:

    1. gulp
    2. node dist/main.js

    注意,即使我们使用了ES2015的模块语法,TypeScript还是会生成Node.js使用的CommonJS模块。 我们在这个教程里会一直使用CommonJS模块,但是你可以通过修改module选项来改变这个行为。

    现在,让我们把这个工程由Node.js环境移到浏览器环境里。 因此,我们将把所有模块捆绑成一个JavaScript文件。 所幸,这正是Browserify要做的事情。 更方便的是,它支持Node.js的CommonJS模块,这也正是TypeScript默认生成的类型。 也就是说TypeScript和Node.js的设置不需要改变就可以移植到浏览器里。

    首先,安装Browserify,tsify和vinyl-source-stream。 tsify是Browserify的一个插件,就像gulp-typescript一样,它能够访问TypeScript编译器。 vinyl-source-stream会将Browserify的输出文件适配成gulp能够解析的格式,它叫做。

    1. npm install --save-dev browserify tsify vinyl-source-stream

    新建一个页面

    src目录下新建一个index.html文件:

    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="UTF-8" />
    5. <title>Hello World!</title>
    6. </head>
    7. <body>
    8. <p id="greeting">Loading ...</p>
    9. <script src="bundle.js"></script>
    10. </body>
    11. </html>

    修改main.ts文件来更新这个页面:

    1. import { sayHello } from './greet';
    2. function showHello(divName: string, name: string) {
    3. const elt = document.getElementById(divName);
    4. elt.innerText = sayHello(name);
    5. showHello('greeting', 'TypeScript');

    showHello调用sayHello函数更改页面上段落的文字。 现在修改gulpfile文件如下:

    1. var gulp = require('gulp');
    2. var browserify = require('browserify');
    3. var source = require('vinyl-source-stream');
    4. var tsify = require('tsify');
    5. var paths = {
    6. pages: ['src/*.html']
    7. };
    8. gulp.task('copy-html', function () {
    9. return gulp.src(paths.pages)
    10. .pipe(gulp.dest('dist'));
    11. });
    12. gulp.task('default', gulp.series(gulp.parallel('copy-html'), function () {
    13. return browserify({
    14. basedir: '.',
    15. debug: true,
    16. entries: ['src/main.ts'],
    17. cache: {},
    18. packageCache: {}
    19. })
    20. .plugin(tsify)
    21. .bundle()
    22. .pipe(source('bundle.js'))
    23. .pipe(gulp.dest('dist'));
    24. }));

    这里增加了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”。

    现在代码已经用Browserify和tsify捆绑在一起了,我们可以使用Browserify插件为构建添加一些特性。

    • Watchify启动Gulp并保持运行状态,当你保存文件时自动编译。 帮你进入到编辑-保存-刷新浏览器的循环中。
    • Babel是个十分灵活的编译器,将ES2015及以上版本的代码转换成ES5和ES3。 你可以添加大量自定义的TypeScript目前不支持的转换器。
    • Uglify帮你压缩代码,将花费更少的时间去下载它们。

    我们启动Watchify,让它在后台帮我们编译:

    修改gulpfile文件如下:

    1. var gulp = require('gulp');
    2. var browserify = require('browserify');
    3. var source = require('vinyl-source-stream');
    4. var watchify = require('watchify');
    5. var tsify = require('tsify');
    6. var fancy_log = require('fancy-log');
    7. var paths = {
    8. pages: ['src/*.html']
    9. };
    10. var watchedBrowserify = watchify(browserify({
    11. basedir: '.',
    12. debug: true,
    13. entries: ['src/main.ts'],
    14. cache: {},
    15. packageCache: {}
    16. }).plugin(tsify));
    17. gulp.task('copy-html', function () {
    18. return gulp.src(paths.pages)
    19. .pipe(gulp.dest('dist'));
    20. });
    21. function bundle() {
    22. return watchedBrowserify
    23. .bundle()
    24. .on('error', fancy_log)
    25. .pipe(source('bundle.js'))
    26. .pipe(gulp.dest('dist'));
    27. }
    28. gulp.task('default', gulp.series(gulp.parallel('copy-html'), bundle));
    29. watchedBrowserify.on('update', bundle);
    30. 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任务。 然后给函数起个名字,因为Watchify和Gulp都要调用它。 (3)是可选的,但是对于调试来讲很有用。

    现在当你执行gulp,它会启动并保持运行状态。 试着改变main.ts文件里showHello的代码并保存。 你会看到这样的输出:

    1. proj$ gulp
    2. [10:34:20] Using gulpfile ~/src/proj/gulpfile.js
    3. [10:34:20] Starting 'copy-html'...
    4. [10:34:20] Starting 'default'...
    5. [10:34:21] 2824 bytes written (0.13 seconds)
    6. [10:34:21] Finished 'default' after 1.36 s
    7. [10:35:22] 2261 bytes written (0.02 seconds)
    8. [10:35:24] 2808 bytes written (0.05 seconds)

    Uglify

    首先安装Uglify。 因为Uglify是用于混淆你的代码,所以我们还要安装vinyl-buffer和gulp-sourcemaps来支持sourcemaps。

    1. npm install --save-dev gulp-uglify vinyl-buffer gulp-sourcemaps

    修改gulpfile文件如下:

    1. var browserify = require('browserify');
    2. var source = require('vinyl-source-stream');
    3. var tsify = require('tsify');
    4. var uglify = require('gulp-uglify');
    5. var sourcemaps = require('gulp-sourcemaps');
    6. var buffer = require('vinyl-buffer');
    7. var paths = {
    8. pages: ['src/*.html']
    9. };
    10. gulp.task('copy-html', function () {
    11. return gulp.src(paths.pages)
    12. .pipe(gulp.dest('dist'));
    13. });
    14. gulp.task('default', gulp.series(gulp.parallel('copy-html'), function () {
    15. return browserify({
    16. basedir: '.',
    17. debug: true,
    18. entries: ['src/main.ts'],
    19. cache: {},
    20. packageCache: {}
    21. })
    22. .plugin(tsify)
    23. .bundle()
    24. .pipe(source('bundle.js'))
    25. .pipe(buffer())
    26. .pipe(sourcemaps.init({loadMaps: true}))
    27. .pipe(uglify())
    28. .pipe(sourcemaps.write('./'))
    29. .pipe(gulp.dest('dist'));
    30. }));

    注意uglify只是调用了自己—buffersourcemaps的调用是用于确保sourcemaps可以工作。 这些调用让我们可以使用单独的sourcemap文件,而不是之前的内嵌的sourcemaps。 你现在可以执行gulp来检查bundle.js是否被压缩了:

    1. gulp
    2. cat dist/bundle.js

    Babel

    首先安装Babelify和ES2015的Babel预置程序。 和Uglify一样,Babelify也会混淆代码,因此我们也需要vinyl-buffer和gulp-sourcemaps。 默认情况下Babelify只会处理扩展名为.js.es.es6.jsx的文件,因此我们需要添加.ts扩展名到Babelify选项。

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

    修改gulpfile文件如下:

    1. var gulp = require('gulp');
    2. var browserify = require('browserify');
    3. var source = require('vinyl-source-stream');
    4. var tsify = require('tsify');
    5. var sourcemaps = require('gulp-sourcemaps');
    6. var buffer = require('vinyl-buffer');
    7. var paths = {
    8. pages: ['src/*.html']
    9. };
    10. gulp.task('copyHtml', function () {
    11. return gulp.src(paths.pages)
    12. .pipe(gulp.dest('dist'));
    13. });
    14. gulp.task('default', gulp.series(gulp.parallel('copy-html'), function () {
    15. return browserify({
    16. basedir: '.',
    17. debug: true,
    18. entries: ['src/main.ts'],
    19. cache: {},
    20. packageCache: {}
    21. })
    22. .plugin(tsify)
    23. .transform('babelify', {
    24. presets: ['es2015'],
    25. extensions: ['.ts']
    26. })
    27. .bundle()
    28. .pipe(source('bundle.js'))
    29. .pipe(buffer())
    30. .pipe(sourcemaps.init({loadMaps: true}))
    31. .pipe(sourcemaps.write('./'))
    32. .pipe(gulp.dest('dist'));
    33. }));

    我们需要设置TypeScript目标为ES2015。 Babel稍后会从TypeScript生成的ES2015代码中生成ES5。 修改tsconfig.json:

    1. {
    2. "files": [
    3. "src/main.ts"
    4. ],
    5. "compilerOptions": {
    6. "noImplicitAny": true,
    7. "target": "es2015"

    对于这样一段简单的代码来说,Babel的ES5输出应该和TypeScript的输出相似。