Javascript 让Grunt为不同的设置生成index.html

Javascript 让Grunt为不同的设置生成index.html,javascript,build,build-automation,gruntjs,Javascript,Build,Build Automation,Gruntjs,我正在尝试使用Grunt作为我的Web应用程序的构建工具 我希望至少有两个设置: I.开发设置-从单独的文件加载脚本,无需连接 因此,我的index.html看起来像: <!DOCTYPE html> <html> <head> <script src="js/module1.js" /> <script src="js/module2.js" /> <script src=

我正在尝试使用Grunt作为我的Web应用程序的构建工具

我希望至少有两个设置:

I.开发设置-从单独的文件加载脚本,无需连接

因此,我的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。生产设置-将我的脚本缩小并连接到一个文件中

相应地使用index.html:

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

问题是,在运行
grunt dev
grunt prod
时,如何使grunt生成这些index.html取决于配置

或者可能我挖错了方向,总是生成
MyApp all.min.js
会更容易,但可以将我的所有脚本(串联)或加载程序脚本(从单独的文件异步加载这些脚本)放在其中


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

我已经想出了自己的解决方案。还没有磨光,但我想我会朝那个方向走

在essense中,我使用从一个模板生成我的
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');
};
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>



UPD。发现基于grunt的内置任务与Yeoman的构建系统集成。它根据index.html开发版本中的信息以及其他环境设置生成index.html的生产版本。有点复杂,但看起来很有趣。

我一直在问自己同样的问题,我认为这个grunt插件可以被配置为做你想做的事情:。它实现了依赖于grunt目标的条件html标记。

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

  • 围绕预处理npm模块的Grunt任务

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

下面是我的
grunfile.js
中的代码片段

环境设置:

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 %>'
            }

        }

    }

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



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

grunt bake是一个很棒的grunt脚本,在这里非常有用。我在JQM自动构建脚本中使用它

请查看我的grunt.coffee文件:

bake:
    resources: 
      files: "index.html":"resources/custom/components/base.html"
这将查看base.html中的所有文件,并将它们吸进以创建index.html,这对于多页应用程序(phonegap)非常有用。这使得开发更加容易,因为所有开发人员都不使用一个长的单页应用程序(防止大量冲突签入)。相反,您可以分解页面,处理较小的代码块,并使用watch命令编译成完整的页面

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

<!DOCTYPE html>

jQuery移动演示

app.initialize();


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

使用CSS选择器读取和处理HTML。从html中读取标签。删除节点、添加节点等


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

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

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

  • 保留两个列出的索引文件版本,并将它们命名为index-development.html和index-prodoction.html
  • 在grunfile.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',
           ...
        },
        ...
    },
    
    concat:{
    索引:{
    src:[(函数(){
    if(grunt.option('Release')){
    返回“views/index production.html”;
    }否则{
    返回“views/index development.html”;
    }
    }()) ],
    dest:“/index.html”,
    ...
    },
    ...
    },
    
  • 运行“grunt--Release”选择index-production.html文件,并关闭标志以获得开发版本


  • 没有要添加或配置的新插件,也没有新的grunt任务。

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

    • 问题在于,在添加/重命名/删除JS文件时,必须手动同步脚本标记列表
    • 问题是JS文件列表不能进行模式匹配。这意味着您必须在Gruntfile中手动更新它
    我已经想出了如何解决这两个问题。我已经设置了grunt任务,以便每次添加或删除文件时,都会自动生成脚本标记以反映这一点。这样,在添加/删除/重命名JS文件时,您不需要修改html文件或grunt文件

    为了总结其工作原理,我有一个html模板,其中包含一个脚本标记变量。我用它来填充这个变量。在开发模式下,该变量来自所有JS文件的全局模式。当JS fil被删除时,监视任务将重新计算此值
    <!DOCTYPE html>
    
    <body>
        <!--(bake /resources/custom/components/page1.html)-->
        <!--(bake /resources/custom/components/page2.html)-->
        <!--(bake /resources/custom/components/page3.html)-->
    </body>
    
    concat: {
        index: {
            src : [ (function() {
                if (grunt.option('Release')) {
                  return 'views/index-production.html';
                } else {
                  return 'views/index-development.html';
                }
              }()) ],
           dest: '<%= distdir %>/index.html',
           ...
        },
        ...
    },
    
    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
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8"/>
        <title>Example</title>
    
    </head>
    
    <body>    
    @@scriptsTags
    </body>
    </html>
    
    <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>
    
    <!-- build:js /scripts/vendor.js -->
    <!-- bower:js -->
    <!-- endbower -->
    <!-- endbuild -->
    
        <!-- env:test/prod -->
        your code here
        <!-- env:test/prod:end -->
    
        <!-- env:dev -->
        your code here
        <!-- env:dev:end -->
    
        /* 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: {
        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"
            }
          ]
        }
      },
    
    <script ng-if="controller.isClassPresent()" src="//localhost:35729/livereload.js"></script> 
    
    <!-- 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">