问题
In the html-loader documentation there is this example
require("html?interpolate=require!./file.ftl");
<#list list as list>
<a href="${list.href!}" />${list.name}</a>
</#list>
<img src="${require(`./images/gallery.png`)}">
<div>${require('./components/gallery.html')}</div>
Where does "list" come from? How can I provide parameters to the interpolation scope?
I would like to do something like template-string-loader does:
var template = require("html?interpolate!./file.html")({data: '123'});
and then in file.html
<div>${scope.data}</div>
But it doesn't work. I have try to mix the template-string-loader with the html-loader but it doesn't works. I could only use the template-string-loader but then the images in the HTML are not transformed by webpack.
Any ideas? Thank you
回答1:
Solution 1
I found another solution, using html-loader
with interpolate
option.
https://github.com/webpack-contrib/html-loader#interpolation
{ test: /\.(html)$/,
include: path.join(__dirname, 'src/views'),
use: {
loader: 'html-loader',
options: {
interpolate: true
}
}
}
And then in html page you can import partials html and javascript variables.
<!-- Importing top <head> section -->
${require('./partials/top.html')}
<title>Home</title>
</head>
<body>
<!-- Importing navbar -->
${require('./partials/nav.html')}
<!-- Importing variable from javascript file -->
<h1>${require('../js/html-variables.js').hello}</h1>
<!-- Importing footer -->
${require('./partials/footer.html')}
</body>
The only downside is that you can't import other variables from HtmlWebpackPlugin
like this <%= htmlWebpackPlugin.options.title %>
(at least I can't find a way to import them) but for me it's not an issue, just write the title in your html or use a separate javascript file for handle variables.
Solution 2
Old answer
Not sure if this is the right solution for you but I'll share my workflow (tested in Webpack 3).
Instead of html-loader
you can use this plugin github.com/bazilio91/ejs-compiled-loader:
{ test: /\.ejs$/, use: 'ejs-compiled-loader' }
Change your .html
files in .ejs
and your HtmlWebpackPlugin
to point to the right .ejs
template:
new HtmlWebpackPlugin({
template: 'src/views/index.ejs',
filename: 'index.html',
title: 'Home',
chunks: ['index']
})
You can import partials, variables, and assets in .ejs
files:
src/views/partials/head.ejs
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
src/js/ejs_variables.js
:
const hello = 'Hello!';
const bye = 'Bye!';
export {hello, bye}
src/views/index.ejs
:
<% include src/views/partials/head.ejs %>
<body>
<h2><%= require("../js/ejs_variables.js").hello %></h2>
<img src=<%= require("../../assets/sample_image.jpg") %> />
<h2><%= require("../js/ejs_variables.js").bye %></h2>
</body>
A note, when you include a partial the path must be relative to the root of your project.
回答2:
mustache-loader did the work for me:
var html = require('mustache-loader!html-loader?interpolate!./index.html')({foo:'bar'});
Then in your template you can use {{foo}}
, and even insert other templates
<h1>{{foo}}</h1>
${require('mustache-loader!html-loader?interpolate!./partial.html')({foo2: 'bar2'})}
回答3:
if you use template engine from htmlWebpackPlugin
in partial, you can use like this:
<!-- index.html -->
<body>
<div id="app"></div>
<%= require('ejs-loader!./partial.gtm.html')({ htmlWebpackPlugin }) %>
</body>
<!-- partial.gtm.html -->
<% if (GTM_TOKEN) { %>
<noscript>
<iframe
src="https://www.googletagmanager.com/ns.html?id=<%= GTM_TOKEN %>"
height="0"
width="0"
style="display:none;visibility:hidden"
></iframe>
</noscript>
<% } %>
// webpack.config.json
{
plugins: [
new webpack.DefinePlugin({
GTM_TOKEN: process.env.GTM_TOKEN,
}),
],
}
need npm i ejs-loader
回答4:
You might laugh, but using default loaders provided with HTMLWebpackPlugin you could do string replacement on the HTML-partial file.
- index.html is ejs template (ejs is the default loader in HTMLWebpackPlugin)
- file.html is just an html string (loaded via html-loader also available by default with HTMLWebpackPlugin or maybe it comes with webpack?)
Setup
Just use the default ejs templating provided in HTMLWebpackPlugin
new HtmlWebpackPlugin({
template: 'src/views/index.ejs',
filename: 'index.html',
title: 'Home',
chunks: ['index'],
templateParameters(compilation, assets, options) {
return {
foo: 'bar'
}
}
})
Here's my top level ejs file
// index.html
<html lang="en" dir="ltr">
<head>
<title><%=foo%></title>
</head>
<body>
<%
var template = require("html-loader!./file.html");
%>
<%= template.replace('${foo}',foo) %>
</body>
</html>
Here's file.html, which html-loader
exports as a string.
// file.html
<h1>${foo}</h1>
回答5:
I feel that Potench's's answer above should be the accepted one, but it comes with a caveat:
Warning: the answer replaces htmlWebpackPlugin.options
default object. Suggest augmenting, not replacing
function templateParametersGenerator (compilation, assets, options) {
return {
compilation: compilation,
webpack: compilation.getStats().toJson(),
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: assets,
options: options,
// your extra parameters here
}
};
}
Source(s): 1 - https://github.com/jantimon/html-webpack-plugin/blob/8440e4e3af94ae5dced4901a13001c0628b9af87/index.js#L719-L729 2 - https://github.com/jantimon/html-webpack-plugin/issues/1004#issuecomment-411311939
回答6:
You can make it on your own: In html-loader plugin folder (in index.js) replace code by this
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
var htmlMinifier = require("html-minifier");
var attrParse = require("./lib/attributesParser");
var loaderUtils = require("loader-utils");
var url = require("url");
var assign = require("object-assign");
var compile = require("es6-templates").compile;
function randomIdent() {
return "xxxHTMLLINKxxx" + Math.random() + Math.random() + "xxx";
}
function getLoaderConfig(context) {
var query = loaderUtils.getOptions(context) || {};
var configKey = query.config || 'htmlLoader';
var config = context.options && context.options.hasOwnProperty(configKey) ? context.options[configKey] : {};
delete query.config;
return assign(query, config);
}
module.exports = function(content) {
this.cacheable && this.cacheable();
var config = getLoaderConfig(this);
var attributes = ["img:src"];
if(config.attrs !== undefined) {
if(typeof config.attrs === "string")
attributes = config.attrs.split(" ");
else if(Array.isArray(config.attrs))
attributes = config.attrs;
else if(config.attrs === false)
attributes = [];
else
throw new Error("Invalid value to config parameter attrs");
}
var root = config.root;
var links = attrParse(content, function(tag, attr) {
var res = attributes.find(function(a) {
if (a.charAt(0) === ':') {
return attr === a.slice(1);
} else {
return (tag + ":" + attr) === a;
}
});
return !!res;
});
links.reverse();
var data = {};
content = [content];
links.forEach(function(link) {
if(!loaderUtils.isUrlRequest(link.value, root)) return;
if (link.value.indexOf('mailto:') > -1 ) return;
var uri = url.parse(link.value);
if (uri.hash !== null && uri.hash !== undefined) {
uri.hash = null;
link.value = uri.format();
link.length = link.value.length;
}
do {
var ident = randomIdent();
} while(data[ident]);
data[ident] = link.value;
var x = content.pop();
content.push(x.substr(link.start + link.length));
content.push(ident);
content.push(x.substr(0, link.start));
});
content.reverse();
content = content.join("");
if (config.interpolate === 'require'){
var reg = /\$\{require\([^)]*\)\}/g;
var result;
var reqList = [];
while(result = reg.exec(content)){
reqList.push({
length : result[0].length,
start : result.index,
value : result[0]
})
}
reqList.reverse();
content = [content];
reqList.forEach(function(link) {
var x = content.pop();
do {
var ident = randomIdent();
} while(data[ident]);
data[ident] = link.value.substring(11,link.length - 3)
content.push(x.substr(link.start + link.length));
content.push(ident);
content.push(x.substr(0, link.start));
});
content.reverse();
content = content.join("");
}
if(typeof config.minimize === "boolean" ? config.minimize : this.minimize) {
var minimizeOptions = assign({}, config);
[
"removeComments",
"removeCommentsFromCDATA",
"removeCDATASectionsFromCDATA",
"collapseWhitespace",
"conservativeCollapse",
"removeAttributeQuotes",
"useShortDoctype",
"keepClosingSlash",
"minifyJS",
"minifyCSS",
"removeScriptTypeAttributes",
"removeStyleTypeAttributes",
].forEach(function(name) {
if(typeof minimizeOptions[name] === "undefined") {
minimizeOptions[name] = true;
}
});
content = htmlMinifier.minify(content, minimizeOptions);
}
if(config.interpolate && config.interpolate !== 'require') {
// Double escape quotes so that they are not unescaped completely in the template string
content = content.replace(/\\"/g, "\\\\\"");
content = content.replace(/\\'/g, "\\\\\'");
content = JSON.stringify(content);
content = '`' + content.substring(1, content.length - 1) + '`';
//content = compile('`' + content + '`').code;
} else {
content = JSON.stringify(content);
}
var exportsString = "module.exports = function({...data}){return ";
if (config.exportAsDefault) {
exportsString = "exports.default = function({...data}){return ";
} else if (config.exportAsEs6Default) {
exportsString = "export default function({...data}){return ";
}
return exportsString + content.replace(/xxxHTMLLINKxxx[0-9\.]+xxx/g, function(match) {
if(!data[match]) return match;
var urlToRequest;
if (config.interpolate === 'require') {
urlToRequest = data[match];
} else {
urlToRequest = loaderUtils.urlToRequest(data[match], root);
}
return ' + require(' + JSON.stringify(urlToRequest) + ') + ';
}) + "};";
}
来源:https://stackoverflow.com/questions/39374187/how-can-i-provide-parameters-for-webpack-html-loader-interpolation