31

html-loader文档中有这个例子

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>

“清单”从何而来?如何为插值范围提供参数?

我想做类似template-string-loader 的事情:

var template = require("html?interpolate!./file.html")({data: '123'});

然后在 file.html

<div>${scope.data}</div>

但它不起作用。我尝试将模板字符串加载器与 html-loader 混合,但它不起作用。我只能使用模板字符串加载器,但是 HTML 中的图像不会被 webpack 转换。

有任何想法吗?谢谢

4

7 回答 7

19

解决方案 1

我找到了另一个解决方案,使用html-loaderwithinterpolate选项。

https://github.com/webpack-contrib/html-loader#interpolation

{ test: /\.(html)$/,
  include: path.join(__dirname, 'src/views'),
  use: {
    loader: 'html-loader',
    options: {
      interpolate: true
    }
  }
}

然后在 html 页面中,您可以导入部分 html 和 javascript 变量。

<!-- 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>

唯一的缺点是你不能HtmlWebpackPlugin像这样导入其他变量<%= htmlWebpackPlugin.options.title %>(至少我找不到导入它们的方法)但对我来说这不是问题,只需在你的 html 中写标题或使用单独的 javascript 文件用于句柄变量。

解决方案 2

旧答案

不确定这是否适合您,但我将分享我的工作流程(在 Webpack 3 中测试)。

而不是html-loader你可以使用这个插件github.com/bazilio91/ejs-compiled-loader

{ test: /\.ejs$/, use: 'ejs-compiled-loader' }

更改您的.html 文件.ejsHtmlWebpackPlugin指向正确的.ejs 模板:

new HtmlWebpackPlugin({
    template: 'src/views/index.ejs',
    filename: 'index.html',
    title: 'Home',
    chunks: ['index']
})

您可以在文件中导入部分、变量和资产.ejs

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>

注意,当您包含部分路径时,路径必须相对于项目的根目录。

于 2018-02-12T15:36:46.333 回答
4

你可能会笑,但是使用 HTMLWebpackPlugin 提供的默认加载器,你可以对 HTML 部分文件进行字符串替换。

  1. index.html 是 ejs 模板(ejs 是 HTMLWebpackPlugin 中的默认加载器)
  2. file.html 只是一个 html 字符串(通过 html-loader 加载,HTMLWebpackPlugin 也可以默认使用,或者它可能与 webpack 一起提供?)

设置

只需使用 HTMLWebpackPlugin 中提供的默认 ejs 模板即可

new HtmlWebpackPlugin({
    template: 'src/views/index.ejs',
    filename: 'index.html',
    title: 'Home',
    chunks: ['index'],
    templateParameters(compilation, assets, options) {
        return {
            foo: 'bar'
        }
    }
})

这是我的顶级 ejs 文件

// 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>

这是file.html,它html-loader以字符串形式导出。

// file.html 

<h1>${foo}</h1>
于 2019-01-26T02:06:37.107 回答
2

mustache-loader为我完成了这项工作:

var html = require('mustache-loader!html-loader?interpolate!./index.html')({foo:'bar'});

然后在你的模板中你可以使用{{foo}},甚至插入其他模板

<h1>{{foo}}</h1>
${require('mustache-loader!html-loader?interpolate!./partial.html')({foo2: 'bar2'})}
于 2018-02-20T11:40:40.063 回答
1

使用html-loaderwith interpolate,您可以webpack.config.js通过 using导入变量DefinePlugin

// webpack.config.js:

module.exports = {
  module: {
    rules: [
      {
        test: /\.html$/,
        loader: 'html-loader',
        options: {
          interpolate: true
        }
      }
    ],
  },
  plugins: [
    new DefinePlugin({
      VARNAME: JSON.stringify("here's a value!")
    })
  ]
};

// index.html

<body>${ VARNAME }</body>

html-loader的插值接受任何 JavaScript 表达式,但默认情况下,这些表达式的计算范围不会填充任何配置选项。DefinePlugin将值添加到该全局范围。EnvironmentPlugin也可用于填充process.env.

于 2020-02-28T07:05:00.017 回答
1

如果您htmlWebpackPlugin部分使用模板引擎,则可以这样使用:

  <!-- 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,
      }),
    ],
  }

需要npm i ejs-loader

于 2019-09-14T13:53:45.283 回答
0

您可以自己制作:在 html-loader 插件文件夹(在 index.js 中)中,将代码替换为

/*
	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) + ') + ';
	}) + "};";

}

于 2019-10-08T07:19:39.723 回答
0

我觉得上面Potinch 的回答应该是公认的,但它带有一个警告:

警告:答案替换htmlWebpackPlugin.options默认对象。建议增加,而不是替换

function templateParametersGenerator (compilation, assets, options) {
  return {
    compilation: compilation,
    webpack: compilation.getStats().toJson(),
    webpackConfig: compilation.options,
    htmlWebpackPlugin: {
      files: assets,
      options: options,
      // your extra parameters here
    }
  };
}

来源:1 - https://github.com/jantimon/html-webpack-plugin/blob/8440e4e3af94ae5dced4901a13001c0628b9af87/index.js#L719-L729 2 - https://github.com/jantimon/html-webpack-插件/问题/1004#issuecomment-411311939

于 2019-09-30T08:23:20.580 回答