plugin - llamar javascript desde html
Haz que Grunt genere index.html para diferentes configuraciones (12)
Considere processhtml . Permite la definición de múltiples "objetivos" para construcciones. Los comentarios se utilizan para incluir o excluir condicionalmente material del HTML:
<!-- build:js:production js/app.js -->
...
<!-- /build -->
se convierte en
<script src="js/app.js"></script>
Incluso pretende hacer cosas ingeniosas como esta (ver el README ):
<!-- build:[class]:dist production -->
<html class="debug_mode">
<!-- /build -->
<!-- class is changed to ''production'' only when the ''dist'' build is executed -->
<html class="production">
Estoy tratando de usar Grunt como una herramienta de compilación para mi aplicación web.
Quiero tener al menos dos configuraciones:
I. Configuración de desarrollo : cargue scripts desde archivos separados, sin concatenación,
así que mi index.html se vería algo así como:
<!DOCTYPE html>
<html>
<head>
<script src="js/module1.js" />
<script src="js/module2.js" />
<script src="js/module3.js" />
...
</head>
<body></body>
</html>
II. Configuración de producción : cargue mis scripts minificados y concatenados en un archivo,
con index.html en consecuencia:
<!DOCTYPE html>
<html>
<head>
<script src="js/MyApp-all.min.js" />
</head>
<body></body>
</html>
La pregunta es, ¿cómo puedo hacer que grunt haga que index.html dependa de la configuración cuando ejecuto grunt dev
o grunt prod
?
¿O quizás estoy cavando en la dirección equivocada y sería más fácil generar siempre MyApp-all.min.js
pero poner dentro de ella todos mis scripts (concatenados) o un script de carga que carga de forma asíncrona esos scripts desde archivos separados?
¿Cómo lo haces, chicos?
Encontré un plugin de gruñido llamado grunt-dev-prod-switch. Todo lo que hace es comentar ciertos bloques que busca en función de la opción --env que pasas a grunt (aunque te limita a dev, prod y test).
Una vez que lo configura como se explica here , puede ejecutar, por ejemplo:
grunt serve --env=dev
, y todo lo que hace es comentar los bloques que están envueltos por
<!-- env:test/prod -->
your code here
<!-- env:test/prod:end -->
y descomentará los bloques que están envueltos por
<!-- env:dev -->
your code here
<!-- env:dev:end -->
También funciona en javascript, lo uso para configurar la dirección IP correcta para conectarme con mi API de back-end. Los bloques solo cambian a
/* env:dev */
your code here
/* env:dev:end */
En tu caso, sería tan simple como esto:
<!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>
Esta respuesta no es para los noobs!
Use Jade Templating ... pasar variables a una plantilla de Jade es un caso de uso estándar de bog
Estoy usando grunt (grunt-contrib-jade) pero no tienes que usar grunt. Sólo tiene que utilizar el módulo de jade npm estándar
Si usas gruñido, a tu archivo gruñón le gustaría algo como ...
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"
}
]
}
},
Ahora podemos acceder fácilmente a los datos pasados por grunt en la plantilla de Jade.
Al igual que el enfoque utilizado por Modernizr, establezco una clase CSS en la etiqueta HTML de acuerdo con el valor de la variable pasada y puedo usar la lógica de JavaScript desde allí en función de si la clase CSS está presente o no.
Esto es genial si usa Angular ya que puede hacer ng-if para incluir elementos en la página en función de si la clase está presente.
Por ejemplo, podría incluir un script si la clase está presente ...
(Por ejemplo, podría incluir el script de recarga en vivo en dev pero no en producción)
<script ng-if="controller.isClassPresent()" src="//localhost:35729/livereload.js"></script>
Esta tarea grunt llamada scriptlinker parece una forma fácil de agregar los scripts en modo dev. Probablemente podría ejecutar primero una tarea concat y luego apuntarla a su archivo concatenado en modo prod.
Estaba buscando una solución más simple y directa, así que combiné la respuesta de esta pregunta:
Cómo colocar, si no, bloquear en gruntfile.js
y se le ocurrieron los siguientes pasos simples:
- Conserve dos versiones de sus archivos de índice como aparece en la lista y asígnele el nombre index-development.html y index-prodoction.html.
Use la siguiente lógica en el bloque de copia / concat de Gruntfile.js para su archivo 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'', ... }, ... },
ejecute ''grunt --Release'' para elegir el archivo index-production.html y deje de marcar para tener la versión de desarrollo.
No hay nuevos complementos para agregar o configurar y no hay nuevas tareas grunt.
He encontrado mi propia solución. Aún no está pulido, pero creo que voy a avanzar en esa dirección.
En esencia, estoy usando grunt.template.process() para generar mi index.html
partir de una plantilla que analiza la configuración actual y produce una lista de mis archivos de origen originales o enlaces a un solo archivo con código minificado. El siguiente ejemplo es para archivos js, pero el mismo enfoque puede extenderse a css y a cualquier otro archivo de texto posible.
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 + ''/''');
});
}
Finalmente, index.tmpl
, con lógica de generación 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. Descubrió que Yeoman , que se basa en el gruñido, tiene una tarea usemin de usemin que se integra con el sistema de construcción de Yeoman. Genera una versión de producción de index.html a partir de la información en la versión de desarrollo de index.html, así como otras configuraciones del entorno. Un poco sofisticado pero interesante a la vista.
Me he estado preguntando lo mismo durante un tiempo, y creo que este complemento de grunt podría configurarse para hacer lo que quieras: https://npmjs.org/package/grunt-targethtml . Implementa etiquetas html condicionales, que dependen del objetivo de grunt.
No me gustan las soluciones aquí (incluida la que di anteriormente ) y he aquí por qué:
- El problema con la respuesta más votada es que tiene que sincronizar manualmente la lista de etiquetas de script cuando agrega / renombra / elimina un archivo JS.
- El problema con la respuesta aceptada es que su lista de archivos JS no puede tener coincidencia de patrones. Esto significa que tienes que actualizarlo a mano en el Gruntfile.
He descubierto cómo resolver estos dos problemas. He configurado mi tarea grunt para que cada vez que se agregue o elimine un archivo, las etiquetas de script se generen automáticamente para reflejar eso. De esta manera, no necesita modificar su archivo html o su archivo grunt cuando agrega / elimina / renombra sus archivos JS.
Para resumir cómo funciona, tengo una plantilla html con una variable para las etiquetas de script. Uso https://github.com/alanshaw/grunt-include-replace para rellenar esa variable. En el modo dev, esa variable proviene de un patrón global de todos mis archivos JS. La tarea de observación recalcula este valor cuando se agrega o elimina un archivo JS.
Ahora, para obtener diferentes resultados en modo dev o prod, simplemente rellena esa variable con un valor diferente. Aquí hay un código:
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
es su patrón típico de agrupamiento de archivos. jsScriptTags
toma el jsSrcFileArray
y los concatena junto con etiquetas de script
en ambos lados. destPath
es el prefijo que quiero en cada archivo.
Y aquí está el aspecto del HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Example</title>
</head>
<body>
@@scriptsTags
</body>
</html>
Ahora, como se puede ver en la configuración, genero el valor de esa variable como una etiqueta de script
codificada cuando se ejecuta en modo prod
. En el modo dev, esta variable se expandirá a un valor como este:
<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>
Hazme saber si tienes alguna pregunta.
PD: esta es una cantidad de código para algo que querría hacer en cada aplicación JS del lado del cliente. Espero que alguien pueda convertir esto en un complemento reutilizable. Tal vez algún día lo haga.
Recientemente descubrí estas tareas compatibles con Grunt v0.4.0
:
Tarea ronca alrededor del módulo preprocesamiento npm
Tarea grunt para automatizar la configuración del entorno para tareas futuras.
A continuación hay fragmentos de mi Gruntfile.js
.
Configuración ENV:
env : {
options : {
/* Shared Options Hash */
//globalOption : ''foo''
},
dev: {
NODE_ENV : ''DEVELOPMENT''
},
prod : {
NODE_ENV : ''PRODUCTION''
}
},
Preproceso
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 %>''
}
}
}
}
Tareas:
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'']);
Y en el archivo de plantilla /src/tmpl/index.html
(por ejemplo):
<!-- @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 -->
Estoy seguro de que mi configuración es diferente a la mayoría de las personas, y la utilidad de lo anterior dependerá de su situación. Para mí, si bien es un código asombroso, el grunt-usemin es más robusto de lo que personalmente necesito.
NOTA: Acabo de descubrir las tareas enumeradas anteriormente hoy, por lo que podría faltar una función y / o mi proceso puede cambiar en el futuro. Por ahora, me encanta la simplicidad y las características que grunt-preprocess y grunt-env tienen para ofrecer. :)
Actualización de enero de 2014:
Motivado por un voto negativo ...
Cuando 0.4.x
esta respuesta, no había muchas opciones para Grunt 0.4.x
que ofrecieran una solución que funcionara para mis necesidades. Ahora, meses más tarde, creo que hay más opciones que podrían ser mejores que las que he publicado aquí. Si bien personalmente uso y disfruto usando esta técnica para mis versiones , le pido a los futuros lectores que se tomen el tiempo de leer las otras respuestas y que investiguen todas las opciones. Si encuentra una solución mejor, por favor publique su respuesta aquí.
Actualización de febrero de 2014:
No estoy seguro de que sirva de ayuda para nadie, pero he creado este repositorio de demostración en GitHub que muestra una configuración completa (y más compleja) utilizando la (s) técnica (s) que he descrito anteriormente.
Use una combinación de wiredep https://github.com/taptapship/wiredep y usemin grunt-usemin para que Grunt se encargue de estas tareas. Wiredep agregará sus dependencias un archivo de secuencia de comandos a la vez, y usemin las concatenará en un solo archivo para la producción. Esto se puede lograr con solo algunos comentarios html. Por ejemplo, mis paquetes de bower se incluyen automáticamente y se agregan al html cuando ejecuto bower install && grunt bowerInstall
:
<!-- build:js /scripts/vendor.js -->
<!-- bower:js -->
<!-- endbower -->
<!-- endbuild -->
grunt-bake es un guión fantástico que funcionaría muy bien aquí. Lo uso en mi script de compilación automática JQM.
https://github.com/imaginethepoet/autojqmphonegap
Echa un vistazo a mi archivo grunt.coffee:
bake:
resources:
files: "index.html":"resources/custom/components/base.html"
Esto analiza todos los archivos en base.html y los absorbe para crear index.html que funciona de forma fantástica para aplicaciones de varias páginas (phonegap). Esto permite un desarrollo más fácil ya que todos los desarrolladores no están trabajando en una aplicación de una sola página larga (lo que evita muchos controles de conflictos). En su lugar, puede dividir las páginas y trabajar en trozos más pequeños de código y compilar la página completa con un comando de observación.
Bake lee la plantilla desde base.html e inyecta las páginas html del componente en el reloj.
<!DOCTYPE html>
Demostraciones móviles de jQuery
app.initialize ();
<body>
<!--(bake /resources/custom/components/page1.html)-->
<!--(bake /resources/custom/components/page2.html)-->
<!--(bake /resources/custom/components/page3.html)-->
</body>
Puede llevar esto un paso más allá y agregar inyecciones en sus páginas para los "menús", "elementos emergentes", etc., de modo que realmente pueda dividir las páginas en componentes más pequeños y manejables.
grunt-dom-munger lee y manipula HTML con selectores de CSS. Ex. leer las etiquetas de su html. Eliminar nodos, agregar nodos y más.
Puede usar grunt-dom-munger para leer todos sus archivos JS que están vinculados por su index.html, verificarlos y luego usar grunt-dom-munger nuevamente para modificar su index.html para vincular solo el JS minificado