2015-11-20 7 views
9

У меня есть «среднее» приложение для виджета (как и в, но не тривиальное, но не на уровне предприятия, либо много тысяч строк) с зависимостями от jQuery, React и SocketIO - среди другие более мелкие библиотеки.Проблемы с производительностью с Browserify + Watchify + Tsify + Gulp

Мой текущий 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"]); 

Все работает, как ожидалось, однако, время сборки при использовании задачи часы принимают много секунд. Странно, что в моей задаче сообщается, что повторная компиляция скриптов происходит менее чем в 500 мс (обработчик событий в событии «время»), но если я рассчитываю в своей голове, он не заканчивается до трех-четырех секунд после ,

Обратите внимание, что перед тем, как вставить существующий код типа TypeScript, я загружал/связывал jQuery, React, Moment и другие библиотеки, которые я использовал очень быстро. Из-за этого я не думаю, что использование отдельного пакета поставщиков ускорит что-либо. Кроме того, не выписывать исходные карты, похоже, не влияет на производительность.

Прежде чем переключиться на браузер, я использовал gulp-typescript для компиляции и requirejs для загрузки модуля. Эти сборки заняли второе место. Однако requirejs вызывало проблемы по другим причинам - и в любом случае, я хочу отойти от AMD к CommonJS.

На данный момент это не огромный концерн, но по мере роста проекта это может вызвать проблемы с моим потоком развития. С проектом только этот большой, сколько еще потребуется, чтобы обработать что-нибудь большее?

Кроме того, это также вызывает проблемы с Visual Studio. Это приложение ASP.NET 5, и Visual Studio, видимо, настаивает на при повторной загрузке/повторном анализе связанного файла JavaScript каждый раз, когда он изменяется, что приводит к задержке в IDE в течение 1-2 секунд после каждого изменения: сверху от 3-4 секунд до самой перекомпиляции. Сценарий отображается в моей папке wwwroot, и, похоже, нет возможности «исключить» подпапку скриптов с помощью инструментария ASP.NET 5.

Я знаю, что где-то я чего-то не хватает. Возможная проблема заключается в том, что tsify не использует функцию «project» для машиностроения для реализации перезагрузки, заставляя компилятор TypeScript перерабатывать каждый файл для каждого изменения.

В любом случае, я не могу быть единственным человеком, который использовал эти инструменты вне проектов игрушек, поэтому я спрашиваю здесь, есть ли у кого-то лучшее решение; поскольку, помимо этого, все работает очень хорошо.

EDIT --------------------------------

ОК, придётся съесть свои собственные слова , Сборка доходит примерно до второй секунды, когда я связываю свои сторонние библиотеки с их собственным пакетом. Вот мой обновленный gulpfile (обратите внимание на новую задачу dev.scripts.vendor и .external вызов функции buildScripts)

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"]); 

Однако, я все еще получаю нечетный вопрос. С отключенными исходными картами (которые, как представляется, оказывают влияние на скорость), мой ответ («время»,() => {}) сообщает 60-80 мс для каждого изменения файла, но он все еще висит около секунды , Второй - это все, что я готов дождаться этого, так что снова я волнуюсь, что по мере роста проекта это ожидание может расти.

Было бы интересно посмотреть, на что потрачено это дополнительное второе время, когда событие сообщает о чем-то гораздо меньшем.Возможно, я начну копаться в источнике немного, так как кажется, что никто не имеет ответа сразу.

ДРУГОЙ ВОПРОС Это всего лишь побочный сигнал, но debowerify больше не работает с этим. При использовании debowerify + bower он будет продолжать отображать требуемый модуль в конечном выпуске, даже если этот модуль указан во «внешнем» списке. Поэтому в настоящее время с этой настройкой я могу использовать только модули npm, если только я не в порядке с добавлением большего количества времени компиляции к моему набору приложений.

Кроме того, я узнал, что debowerify будет переопределять модули npm и что он основан на списке каталогов bower_components, а не на вашем файле конфигурации bower. У меня был jQuery, установленный в npm, и только бутстрап в bower; но поскольку bootstrap вытащил jQuery в качестве зависимости, модуль jower jQuery загружался преимущественно над jQuery NPM. Просто голова для людей.

+1

Просто мысли: Это, по существу, задача профилирования. Как насчет добавления 'var debug = require ('gulp-debug');' и '.pipe (debug ({title: 'ваше сообщение:'}))' чтобы узнать, какие части медленны? Я удивлен тем, что sourcemaps не медленный, он значительно замедляет процесс сборки на моем компьютере. Мы используем gulp-typescript, и исходная компиляция занимает до 10 секунд, но тогда инкрементная компиляция составляет всего 1,5 секунды, что вполне удобно. –

+0

В нашем большом проекте мы используем ASP.NET 4, а TypScript скомпилирован Visual Studio 2015. Он принимает до 1s для компиляции, и все в порядке. Поскольку я пытался играть с ASP.NET 5, он швы, как будто все еще очень глючит, если вы используете TypScript. –

+0

@MartinVseticka: bundler.bundle() - это то, что запускает поток, поэтому я ничего не могу поставить перед ним. Однако, похоже, что операция bundler.bundle() - это то, что вызывает дополнительное время. Кроме того, gulp-typescript * * быстро, но поскольку я использую браузер для загрузки модуля, я не могу его использовать. Что вы, ребята, используете для загрузки модуля? – nlaq

ответ

2

Забудьте это, просто использовать последние TS + WebPack :)

+0

Я согласен, я прекратил использование браузера полностью в пользу webpack. – nlaq