与Gulp一起使用Concat脚本


191

举例来说,假设您要在Backbone或任何其他平台上构建项目,并且需要按特定顺序加载脚本,例如,underscore.js需要在之前加载backbone.js

我如何获得它来连接脚本,以便它们井井有条?

// JS concat, strip debugging and minify
gulp.task('scripts', function() {
    gulp.src(['./source/js/*.js', './source/js/**/*.js'])
    .pipe(concat('script.js'))
    .pipe(stripDebug())
    .pipe(uglify())
    .pipe(gulp.dest('./build/js/'));
});

我的脚本顺序正确source/index.html,但是由于文件是按字母顺序组织的,因此gulp将underscore.js在之后连接backbone.js,而我的脚本顺序source/index.html并不重要,它会查看目录中的文件。

那么有人对此有想法吗?

我有最好的办法是重新命名与供应商的脚本123给他们以正确的顺序,但我不知道如果我喜欢这个。

当我了解到更多信息后,我发现Browserify是一个很好的解决方案,乍一看可能很麻烦,但是它很棒。


4
我可能会提到,如今我正在使用browserify。它有自己的学习曲线IMO。一开始我很挣扎,但是gulp browserify是一个不错的选择!允许您的代码模块化!您可以使用填充程序来处理订单,因此在使用browserify时不必进行串联。
Michael Joseph Aubry'4

愿意为您的解决方案或链接提供更多详细信息吗?
Dmitri Zaitsev 2014年

kroltech.com/2013/12/…这里是样板项目的链接,该链接确实帮助我开始了良好的项目管理。在学习了所有这些之后,我可以更好地管理我的项目。他在github上有项目,您可以看到他如何使用browserify。Youtube总是有帮助,当然,源本身总是被低估了github.com/substack/node-browserify#usage
Michael Joseph Aubry

基本上,这个想法是能够require在前端使用类似npm的语法,因为如果您在服务器端使用了npm,那么您将看到如何需要模块,但是browserify允许您在客户端代码上执行此操作,请保留请记住,要开始使用它需要一些修补,但是它主要在package.json中,并且如果要与gulp.js或grunt.js一起使用。如果您安装了gulp / grunt browserify软件包,则可以运行gulp/grunt browserify并将脚本转换为一个主脚本,这虽然需要一点学习,但值得IMO借鉴。
迈克尔·约瑟夫·奥布里2014年

谢谢!其实我碰到大文章来medium.com/@dickeyxxx/...做一个好点,你并不真正需要做browserifyAngular模块,其中简单拼接工程和顺序无关紧要:)
德米特里·扎伊采夫

Answers:


198

最近,在构建AngularJS应用时,Grunt也遇到了类似的问题。这是我发布的问题

我最终要做的是在grunt配置中按顺序显式列出文件。配置文件将如下所示:

[
  '/path/to/app.js',
  '/path/to/mymodule/mymodule.js',
  '/path/to/mymodule/mymodule/*.js'
]

Grunt能够找出哪些文件是重复的,而不包括它们。同样的技术也适用于Gulp。


74
顺便说一句,这在gulp下也能正常工作。Gulp也不会重复文件。
OverZealous 2014年

1
帅哥,这两个杰作都很棒。最后,我终于设置了gulp.js文件,按我的意愿工作,用一些html编写了文件,保存了文件,并且只需按一下按钮,即可兴建一个具有最佳框架和良好实践的网站。如果您不使用任何一个,那么Plus更新将很容易!
迈克尔·约瑟夫·奥布里2014年

1
是! 我最近开始使用Grunt,它很棒。不再将JavaScript应用程序嵌入后端框架中。
乍得·约翰逊

3
Gulp尝试复制文件,但我意识到gulp与文件系统的情况有所不同,因此请注意!在确切的情况下,gulp不会复制文件。
克里斯

2
手动订购是严重项目中的噩梦。有特殊的文件分类器-用于angularjs和其他。
zhekaus

135

帮助,如果你需要一些文件来另一件事文件的斑点,是从你的水珠排除特定的文件,就像这样:

[
  '/src/**/!(foobar)*.js', // all files that end in .js EXCEPT foobar*.js
  '/src/js/foobar.js',
]

您可以将其与指定需要首先出现的文件结合起来,如查德·约翰逊的答案中所述。


嗯,我实际上早些时候看过您的帖子,它帮助我向自己的代码注入代码,src而且build我看到您使用该语法,最后我删除了该部分,因为我不确定确切为什么需要它,现在有意义。
迈克尔·约瑟夫·奥布里2014年

哦,好吧,您的意思就是我,那会使foobar.js持续下去吗?不应该是另一回事。['./source/js/*.js', './source/js/**/underscore.js', './source/js/**/!(underscore)*.js']
迈克尔·约瑟夫·奥布里2014年

是的,这只是额外的帮助。当您需要或希望在加载所有其他内容之后将核心应用程序代码引入时,它非常有用。!(foobar)如果您事先包含了特定文件,则没有理由使用它。
OverZealous 2014年

对于我的模块定义位于名称中不带破折号的文件中的AngularJS应用程序而言,这种Gulp glob模式有效;['src/app/**/!(*-)*.js', 'src/app/**/*.js']
山姆T

17

我已经使用了gulp-order插件,但是它并不总是成功的,因为您可以通过我的堆栈溢出后的gulp-order节点模块的合并流看到。在浏览Gulp文档时,我遇到了流线型模块,该模块在指定我的情况下并置的顺序非常有效。https://github.com/gulpjs/gulp/blob/master/docs/recipes/using-multiple-sources-in-one-task.md

下面是我如何使用它的示例

var gulp         = require('gulp');
var concat       = require('gulp-concat');
var handleErrors = require('../util/handleErrors');
var streamqueue  = require('streamqueue');

gulp.task('scripts', function() {
    return streamqueue({ objectMode: true },
        gulp.src('./public/angular/config/*.js'),
        gulp.src('./public/angular/services/**/*.js'),
        gulp.src('./public/angular/modules/**/*.js'),
        gulp.src('./public/angular/primitives/**/*.js'),
        gulp.src('./public/js/**/*.js')
    )
        .pipe(concat('app.js'))
        .pipe(gulp.dest('./public/build/js'))
        .on('error', handleErrors);
});

另请参见stream-series。它不需要您指定对象模式,并且可以使用gulp-inject。看我的答案。
编码

似乎一半的gulp插件根本无法正常工作(就像您指出的顺序一样),这真是令人遗憾,因为gulp的体系结构概念非常壮观,我认为很多人实施和维护他们的插件都很差...我发现基础节点模块更有用,因此谢谢您提供此解决方案!很棒!
Jimmy Hoffa 2015年

1
streamqueue,事件流对我不起作用,但是merge2按预期工作npmjs.com/package/merge2
Alexander Shutau

12

使用gulp-useref,可以按声明顺序将索引文件中声明的每个脚本连接起来。

https://www.npmjs.com/package/gulp-useref

var $ = require('gulp-load-plugins')();
gulp.task('jsbuild', function () {
  var assets = $.useref.assets({searchPath: '{.tmp,app}'});
  return gulp.src('app/**/*.html')
    .pipe(assets)
    .pipe($.if('*.js', $.uglify({preserveComments: 'some'})))
    .pipe(gulp.dest('dist'))
    .pipe($.size({title: 'html'}));
});

在HTML中,必须声明要生成的构建文件的名称,如下所示:

<!-- build:js js/main.min.js -->
    <script src="js/vendor/vendor.js"></script>
    <script src="js/modules/test.js"></script>
    <script src="js/main.js"></script>

在构建目录中,您将引用main.min.js,其中将包含vendor.js,test.js和main.js


2
太棒了!我讨厌需要定义顺序的答案!你知道吗?订单在那里:在HTML文件中。完美的解决方案。
阿里·

6

类型的流也可用于确保文件的具体顺序用gulp.src。将backbone.js始终作为最后一个文件处理的示例代码:

var gulp = require('gulp');
var sort = require('sort-stream');
gulp.task('scripts', function() {
gulp.src(['./source/js/*.js', './source/js/**/*.js'])
  .pipe(sort(function(a, b){
    aScore = a.path.match(/backbone.js$/) ? 1 : 0;
    bScore = b.path.match(/backbone.js$/) ? 1 : 0;
    return aScore - bScore;
  }))
  .pipe(concat('script.js'))
  .pipe(stripDebug())
  .pipe(uglify())
  .pipe(gulp.dest('./build/js/'));
});

我希望该模块能够正常工作,因为这对我来说似乎是最简单的答案,但是在我的情况下,如果我给文件名编号并具有一个非常简单的比较功能,则该模块将无法工作。
杰里米·约翰

5

我只是在文件名的开头添加数字:

0_normalize.scss
1_tikitaka.scss
main.scss

它完全没有任何问题。


1
是的,我发现这有点容易,我的意思是,如果您要编译所有文件以进行生产,那么在开发中命名文件的名称就没有关系。
迈克尔·约瑟夫·奥布里2014年

2
我只是发现这无法正常工作。尝试使用1_xx,2_xx,10_xx,11_xx。至少在Windows下,它将为1_xx,10_xx,11_xx,2_xx
dbinott 2015年

17
就我个人而言,我几乎完全不同意这样的说法,即您在开发中为文件命名是无关紧要的。所有开发都应首先以人为本,而不是计算机。更改文件以匹配构建工具会破坏构建工具的目的。更改构建以匹配您的项目,而不是相反。
乔恩·希伯

2
@JonHieb在某种程度上,给文件加上前缀还可以帮助下一个开发人员了解每个文件的依赖性,不是吗?如果我打开一个文件夹并看到1_foo.js,2_bar.js,3_baz.js,我将按此顺序打开这些文件,并在1_foo.js
sqram

我发现gulp.src运行异步,这意味着在dir中有更多文件要处理的情况下,此操作将不一致。
杰里米·约翰

5

对于我从bower提取的每个程序包,我的脚本都组织在不同的文件夹中,另外,我自己的脚本也适用于我的应用程序。由于要在某个位置列出这些脚本的顺序,为什么不只在gulp文件中列出它们呢?对于您项目中的新开发人员,很高兴在此列出了所有脚本端点。您可以使用gulp-add-src来做到这一点:

gulpfile.js

var gulp = require('gulp'),
    less = require('gulp-less'),
    minifyCSS = require('gulp-minify-css'),
    uglify = require('gulp-uglify'),
    concat = require('gulp-concat'),
    addsrc = require('gulp-add-src'),
    sourcemaps = require('gulp-sourcemaps');

// CSS & Less
gulp.task('css', function(){
    gulp.src('less/all.less')
        .pipe(sourcemaps.init())
        .pipe(less())
        .pipe(minifyCSS())
        .pipe(sourcemaps.write('source-maps'))
        .pipe(gulp.dest('public/css'));
});

// JS
gulp.task('js', function() {
    gulp.src('resources/assets/bower/jquery/dist/jquery.js')
    .pipe(addsrc.append('resources/assets/bower/bootstrap/dist/js/bootstrap.js'))
    .pipe(addsrc.append('resources/assets/bower/blahblah/dist/js/blah.js'))
    .pipe(addsrc.append('resources/assets/js/my-script.js'))
    .pipe(sourcemaps.init())
    .pipe(concat('all.js'))
    .pipe(uglify())
    .pipe(sourcemaps.write('source-maps'))
    .pipe(gulp.dest('public/js'));
});

gulp.task('default',['css','js']);

注意: jQuery和Bootstrap已添加,用于演示订单。对于CDN来说,使用CDN可能更好一些,因为它们的使用非常广泛,而浏览器可能已经从其他站点缓存了它们。



2

merge2看起来是目前唯一可用且可维护的有序流合并工具。

更新2020

API总是在变化,某些库变得不可用或包含漏洞,或者它们的依存关系包含漏洞,这些漏洞多年来没有固定。对于文本文件操作,最好使用自定义NodeJS脚本和流行的库,例如globbyfs-extra以及其他没有Gulp,Grunt等包装器的库。

import globby from 'globby';
import fs from 'fs-extra';

async function bundleScripts() {
    const rootPaths = await globby('./source/js/*.js');
    const otherPaths = (await globby('./source/**/*.js'))
        .filter(f => !rootFiles.includes(f));
    const paths = rootPaths.concat(otherPaths);

    const files = Promise.all(
        paths.map(
            // Returns a Promise
            path => fs.readFile(path, {encoding: 'utf8'})
        )
    );

    let bundle = files.join('\n');
    bundle = uglify(bundle);
    bundle = whatever(bundle);
    bundle = bundle.replace(/\/\*.*?\*\//g, '');

    await fs.outputFile('./build/js/script.js', bundle, {encoding: 'utf8'});
}

bundleScripts.then(() => console.log('done');

1

另一种方法是使用专门为此问题创建的Gulp插件。https://www.npmjs.com/package/gulp-ng-module-sort

它允许您通过添加.pipe(ngModuleSort())这样对脚本进行排序:

var ngModuleSort = require('gulp-ng-module-sort');
var concat = require('gulp-concat');

gulp.task('angular-scripts', function() {
    return gulp.src('./src/app/**/*.js')
        .pipe(ngModuleSort())
        .pipe(concat('angularAppScripts.js))
        .pipe(gulp.dest('./dist/));
});

假设目录约定为:

|——— src/
|   |——— app/
|       |——— module1/
|           |——— sub-module1/
|               |——— sub-module1.js
|           |——— module1.js
|       |——— module2/
|           |——— sub-module2/
|               |——— sub-module2.js
|           |——— sub-module3/
|               |——— sub-module3.js
|           |——— module2.js
|   |——— app.js

希望这可以帮助!


1

对我来说,管道中有natualSort()和angularFileSort(),它们正在重新排序文件。我删除了它,现在对我来说效果很好

$.inject( // app/**/*.js files
    gulp.src(paths.jsFiles)
      .pipe($.plumber()), // use plumber so watch can start despite js errors
      //.pipe($.naturalSort())
      //.pipe($.angularFilesort()),
    {relative: true}))

1

我只是用gulp-angular-filesort

function concatOrder() {

    return gulp.src('./build/src/app/**/*.js')
        .pipe(sort())
        .pipe(plug.concat('concat.js'))
        .pipe(gulp.dest('./output/'));
}

糟糕,我才意识到您不是在问棱角问题,对不起
Maccurt

0

我在一个模块环境中,所有环境都是使用gulp依赖于核心的。因此,core需要在其他模块之前附加模块。

我做了什么:

  1. 将所有脚本移动到src文件夹
  2. 只是gulp-rename你的core目录_core
  3. gulp保持您的订单gulp.src,我的concat src看起来像这样:

    concat: ['./client/src/js/*.js', './client/src/js/**/*.js', './client/src/js/**/**/*.js']

显然,它将_作为列表中的第一个目录(自然排序?)。

注意(angularjs):然后,我使用gulp -angular-extender将模块动态添加到core模块中。编译后看起来像这样:

angular.module('Core', ["ui.router","mm.foundation",(...),"Admin","Products"])

其中Admin和Products是两个模块。


0

如果您要订购第三方库依赖项,请尝试wiredep。该软件包基本上检查bower.json中的每个软件包依赖性,然后为您连接它们。


0

我从此页面尝试了几种解决方案,但没有一个起作用。我有一系列带编号的文件,我只是想按字母顺序的文件夹名称排序,因此在通过管道传输到concat()它们按相同的顺序进行排序。即,保留全局输入的顺序。容易吧?

这是我特定的概念验证代码(print只是为了查看打印到cli的顺序):

var order = require('gulp-order');
var gulp = require('gulp');
var print = require('gulp-print').default;

var options = {};

options.rootPath = {
  inputDir: process.env.INIT_CWD + '/Draft',
  inputGlob: '/**/*.md',
};

gulp.task('default', function(){
  gulp.src(options.rootPath.inputDir + options.rootPath.inputGlob, {base: '.'})
    .pipe(order([options.rootPath.inputDir + options.rootPath.inputGlob]))
    .pipe(print());
});

之所以疯狂gulp.src?我确定gulp.src当我能够使用sleep()函数时(使用a.map睡眠时间按索引递增的命令)来正确排序流输出。

src具有更多文件的平均目录异步结果是在具有较少文件的目录之后进行的,因为它们需要更长的处理时间。


1
您是否找到了同步(至少是确定性的)替代方案?
ELLIOTTCABLE

0

在我的gulp设置中,我先指定供应商文件,然后再指定(更一般的)所有内容。它将供应商js成功放置在其他自定义内容之前。

gulp.src([
  // vendor folder first
  path.join(folder, '/vendor/**/*.js'),
  // custom js after vendor
  path.join(folder, '/**/*.js')
])    

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.