使用Browserify + Watchify + Tsify + Gulp存在性能问题

9

我有一个中等规模的Typescript应用程序(不是微不足道的,也不是企业级的,有成千上万行代码),依赖于jQuery、React和SocketIO等许多小型库。

我的当前gulpfile如下:

var gulp = require("gulp"),
    $ = require("gulp-load-plugins")(),
    _ = require("lodash"),
    tsify = require("tsify"),
    browserify = require("browserify"),
    source = require("vinyl-source-stream"),
    debowerify = require("debowerify"),
    watchify = require("watchify"),
    lr = require("tiny-lr"),
    buffer = require("vinyl-buffer");

var lrServer = lr();

var config = {
    scripts: {
        base: __dirname + "/Resources/Scripts",
        main: "Application.ts",
        output: "App.js"
    },

    styles: {
        base: __dirname + "/Resources/Styles",
        sheets: ["Application.less", "Preload.less"],
        autoprefixer: ["last 2 version", "safari 5", "ie 8", "ie 9", "opera 12.1", "ios 6", "android 4"]
    },

    publicPath: __dirname + "/wwwroot"
};

function printError(err) {
    $.util.log($.util.colors.red.bold(err.type + " " + err.name + ":"), $.util.colors.white(err.message));
    this.emit("end");
}

function buildScripts(watch, debug) {
    var bundler = browserify({
            basedir: config.scripts.base,
            debug: false,
            entries: [config.scripts.base + "/" + config.scripts.main],
            cache: {},
            packageCache: {}
        })
        .plugin(tsify, {
            module: "commonjs",
            target: "es5",
            jsx: "react"
        })
        .transform(debowerify);

    function build() {
        return bundler.bundle()
            .on("error", printError)
            .pipe(source(config.scripts.output))
            .pipe($.if(!debug, buffer()))
            .pipe($.if(!debug, $.uglify()))
            .pipe(gulp.dest(config.publicPath + "/" + "scripts"));
    }

    if (!watch)
        return build();

    bundler
        .plugin(watchify)
        .on("update", function () {
            $.util.log($.util.colors.grey("Building scripts..."));
            build();
        })
        .on("time", function (timeMs) {
            $.util.log(
                $.util.colors.grey("Finished"),
                $.util.colors.cyan("'dev.scripts.watch' after"),
                $.util.colors.magenta(timeMs.toLocaleString() + " ms"));
        });

    return build();
}

gulp.task("prod.scripts", function() {
    return buildScripts(false, false);
});

gulp.task("dev.scripts", function () {
    return buildScripts(false, true);
});

gulp.task("dev.scripts.watch", function () {
    return buildScripts(true, true);
});

gulp.task("prod.styles", function () {
    return gulp
        .src(_.map(config.styles.sheets, function (sheet) { return config.styles.base + "/" + sheet; }))
        .pipe($.less())
        .on("error", printError)
        .pipe($.autoprefixer(config.styles.autoprefixer))
        .pipe($.uglifycss())
        .pipe(gulp.dest(config.publicPath + "/styles/"));
});

gulp.task("dev.styles", function () {
    return gulp
        .src(_.map(config.styles.sheets, function (sheet) { return config.styles.base + "/" + sheet; }))
        .pipe($.sourcemaps.init())
        .pipe($.less())
        .on("error", printError)
        .pipe($.autoprefixer(config.styles.autoprefixer))
        .pipe($.sourcemaps.write())
        .pipe(gulp.dest(config.publicPath + "/styles/"));
});

gulp.task("dev.styles.watch", ["dev.styles"], function () {
    return gulp.watch(config.styles.base + "/**/*.{css,less}", ["dev.styles"]);
});

gulp.task("dev.watch", ["dev.scripts.watch", "dev.styles.watch"], function () {
    lrServer.listen(35729);

    gulp.watch(config.publicPath + "/styles/**").on("change", function(file) {
        lrServer.changed({ body: { files: [file.path] } });
    });
});

gulp.task("dev", ["dev.styles", "dev.scripts"]);
gulp.task("prod", ["prod.styles", "prod.scripts"]);

一切都符合预期,但使用观察任务时的构建时间需要很多秒。奇怪的是,我的任务报告说脚本重新编译发生在500ms以下(“time”事件上的事件处理程序),但如果我用头脑计算,它要三到四秒才能完成。
请注意,在我粘贴现有的TypeScript代码之前,我非常快地加载/捆绑了jQuery,React,Moment和其他库。因此,我不认为使用单独的供应商包会加速任何内容。此外,不写出sourcemaps似乎也不会影响性能。
在切换到browserify之前,我使用gulp-typescript进行编译并使用requirejs进行模块加载。这些构建只需要不到一秒钟。但是,requirejs由于其他原因而引起问题-无论如何,我都想从AMD移动到CommonJS。
目前这不是一个很大的问题,但随着项目的增长,它肯定会对我的开发流程造成问题。对于一个仅这么大的项目,处理任何更大的东西需要多长时间?
此外,它还会导致Visual Studio出现问题。这是一个ASP.NET 5应用程序,Visual Studio显然坚持每次更改后重新加载/重新解析捆绑的JavaScript文件,导致IDE在每次更改后延迟1-2秒:加上重新编译本身需要3-4秒。脚本被呈现到我的wwwroot文件夹中,似乎没有办法使用ASP.NET 5工具排除脚本子文件夹。
我知道我在某个地方漏掉了什么。可能的问题是tsify没有使用typescript的“project”功能来实现重新加载,从而导致TypeScript编译器为每个更改重新处理每个文件。
总之,我不可能是唯一一个在玩具项目之外使用这些工具的人,因此我在这里询问是否有更好的解决方案;因为除了这个问题之外,一切都很好。
编辑--------------------------------
好吧,我要自食其言。现在,当我将第三方库捆绑到它们自己的包中时,构建时间已经缩短到大约一秒钟。这是我的更新gulpfile(请注意新的dev.scripts.vendor任务和buildScripts函数中的.external调用)
var gulp = require("gulp"),
    $ = require("gulp-load-plugins")(),
    _ = require("lodash"),
    tsify = require("tsify"),
    browserify = require("browserify"),
    source = require("vinyl-source-stream"),
    debowerify = require("debowerify"),
    watchify = require("watchify"),
    lr = require("tiny-lr"),
    buffer = require("vinyl-buffer");

var lrServer = lr();

var config = {
    scripts: {
        base: __dirname + "/Resources/Scripts",
        main: "Application.ts",
        output: "App.js",
        vendor: ["react", "jquery", "moment", "socket.io-client", "lodash", "react-dom"]
    },

    styles: {
        base: __dirname + "/Resources/Styles",
        sheets: ["Application.less", "Preload.less"],
        autoprefixer: ["last 2 version", "safari 5", "ie 8", "ie 9", "opera 12.1", "ios 6", "android 4"]
    },

    publicPath: __dirname + "/wwwroot"
};

function printError(err) {
    $.util.log($.util.colors.red.bold(err.type + " " + err.name + ":"), $.util.colors.white(err.message));
    this.emit("end");
}

function buildScripts(watch, debug) {
    var bundler = browserify({
            basedir: config.scripts.base,
            debug: false,
            entries: [config.scripts.base + "/" + config.scripts.main],
            cache: {},
            packageCache: {}
        })
        .plugin(tsify, {
            module: "commonjs",
            target: "es5",
            jsx: "react"
        });

    if (debug)
        bundler.external(config.scripts.vendor);

    function build() {
        return bundler.bundle()
            .on("error", printError)
            .pipe(source(config.scripts.output))
            .pipe($.if(!debug, buffer()))
            .pipe($.if(!debug, $.uglify()))
            .pipe(gulp.dest(config.publicPath + "/" + "scripts"));
    }

    if (!watch)
        return build();

    bundler
        .plugin(watchify)
        .on("update", function () {
            $.util.log($.util.colors.grey("Building scripts..."));
            build();
        })
        .on("time", function (timeMs) {
            $.util.log(
                $.util.colors.grey("Finished"),
                $.util.colors.cyan("'dev.scripts.watch' after"),
                $.util.colors.magenta(timeMs.toLocaleString() + " ms"));
        });

    return build();
}

gulp.task("prod.scripts", function() {
    return buildScripts(false, false);
});

gulp.task("dev.scripts", ["dev.scripts.vendor"], function () {
    return buildScripts(false, true);
});

gulp.task("dev.scripts.vendor", function() {
    return browserify({
            debug: true,
            cache: {},
            packageCache: {},
            require: config.scripts.vendor
        })
        .bundle()
        .on("error", printError)
        .pipe(source("Vendor.js"))
        .pipe(gulp.dest(config.publicPath + "/" + "scripts"));
});

gulp.task("dev.scripts.watch", ["dev.scripts.vendor"], function () {
    return buildScripts(true, true);
});

gulp.task("prod.styles", function () {
    return gulp
        .src(_.map(config.styles.sheets, function (sheet) { return config.styles.base + "/" + sheet; }))
        .pipe($.less())
        .on("error", printError)
        .pipe($.autoprefixer(config.styles.autoprefixer))
        .pipe($.uglifycss())
        .pipe(gulp.dest(config.publicPath + "/styles/"));
});

gulp.task("dev.styles", function () {
    return gulp
        .src(_.map(config.styles.sheets, function (sheet) { return config.styles.base + "/" + sheet; }))
        .pipe($.sourcemaps.init())
        .pipe($.less())
        .on("error", printError)
        .pipe($.autoprefixer(config.styles.autoprefixer))
        .pipe($.sourcemaps.write())
        .pipe(gulp.dest(config.publicPath + "/styles/"));
});

gulp.task("dev.styles.watch", ["dev.styles"], function () {
    return gulp.watch(config.styles.base + "/**/*.{css,less}", ["dev.styles"]);
});

gulp.task("dev.watch", ["dev.scripts.watch", "dev.styles.watch"], function () {
    lrServer.listen(35729);

    gulp.watch(config.publicPath + "/styles/**").on("change", function(file) {
        lrServer.changed({ body: { files: [file.path] } });
    });
});

gulp.task("dev", ["dev.styles", "dev.scripts"]);
gulp.task("prod", ["prod.styles", "prod.scripts"]);

然而,我仍然遇到了一个奇怪的问题。当禁用sourcemaps(现在似乎会影响速度)时,我的on("time", () => {})回调为每个文件更改报告60-80毫秒,但它仍然会卡顿大约一秒钟。一秒钟是我愿意等待的时间,所以我担心随着项目的增长,这个等待时间也可能增加。
有趣的是,当事件报告较小的内容时,这额外的一秒钟时间被花费在什么地方。也许我会开始深入挖掘源代码,因为似乎没有人能够立即回答这个问题。
另外,还有一个问题,这只是一个旁注,但debowerify不再与此兼容。使用debowerify+bower时,它将继续在最终输出中呈现所需的模块,即使该模块在“external”列表中列出。因此,目前使用这种设置,我只能使用npm模块,除非我可以接受将更多编译时间添加到我的应用程序包中。
另外,我了解到debowerify将覆盖npm模块,并且它基于bower_components的目录列表,而不是您的bower配置文件。我已经在npm中安装了jQuery,只有在bower中安装了bootstrap;但由于bootstrap作为依赖项拉取了jQuery,因此bower jQuery模块被优先加载而不是NPM jQuery。提醒大家注意一下。

1
只是一个想法:这本质上是一个分析任务。添加 var debug = require('gulp-debug');.pipe(debug({title: 'your message:'})) 来找出哪些部分很慢怎么样?我很惊讶的是,源映射不会很慢,在我的电脑上它会大大减缓构建过程。我们使用 gulp-typescript,初始编译需要长达10秒的时间,但增量编译只需要1.5秒左右,非常舒适。 - MartyIX
在我们的大型项目中,我们使用ASP.NET 4,而Typescript是由Visual Studio 2015编译的。它编译需要1秒钟左右,完全没有问题。但是当我尝试使用ASP.NET 5时,如果使用Typescript,似乎仍然非常不稳定。 - Andzej Maciusovic
@MartinVseticka:bundler.bundle() 是启动流的方法,所以我不能在它之前放置任何东西。然而,似乎 bundler.bundle() 操作是导致额外时间消耗的原因。此外,gulp-typescript确实很快,但由于我正在使用browserify进行模块加载,所以我不能使用它。你们用什么进行模块加载? - user19302
@AndzejMaciusovic:你用什么进行模块加载?此外,我不想完全依赖编辑器,因为我希望这个项目可以跨平台。因此,使用监视任务基本上是我前进的唯一方式。 - user19302
看起来你不是唯一遇到这个问题的人:https://github.com/TypeStrong/tsify/issues/54 - gustavohenke
多么令人沮丧啊,几个月后回来看这个问题(还有另一个问题),发现还没有修复 :( - gustavohenke
1个回答

2
忘记这个吧,只需使用最新的TS + webpack :)

我同意,我已经完全停止使用Browserify,转而使用Webpack。 - user19302

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接