Javascript 让 Grunt 为不同的设置生成 index.html
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12401998/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Have Grunt generate index.html for different setups
提问by Dmitry Pashkevich
I'm trying to use Grunt as a build tool for my webapp.
我正在尝试使用 Grunt 作为我的 web 应用程序的构建工具。
I want to have at least two setups:
我想至少有两个设置:
I. Development setup- load scripts from separate files, without concatenation,
I. 开发设置- 从单独的文件加载脚本,不串联,
so my index.html would look something like:
所以我的 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>
II. Production setup- load my scripts minified & concatenated in one file,
二、生产设置- 加载我的脚本,并在一个文件中进行缩小和连接,
with index.html accordingly:
与 index.html 相应地:
<!DOCTYPE html>
<html>
<head>
<script src="js/MyApp-all.min.js" />
</head>
<body></body>
</html>
The question is, how can I make grunt make these index.html's depending on the configuration when I run grunt dev
or grunt prod
?
问题是,当我运行或运行时,如何根据配置使 grunt 生成这些 index.html ?grunt dev
grunt prod
Or maybe I'm digging in the wrong direction and it would be easier to always generate MyApp-all.min.js
but put inside it either all my scripts (concatenated) or a loader script that asynchronously loads those scripts from separate files?
或者,也许我在错误的方向上挖掘,总是更容易生成MyApp-all.min.js
但将我的所有脚本(连接)或从单独的文件异步加载这些脚本的加载器脚本放入其中?
How do you do it, guys?
各位,你们是怎么做的?
采纳答案by Dmitry Pashkevich
I've come up with my own solution. Not polished yet but I think I'm going to move in that direction.
我想出了我自己的解决方案。还没有完善,但我想我会朝着那个方向前进。
In essense, I'm using grunt.template.process()to generate my index.html
from a template that analyzes current configuration and produces either a list of my original source files or links to a single file with minified code. The below example is for js files but the same approach can be extended to css and any other possible text files.
从本质上讲,我使用grunt.template.process()index.html
从分析当前配置的模板生成我的,并生成我的原始源文件列表或带有缩小代码的单个文件的链接。下面的示例适用于 js 文件,但相同的方法可以扩展到 css 和任何其他可能的文本文件。
grunt.js
:
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)
:
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 + '\'');
});
}
Finally, index.tmpl
, with generation logic baked in:
最后,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>
UPD.Found out that Yeoman, which is based on grunt, has a built-in usemintask that integrates with Yeoman's build system. It generates a production version of index.html from information in development version of index.html as well as other environment settings. A bit sophisticated but interesting to look at.
更新。发现基于 grunt 的Yeoman内置了usemin任务,与 Yeoman 的构建系统集成。它根据 index.html 开发版本中的信息以及其他环境设置生成 index.html 的生产版本。有点复杂,但看起来很有趣。
回答by mhulse
I recently discovered these Grunt v0.4.0
compatible tasks:
我最近发现了这些与 Gruntv0.4.0
兼容的任务:
Grunt task around preprocess npm module.
Grunt task to automate environment configuration for future tasks.
Below are snippets from my Gruntfile.js
.
以下是我的Gruntfile.js
.
ENV setup:
环境设置:
env : {
options : {
/* Shared Options Hash */
//globalOption : 'foo'
},
dev: {
NODE_ENV : 'DEVELOPMENT'
},
prod : {
NODE_ENV : 'PRODUCTION'
}
},
Preprocess:
预处理:
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 %>'
}
}
}
}
Tasks:
任务:
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']);
And in the /src/tmpl/index.html
template file (for example):
在/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 -->
I'm sure my setup is different than most people, and the usefulness of the above will depend on your situation. For me, while it's an awesome bit of code, the Yeoman grunt-useminis a more robust than I personally need.
我确信我的设置与大多数人不同,上述内容的用处将取决于您的情况。对我来说,虽然这是一段很棒的代码,但 Yeoman grunt-usemin比我个人需要的更强大。
NOTE:I justdiscovered the above listed tasks today, so I might be missing a feature and/or my process may change down the road. For now, I'm loving the simplicity andfeatures that grunt-preprocessand grunt-envhave to offer. :)
注意:我今天刚刚发现了上面列出的任务,所以我可能会遗漏一个功能和/或我的流程可能会在未来发生变化。现在,我喜欢grunt-preprocess和grunt-env必须提供的简单性和功能。:)
Jan 2014 update:
2014 年 1 月更新:
Motivated by a down vote ...
受到反对票的推动......
When I posted this answer there weren't many options for Grunt 0.4.x
that offered a solution that worked for my needs. Now, months later, I would guess that there are more options out there that couldbe better than what I have posted here. While I still personally use, and enjoy using, this technique for my builds, I ask that future readers take the time to read the other answers given and to research all the options. If you find a better solution, please post your answer here.
当我发布此答案时,Grunt0.4.x
提供的解决方案可满足我的需求的选项并不多。现在,几个月后,我想还有更多的选择可能比我在这里发布的更好。虽然我个人仍然使用并喜欢在我的构建中使用这种技术,但我要求未来的读者花时间阅读给出的其他答案并研究所有选项。如果您找到更好的解决方案,请在此处发布您的答案。
Feb 2014 update:
2014 年 2 月更新:
I'm not sure if it will be of any help to anyone, but I've created this demo repository on GitHubthat shows a complete (and more complex setup) using the technique(s) I've outlined above.
我不确定它是否对任何人有帮助,但我已经在 GitHub 上创建了这个演示存储库,它使用我上面概述的技术展示了一个完整的(和更复杂的设置)。
回答by Daniel Kaplan
I dislike the solutions here (including the one I previously gave) and here's why:
我不喜欢这里的解决方案(包括我之前给出的解决方案),原因如下:
- The problem with the highest voted answeris that you have to manually sync the list of script tags when you add/rename/delete a JS file.
- The problem with the accepted answeris that your list of JS files can't have pattern matching. This means you've got to update it by hand in the Gruntfile.
I've figured out how to solve both of these issues. I've set up my grunt task so that every time a file is added or deleted, the script tags automatically get generated to reflect that. This way, you don't need to modify your html file or your grunt filewhen you add/remove/rename your JS files.
我已经想出了如何解决这两个问题。我已经设置了我的 grunt 任务,以便每次添加或删除文件时,都会自动生成脚本标记以反映这一点。这样,当您添加/删除/重命名 JS 文件时,您无需修改 html 文件或 grunt文件。
To summarize how that works, I have a html template with a variable for the script tags. I use https://github.com/alanshaw/grunt-include-replaceto populate that variable. In dev mode, that variable comes from a globbing pattern of all my JS files. The watch task recalculates this value when a JS file is added or removed.
为了总结它的工作原理,我有一个带有脚本标签变量的 html 模板。我使用https://github.com/alanshaw/grunt-include-replace来填充该变量。在开发模式下,该变量来自我所有 JS 文件的通配模式。当添加或删除 JS 文件时,watch 任务会重新计算此值。
Now, to get different results in dev or prod mode, you simply populate that variable with a different value. Here's some code:
现在,要在 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
is your typical grunt file-globbing pattern. jsScriptTags
takes the jsSrcFileArray
and concatenates them together with script
tags on both sides. destPath
is the prefix I want on each file.
jsSrcFileArray
是典型的 grunt 文件通配模式。 jsScriptTags
获取jsSrcFileArray
并将它们与script
两侧的标签连接在一起。 destPath
是我想要的每个文件的前缀。
And here's what the HTML looks like:
这是 HTML 的样子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Example</title>
</head>
<body>
@@scriptsTags
</body>
</html>
Now, as you can see in the config, I generate the value of that variable as a hard coded script
tag when it's run in prod
mode. In dev mode, this variable will expand to a value like this:
现在,正如您在配置中看到的那样,script
当它在prod
模式下运行时,我将该变量的值生成为硬编码标记。在开发模式下,此变量将扩展为如下值:
<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>
Let me know if you have any questions.
如果您有任何问题,请告诉我。
PS: This is a crazy amount of code for something I'd want to do in every client-side JS app. I hope someone can turn this into a reusable plugin. Maybe I will some day.
PS:对于我想在每个客户端 JS 应用程序中做的事情来说,这是大量的代码。我希望有人可以把它变成一个可重用的插件。也许有一天我会。
回答by Per Quested Aronsson
I have been asking myself the same question for a while, and I think this grunt plugin could be configured to do what you want: https://npmjs.org/package/grunt-targethtml. It implements conditional html tags, that depend on the grunt target.
我一直在问自己同样的问题,我认为这个 grunt 插件可以配置为做你想做的事:https: //npmjs.org/package/grunt-targethtml。它实现了条件 html 标签,这取决于 grunt 目标。
回答by Edward Tan
I was looking for a more simple, straight forward solution so I combined the answer from this question:
我正在寻找一个更简单、直接的解决方案,所以我结合了这个问题的答案:
How to place if else block in gruntfile.js
如何在 gruntfile.js 中放置 if else 块
and came up with following simple steps:
并提出了以下简单步骤:
- Keep two versions of your index files as you listed and name them index-development.html and index-prodoction.html.
Use the following logic in your Gruntfile.js's concat/copy block for your index.html file:
concat: { index: { src : [ (function() { if (grunt.option('Release')) { return 'views/index-production.html'; } else { return 'views/index-development.html'; } }()) ], dest: '<%= distdir %>/index.html', ... }, ... },
run 'grunt --Release' to choose the index-production.html file and leave off the flag to have the development version.
- 保留您列出的索引文件的两个版本,并将它们命名为 index-development.html 和 index-prodoction.html。
在您的 index.html 文件的 Gruntfile.js 的 concat/copy 块中使用以下逻辑:
concat: { index: { src : [ (function() { if (grunt.option('Release')) { return 'views/index-production.html'; } else { return 'views/index-development.html'; } }()) ], dest: '<%= distdir %>/index.html', ... }, ... },
运行 'grunt --Release' 以选择 index-production.html 文件并去掉标志以获得开发版本。
No new plugins to add or configure and no new grunt tasks.
没有要添加或配置的新插件,也没有新的 grunt 任务。
回答by brillout
grunt-dom-mungerreads and manipulates HTML with CSS selectors. Ex. read tags from your html. Remove nodes, add nodes, and more.
grunt-dom-munger使用 CSS 选择器读取和操作 HTML。前任。从您的 html 中读取标签。删除节点、添加节点等。
You can use grunt-dom-munger to read all your JS files that are linked by your index.html, uglify them and then use grunt-dom-munger again to modify your index.html to only link the minified JS
您可以使用 grunt-dom-munger 读取由 index.html 链接的所有 JS 文件,将它们丑化,然后再次使用 grunt-dom-munger 修改您的 index.html 以仅链接缩小的 JS
回答by Daniel Kaplan
This grunt task named scriptlinkerlooks like an easy way to add the scripts in dev mode. You could probably run a concat task first and then point it to your concatenated file in prod mode.
这个名为scriptlinker 的grunt 任务看起来是一种在开发模式下添加脚本的简单方法。您可以先运行 concat 任务,然后在 prod 模式下将其指向您的连接文件。
回答by anonymous
I found a grunt plugin called grunt-dev-prod-switch. All it does is comment out certain blocks it looks for based on an --env option you pass to grunt (although it limits you to dev, prod, and test).
我找到了一个叫 grunt-dev-prod-switch 的 grunt 插件。它所做的只是根据您传递给 grunt 的 --env 选项注释掉它寻找的某些块(尽管它限制了您的开发、生产和测试)。
Once you set it up as it explains here, you can run for example:
按照此处的说明进行设置后,您可以运行例如:
grunt serve --env=dev
, and all it does is comment out the blocks which are wrapped by
grunt serve --env=dev
,它所做的只是注释掉由
<!-- env:test/prod -->
your code here
<!-- env:test/prod:end -->
and it will uncomment out blocks which are wrapped by
它会取消注释被包裹的块
<!-- env:dev -->
your code here
<!-- env:dev:end -->
It also works on javascript, I use it for setting up the right IP address to connect to for my backend API. The blocks just change to
它也适用于 javascript,我用它来设置正确的 IP 地址以连接到我的后端 API。块只是更改为
/* env:dev */
your code here
/* env:dev:end */
In your case, it would be as simple as this:
在你的情况下,它会像这样简单:
<!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>
回答by imaginethepoet
grunt-bake is a fantastic grunt script that would work great here. I use it in my JQM auto build script.
grunt-bake 是一个很棒的 grunt 脚本,在这里效果很好。我在我的 JQM 自动构建脚本中使用它。
https://github.com/imaginethepoet/autojqmphonegap
https://github.com/imaginethepoet/autojqmphonegap
Take a look at my grunt.coffee file:
看看我的 grunt.coffee 文件:
bake:
resources:
files: "index.html":"resources/custom/components/base.html"
This looks at all the files in base.html and sucks them in to create index.html works fantastic for multipage apps (phonegap). This allows for easier development as all devs are not working on one long single page app (preventing lots of conflict checkins). Instead you can break up the pages and work on smaller chunks of code and compile to the full page using a watch command.
这会查看 base.html 中的所有文件并将它们吸入创建 index.html 对多页应用程序(phonegap)来说非常棒。这允许更轻松的开发,因为所有开发人员都没有在一个长的单页应用程序上工作(防止大量冲突签入)。相反,您可以拆分页面并处理较小的代码块,然后使用 watch 命令编译为完整页面。
Bake reads the template from base.html and injects the component html pages on watch.
Bake 从 base.html 读取模板并在 watch 上注入组件 html 页面。
<!DOCTYPE html>
jQuery Mobile Demos
jQuery 移动演示
app.initialize();
app.initialize();
<body>
<!--(bake /resources/custom/components/page1.html)-->
<!--(bake /resources/custom/components/page2.html)-->
<!--(bake /resources/custom/components/page3.html)-->
</body>
You can take this a step further and add injections in your pages for "menus" "popups" etc so you can really break pages into smaller manageable components.
您可以更进一步,在您的页面中添加“菜单”“弹出窗口”等的注入,这样您就可以真正将页面分解为更小的可管理组件。
回答by Scottux
Use a combination of wiredep https://github.com/taptapship/wiredepand usemin https://github.com/yeoman/grunt-useminin order to have grunt take care of these tasks. Wiredep will add your dependencies one script file at a time, and usemin will concatenate them all into a single file for production. This can then be accomplished with just some html comments. For instance, my bower packages are automatically included and added to the html when I run bower install && grunt bowerInstall
:
使用wiredep https://github.com/taptapship/wiredep和usemin https://github.com/yeoman/grunt-usemin的组合,以便grunt 处理这些任务。Wiredep 将一次添加您的依赖项一个脚本文件,而 usemin 会将它们全部连接到一个文件中以进行生产。这可以通过一些 html 注释来完成。例如,当我运行时,我的凉亭包会自动包含并添加到 html 中bower install && grunt bowerInstall
:
<!-- build:js /scripts/vendor.js -->
<!-- bower:js -->
<!-- endbower -->
<!-- endbuild -->