有Grunt生成index.html不同的设置

我试图使用Grunt作为我的网络应用程序的构建工具。

我想至少有两个设置:

一、开发设置 -从单独的文件加载脚本,没有连接,

所以我的index.html看起来是这样的:

<!DOCTYPE html>
<html>
<head>
<script src="js/module1.js" />
<script src="js/module2.js" />
<script src="js/module3.js" />
...
</head>
<body></body>
</html>

2生产设置 -加载我的脚本缩小&连接到一个文件中,

根据index.html:

<!DOCTYPE html>
<html>
<head>
<script src="js/MyApp-all.min.js" />
</head>
<body></body>
</html>

问题是,当我运行__ABC0或grunt prod时,我如何使grunt使这些index.html取决于配置?

或者也许我在错误的方向上挖掘,它会更容易总是生成MyApp-all.min.js,但把它里面要么我所有的脚本(连接)或加载器脚本异步加载这些脚本从单独的文件?

你们是怎么做到的,伙计们?

77533 次浏览

我已经有了自己的解决办法。还没有完善,但我想我会朝着那个方向发展。

从本质上讲,我使用grunt.template.process ()从一个模板生成我的index.html,该模板分析当前配置,并生成我的原始源文件列表或带有简化代码的单个文件的链接。下面的例子是针对js文件的,但同样的方法可以扩展到css和任何其他可能的文本文件。

grunt.js:

/*global module:false*/
module.exports = function(grunt) {
var   // js files
jsFiles = [
'src/module1.js',
'src/module2.js',
'src/module3.js',
'src/awesome.js'
];


// Import custom tasks (see index task below)
grunt.loadTasks( "build/tasks" );


// Project configuration.
grunt.initConfig({
pkg: '<json:package.json>',
meta: {
banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %> */'
},


jsFiles: jsFiles,


// file name for concatenated js
concatJsFile: '<%= pkg.name %>-all.js',


// file name for concatenated & minified js
concatJsMinFile: '<%= pkg.name %>-all.min.js',


concat: {
dist: {
src: ['<banner:meta.banner>'].concat(jsFiles),
dest: 'dist/<%= concatJsFile %>'
}
},
min: {
dist: {
src: ['<banner:meta.banner>', '<config:concat.dist.dest>'],
dest: 'dist/<%= concatJsMinFile %>'
}
},
lint: {
files: ['grunt.js'].concat(jsFiles)
},
// options for index.html builder task
index: {
src: 'index.tmpl',  // source template file
dest: 'index.html'  // destination file (usually index.html)
}
});




// Development setup
grunt.registerTask('dev', 'Development build', function() {
// set some global flags that all tasks can access
grunt.config('isDebug', true);
grunt.config('isConcat', false);
grunt.config('isMin', false);


// run tasks
grunt.task.run('lint index');
});


// Production setup
grunt.registerTask('prod', 'Production build', function() {
// set some global flags that all tasks can access
grunt.config('isDebug', false);
grunt.config('isConcat', true);
grunt.config('isMin', true);


// run tasks
grunt.task.run('lint concat min index');
});


// Default task
grunt.registerTask('default', 'dev');
};

index.js (the index task):

module.exports = function( grunt ) {
grunt.registerTask( "index", "Generate index.html depending on configuration", function() {
var conf = grunt.config('index'),
tmpl = grunt.file.read(conf.src);


grunt.file.write(conf.dest, grunt.template.process(tmpl));


grunt.log.writeln('Generated \'' + conf.dest + '\' from \'' + conf.src + '\'');
});
}

最后,包含生成逻辑的index.tmpl:

<doctype html>
<head>
<%
var jsFiles = grunt.config('jsFiles'),
isConcat = grunt.config('isConcat');


if(isConcat) {
print('<script type="text/javascript" src="' + grunt.config('concat.dist.dest') + '"></script>\n');
} else {
for(var i = 0, len = jsFiles.length; i < len; i++) {
print('<script type="text/javascript" src="' + jsFiles[i] + '"></script>\n');
}
}
%>
</head>
<html>
</html>

乌利希期刊指南。发现基于grunt的自由民有一个内置的usemin任务,它与Yeoman的构建系统集成在一起。它根据index.html开发版本中的信息以及其他环境设置生成index.html的生产版本。有点复杂,但看起来很有趣。

我一直在问自己同样的问题有一段时间了,我认为这个grunt插件可以配置为你想要的:https://npmjs.org/package/grunt-targethtml。它实现了条件html标记,依赖于grunt目标。

我最近发现了这些Grunt v0.4.0兼容的任务:

围绕npm模块预处理任务。

为将来的任务自动化环境配置的Grunt任务。

下面是我的Gruntfile.js的片段。

ENV设置:

env : {
    

options : {
        

/* Shared Options Hash */
//globalOption : 'foo'
        

},
    

dev: {
        

NODE_ENV : 'DEVELOPMENT'
        

},
    

prod : {
        

NODE_ENV : 'PRODUCTION'
        

}
    

},

预处理:

preprocess : {
    

dev : {
        

src : './src/tmpl/index.html',
dest : './dev/index.html'
        

},
    

prod : {
        

src : './src/tmpl/index.html',
dest : '../<%= pkg.version %>/<%= now %>/<%= ver %>/index.html',
options : {
            

context : {
name : '<%= pkg.name %>',
version : '<%= pkg.version %>',
now : '<%= now %>',
ver : '<%= ver %>'
}
            

}
        

}
    

}

任务:

grunt.registerTask('default', ['jshint']);


grunt.registerTask('dev', ['jshint', 'env:dev', 'clean:dev', 'preprocess:dev']);


grunt.registerTask('prod', ['jshint', 'env:prod', 'clean:prod', 'uglify:prod', 'cssmin:prod', 'copy:prod', 'preprocess:prod']);

/src/tmpl/index.html模板文件中(例如):

<!-- @if NODE_ENV == 'DEVELOPMENT' -->
    

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.js"></script>
<script src="../src/js/foo1.js"></script>
<script src="../src/js/foo2.js"></script>
<script src="../src/js/jquery.blah.js"></script>
<script src="../src/js/jquery.billy.js"></script>
<script src="../src/js/jquery.jenkins.js"></script>
    

<!-- @endif -->


<!-- @if NODE_ENV == 'PRODUCTION' -->
    

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    

<script src="http://cdn.foo.com/<!-- @echo name -->/<!-- @echo version -->/<!-- @echo now -->/<!-- @echo ver -->/js/<!-- @echo name -->.min.js"></script>
    

<!-- @endif -->

我相信我的设置和大多数人不同,上面的有用性取决于你的情况。对我来说,虽然这是一段很棒的代码,但Yeoman grunt-usemin比我个人需要的更健壮。

注意:只是今天发现了上面列出的任务,所以我可能会错过一个功能和/或我的过程可能会改变。现在,我喜欢grunt-preprocessgrunt-env所提供的简单的而且特性。:)


我不确定它是否会对任何人有任何帮助,但我已经创建了这个演示库在GitHub上,它使用我上面概述的技术显示了一个完整的(和更复杂的设置)。

这个名为scriptlinker的任务看起来是在开发模式中添加脚本的简单方法。您可以先运行连接任务,然后以prod模式将其指向连接的文件。

grunt-bake是一个很棒的grunt脚本,可以在这里工作。我在JQM自动构建脚本中使用了它。

https://github.com/imaginethepoet/autojqmphonegap

看看我的呼噜声。咖啡文件:

bake:
resources:
files: "index.html":"resources/custom/components/base.html"

这将查看base.html中的所有文件,并将它们吸收到创建index.html中,这对于多页面应用(phonegap)非常有用。这让开发变得更容易,因为所有的开发者都不需要开发一个很长的单页应用(防止大量冲突)。相反,您可以将页面拆分,处理较小的代码块,并使用watch命令编译成整个页面。

Bake从base.html中读取模板,并在watch中注入组件html页面。

<!DOCTYPE html>

jQuery Mobile demo

app.initialize ();

<body>
<!--(bake /resources/custom/components/page1.html)-->
<!--(bake /resources/custom/components/page2.html)-->
<!--(bake /resources/custom/components/page3.html)-->
</body>

你可以更进一步,在页面中为“菜单”、“弹出窗口”等添加注入,这样你就可以真正地将页面分解成更小的可管理组件。

grunt-dom-munger使用CSS选择器读取和操作HTML。例如,从html中读取标签。删除节点、添加节点等等。

你可以使用grunt-dom-munger读取所有由index.html链接的JS文件,丑化它们,然后再次使用grunt-dom-munger修改index.html,只链接最小化的JS文件

我一直在寻找一个更简单、直接的解决方案,所以我结合了这个问题的答案:

如何在gruntfile.js中放置if else块 .js

并提出了以下简单的步骤:

  1. 保留你列出的两个版本的索引文件,命名为index-development.html和index-prodoction.html。
  2. 在你的Gruntfile.js的concat/copy块中为你的index.html文件使用以下逻辑:

    concat: {
    index: {
    src : [ (function() {
    if (grunt.option('Release')) {
    return 'views/index-production.html';
    } else {
    return 'views/index-development.html';
    }
    }()) ],
    dest: '<%= distdir %>/index.html',
    ...
    },
    ...
    },
    
  3. run 'grunt --Release' to choose the index-production.html file and leave off the flag to have the development version.

No new plugins to add or configure and no new grunt tasks.

我不喜欢这里的解决方案(包括我之前给的那个),原因如下:

  • 得票最高的答案的问题是,当你添加/重命名/删除一个JS文件时,你必须手动同步脚本标签列表。
  • 公认的答案的问题是你的JS文件列表不能有模式匹配。这意味着你必须在Gruntfile中手动更新它。

我已经找到了解决这两个问题的方法。我已经设置了grunt任务,以便每次添加或删除文件时,都会自动生成脚本标记来反映该任务。这样,当你添加/删除/重命名JS文件时,您不需要修改HTML文件或grunt文件

为了总结它是如何工作的,我有一个带有脚本标记变量的html模板。我使用https://github.com/alanshaw/grunt-include-replace填充该变量。在开发模式下,该变量来自我所有JS文件的通配符模式。当添加或删除JS文件时,监视任务会重新计算这个值。

现在,要在dev或prod模式中获得不同的结果,只需用不同的值填充该变量。下面是一些代码:

var jsSrcFileArray = [
'src/main/scripts/app/js/Constants.js',
'src/main/scripts/app/js/Random.js',
'src/main/scripts/app/js/Vector.js',
'src/main/scripts/app/js/scripts.js',
'src/main/scripts/app/js/StatsData.js',
'src/main/scripts/app/js/Dialog.js',
'src/main/scripts/app/**/*.js',
'!src/main/scripts/app/js/AuditingReport.js'
];


var jsScriptTags = function (srcPattern, destPath) {
if (srcPattern === undefined) {
throw new Error("srcPattern undefined");
}
if (destPath === undefined) {
throw new Error("destPath undefined");
}
return grunt.util._.reduce(
grunt.file.expandMapping(srcPattern, destPath, {
filter: 'isFile',
flatten: true,
expand: true,
cwd: '.'
}),
function (sum, file) {
return sum + '\n<script src="' + file.dest + '" type="text/javascript"></script>';
},
''
);
};


...


grunt.initConfig({


includereplace: {
dev: {
options: {
globals: {
scriptsTags: '<%= jsScriptTags(jsSrcFileArray, "../../main/scripts/app/js")%>'
}
},
src: [
'src/**/html-template.html'
],
dest: 'src/main/generated/',
flatten: true,
cwd: '.',
expand: true
},
prod: {
options: {
globals: {
scriptsTags: '<script src="app.min.js" type="text/javascript"></script>'
}
},
src: [
'src/**/html-template.html'
],
dest: 'src/main/generatedprod/',
flatten: true,
cwd: '.',
expand: true
}


...


jsScriptTags: jsScriptTags

jsSrcFileArray是典型的grunt文件替换模式。jsScriptTags接受jsSrcFileArray并在两边使用script标记将它们连接在一起。destPath是我想在每个文件上的前缀。

下面是HTML的样子:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Example</title>


</head>


<body>
@@scriptsTags
</body>
</html>

现在,正如你在配置中看到的,当它以prod模式运行时,我将该变量的值生成为硬编码的script标记。在dev模式下,该变量将展开为如下值:

<script src="../../main/scripts/app/js/Constants.js" type="text/javascript"></script>
<script src="../../main/scripts/app/js/Random.js" type="text/javascript"></script>
<script src="../../main/scripts/app/js/Vector.js" type="text/javascript"></script>
<script src="../../main/scripts/app/js/StatsData.js" type="text/javascript"></script>
<script src="../../main/scripts/app/js/Dialog.js" type="text/javascript"></script>

如果你有任何问题请告诉我。

PS:对于我想在每个客户端JS应用中做的事情来说,这是一个疯狂的代码量。我希望有人能把它变成一个可重用的插件。也许有一天我会的。

使用wiredep https://github.com/taptapship/wiredep和usemin https://github.com/yeoman/grunt-usemin的组合,以便让grunt处理这些任务。Wiredep会一次添加一个脚本文件,而usemin会将它们全部连接到一个文件中进行生产。这可以通过一些html注释来完成。例如,当我运行bower install && grunt bowerInstall时,我的bower包会自动包含并添加到html中:

<!-- build:js /scripts/vendor.js -->
<!-- bower:js -->
<!-- endbower -->
<!-- endbuild -->

我发现了一个叫咕哝-dev-prod-switch的插件。它所做的只是根据您传递给grunt的——env选项注释掉它所寻找的某些块(尽管它将您限制为dev、prod和test)。

一旦你设置了它,因为它解释了在这里,你可以运行,例如:

grunt serve --env=dev,它所做的就是注释掉被

    <!-- env:test/prod -->
your code here
<!-- env:test/prod:end -->

它会取消注释那些被

    <!-- env:dev -->
your code here
<!-- env:dev:end -->

它也适用于javascript,我用它来设置正确的IP地址连接到我的后端API。方块变成

    /* env:dev */
your code here
/* env:dev:end */

在你的情况下,它会像这样简单:

<!DOCTYPE html>
<html>
<head>
<!-- env:dev -->
<script src="js/module1.js" />
<script src="js/module2.js" />
<script src="js/module3.js" />
...
<!-- env:dev:end -->
<!-- env:prod -->
<script src="js/MyApp-all.min.js" />
...
<!-- env:prod:end -->
</head>
<body></body>
</html>

这个答案不适合新手!

使用玉石模板…将变量传递给Jade模板是一个标准的用例

我用的是grunt(咕哝-contrib-jade),但你不必用grunt。只需使用标准的npm jade模块。

如果使用grunt,那么你的gruntfile会像这样…

jade: {
options: {
// TODO - Define options here
},
dev: {
options: {
data: {
pageTitle: '<%= grunt.file.name %>',
homePage: '/app',
liveReloadServer: liveReloadServer,
cssGruntClassesForHtmlHead: 'grunt-' + '<%= grunt.task.current.target %>'
},
pretty: true
},
files: [
{
expand: true,
cwd: "src/app",
src: ["index.jade", "404.jade"],
dest: "lib/app",
ext: ".html"
},
{
expand: true,
flatten: true,
cwd: "src/app",
src: ["directives/partials/*.jade"],
dest: "lib/app/directives/partials",
ext: ".html"
}
]
}
},

现在我们可以很容易地访问Jade模板中grunt传递的数据。

与Modernizr使用的方法非常相似,我根据传递的变量值在HTML标记上设置了一个CSS类,并且可以根据CSS类是否存在从那里使用JavaScript逻辑。

如果使用Angular,这是非常棒的,因为你可以根据类是否存在来在页面中包含元素。

例如,我可能会包含一个脚本,如果类是存在的…

(例如,我可能在开发中包含实时重载脚本,但在生产中不包含)

<script ng-if="controller.isClassPresent()" src="//localhost:35729/livereload.js"></script>

考虑processhtml。它允许为构建定义多个“目标”。注释用于有条件地从HTML中包含或排除材料:

<!-- build:js:production js/app.js -->
...
<!-- /build -->

就变成了

<script src="js/app.js"></script>

它甚至声称可以做这样漂亮的事情(参见的自述):

<!-- build:[class]:dist production -->
<html class="debug_mode">
<!-- /build -->


<!-- class is changed to 'production' only when the 'dist' build is executed -->
<html class="production">