使用 Gulp 按顺序连接脚本
Posted
技术标签:
【中文标题】使用 Gulp 按顺序连接脚本【英文标题】:Concat scripts in order with Gulp 【发布时间】:2014-03-24 13:03:52 【问题描述】:例如,您正在 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 将在backbone.js
之后连接underscore.js
,而我的source/index.html
中的脚本顺序无关紧要,它会查看目录中的文件。
那么有人对此有什么想法吗?
我最好的办法是用 1
、2
、3
重命名供应商脚本,以给它们正确的顺序,但我不确定我是否喜欢这样。
随着我了解更多,我发现 Browserify 是一个很好的解决方案,一开始可能会很痛苦,但它很棒。
【问题讨论】:
我可能会提到现在我正在使用 browserify。它有自己的小学习曲线IMO。一开始我很挣扎,但 gulp browserify 是一个很酷的方法!让你的代码模块化!您在 shim 中处理订单,因此在使用 browserify 时不需要连接。 想为您的解决方案或链接提供更多详细信息? kroltech.com/2013/12/… 这是一个样板项目的链接,它真正帮助我开始进行良好的项目管理。在学习了所有这些之后,我可以更好地管理我的项目。他在 github 上有这个项目,你可以看到他是如何使用 browserify 的。 Youtube 总是有帮助,当然来源本身总是被低估github.com/substack/node-browserify#usage 基本上,这个想法是能够在前端使用带有require
的npm 语法,因为当然,如果您在服务器端使用过npm,您会看到如何需要模块,但browserify 允许您要在客户端代码上执行此操作,请记住要开始它需要一些修补,但它主要位于 package.json 中,如果您想与 gulp.js 或 grunt.js 一起使用。如果你安装 gulp/grunt browserify 包,你可以运行 gulp/grunt browserify
并将你的脚本变成一个主脚本,这是一个轻微的学习曲线,但值得 IMO。
谢谢!实际上,我遇到了很棒的文章 medium.com/@dickeyxxx/… 提出了一个很好的观点,即对于 Angular
模块,您并不真正需要 browserify
,其中简单的连接有效且顺序无关紧要:)
【参考方案1】:
我最近在构建 AngularJS 应用程序时遇到了与 Grunt 类似的问题。这是我发布的a question。
我最终做的是在 grunt 配置中按顺序显式列出文件。配置文件将如下所示:
[
'/path/to/app.js',
'/path/to/mymodule/mymodule.js',
'/path/to/mymodule/mymodule/*.js'
]
Grunt 能够找出哪些文件是重复的并且不包含它们。同样的技术也适用于 Gulp。
【讨论】:
顺便说一句,这在 gulp 下也可以正常工作。 Gulp 也不会重复文件。 帅哥,这两部杰作太棒了。我终于设置了我的 gulp.js 文件以按我想要的方式工作,用一些 html 编写,保存文件并通过一个按钮构建一个使用最佳框架和良好实践的网站。 Plus 更新将很容易,如果您不使用任何一个,您需要! 是的!我最近开始使用 Grunt,它很棒。不再将 javascript 应用程序嵌入后端框架中。 Gulp 在我的尝试中复制了文件,但我意识到我在 gulp 与文件系统中的情况不同,所以要小心!在精确的情况下,gulp 不会复制文件。 手动订购是严肃项目中的噩梦。有特殊的文件分类器 - 用于 angularjs 和其他。【参考方案2】:如果您需要一些文件在一堆文件之后,另一件有帮助的事情是从您的 glob 中排除特定文件,如下所示:
[
'/src/**/!(foobar)*.js', // all files that end in .js EXCEPT foobar*.js
'/src/js/foobar.js',
]
您可以将此与指定需要首先出现的文件结合起来,如 Chad Johnson 的回答中所述。
【讨论】:
啊,我之前确实看到了你的帖子,它帮助我将代码注入到我的src
和我的build
我看到你使用这种语法,我最终删除了那部分,因为我没有不知道我为什么需要它,现在说得通了。
哦,好吧,你的观点刚刚击中了我,这不会让 foobar.js 持久吗?不应该反过来。 ['./source/js/*.js', './source/js/**/underscore.js', './source/js/**/!(underscore)*.js']
是的,这只是额外的一点帮助。当您需要或希望在加载其他所有内容后进入核心应用程序代码时,它最有用。如果您事先包含了特定文件,则没有理由使用它 (!(foobar)
)。
对于一个 AngularJS 应用程序,我的模块定义驻留在名称中没有“破折号”的文件中,这个 Gulp glob 模式有效; ['src/app/**/!(*-)*.js', 'src/app/**/*.js']
【参考方案3】:
我只是在文件名的开头添加数字:
0_normalize.scss
1_tikitaka.scss
main.scss
它在 gulp 中运行没有任何问题。
【讨论】:
是的,我觉得这更容易一些,我的意思是,如果您正在编译所有文件以用于生产,那么在开发中命名文件没有区别。 我刚刚发现这不能正常工作。尝试使用 1_xx、2_xx、10_xx、11_xx。至少在 Windows 下是 1_xx,10_xx,11_xx,2_xx 就我个人而言,我完全不同意这样一种说法,即在开发中命名文件并不重要。所有的开发都应该首先以人为中心,而不是以计算机为中心。更改文件以匹配构建工具会破坏构建工具的目的。更改您的构建以匹配您的项目,而不是相反。 @JonHieb 在某种程度上,给你的文件加上一个数字前缀也将帮助下一个开发人员了解每个文件的依赖关系,不是吗?如果我打开一个文件夹并看到 1_foo.js、2_bar.js、3_baz.js,我将按此顺序打开这些文件,并在 1_foo.js 处阅读开始阅读代码 我发现 gulp.src 是异步运行的,这意味着在一个目录中有更多文件要处理的情况下,它不能始终如一地工作。【参考方案4】:我使用了 gulp-order 插件,但它并不总是成功的,正如您在我的堆栈溢出帖子 gulp-order node module with merged streams 中看到的那样。在浏览 Gulp 文档时,我遇到了 streamque 模块,该模块在指定我的案例串联顺序方面效果很好。 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 插件根本无法始终如一地工作(就像您指出的 order 一样),这真是令人遗憾,因为 gulp 的架构概念非常壮观,只有这么多人实施和维护我认为他们的插件很糟糕......我发现底层节点模块更有用所以谢谢你的这个解决方案!效果很好! streamqueue,事件流对我不起作用,但 merge2 按预期工作npmjs.com/package/merge2【参考方案5】:我在一个模块环境中,所有都是使用 gulp 的核心依赖项。
因此,core
模块需要在其他模块之前附加。
我做了什么:
-
将所有脚本移至
src
文件夹
只需将gulp-rename
您的core
目录转至_core
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 是两个模块。
【讨论】:
【参考方案6】:sort-stream 也可用于确保带有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/'));
);
【讨论】:
我希望这个模块能工作,因为它对我来说似乎是最简单的答案,但在我的例子中,我有编号的文件名和一个非常简单的比较函数,这不起作用。 @JeremyJohn 你应该可以使用return a.path.localeCompare(b.path, undefined, numeric: true )
进行排序【参考方案7】:
使用 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
【讨论】:
这太完美了!我讨厌需要定义顺序的答案!你知道吗?顺序在那里:在 HTML 文件中。完美的解决方案。【参考方案8】:对于我从 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 可能会更好,因为它们被广泛使用,并且浏览器已经可以从其他站点缓存它们。
【讨论】:
【参考方案9】:试试stream-series。它的工作方式类似于merge-stream/event-stream.merge(),只是它不是交错,而是附加到末尾。它不需要您像streamqueue 那样指定对象模式,因此您的代码更清晰。
var series = require('stream-series');
gulp.task('minifyInOrder', function()
return series(gulp.src('vendor/*'),gulp.src('extra'),gulp.src('house/*'))
.pipe(concat('a.js'))
.pipe(uglify())
.pipe(gulp.dest('dest'))
);
【讨论】:
【参考方案10】:另一种方法是使用专门为此问题创建的 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
希望这会有所帮助!
【讨论】:
【参考方案11】:对我来说,我在管道中有 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))
【讨论】:
【参考方案12】:我只是使用 gulp-angular-filesort
function concatOrder()
return gulp.src('./build/src/app/**/*.js')
.pipe(sort())
.pipe(plug.concat('concat.js'))
.pipe(gulp.dest('./output/'));
【讨论】:
哎呀,我才意识到你不是在问角度,对不起【参考方案13】:如果您想订购第三方库依赖项,请尝试wiredep。这个包基本上会检查 bower.json 中的每个包依赖关系,然后为您连接它们。
【讨论】:
【参考方案14】:merge2 看起来是目前唯一工作和维护的有序流合并工具。
2020 年更新
API 总是在变化,一些库变得不可用或包含漏洞,或者它们的依赖项包含漏洞,这些漏洞多年未修复。对于文本文件操作,您最好使用自定义 NodeJS 脚本和流行的库,如 globby
和 fs-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');
【讨论】:
【参考方案15】:我从这个页面尝试了几种解决方案,但都没有奏效。我有一系列编号的文件,我只想按字母文件夹名排序,因此当通过管道传输到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
发疯的原因是什么?当我能够使用sleep()
函数(使用.map
,睡眠时间按索引递增)来正确排序流输出时,我确定gulp.src
正在异步运行。
src
的异步结果意味着文件较多的目录排在文件较少的目录之后,因为它们需要更长的处理时间。
【讨论】:
您是否找到了同步(嗯,至少是确定性的)替代方案?【参考方案16】:在我的 gulp 设置中,我首先指定供应商文件,然后再指定(更一般的)所有内容。它成功地将供应商 js 放在其他自定义内容之前。
gulp.src([
// vendor folder first
path.join(folder, '/vendor/**/*.js'),
// custom js after vendor
path.join(folder, '/**/*.js')
])
【讨论】:
【参考方案17】:显然您可以将“nosort”选项传递给 gulp.src gulp.src。
【讨论】:
该选项是在 gulp 4 中添加的。以上是关于使用 Gulp 按顺序连接脚本的主要内容,如果未能解决你的问题,请参考以下文章