tl;dr:有可能使一个可重用的模板文字吗?

我一直试图使用模板文字,但我想我只是不明白,现在我感到沮丧。我是说,我觉得我懂了,但"它"不应该是这样的,也不应该是这样的。情况应该有所不同。

我看到的所有示例(甚至是带标签的模板)都要求“替换”在声明时完成,而不是在运行时完成,这对模板来说似乎完全没有用。也许我疯了,但“模板”对我来说是一个包含标记的文档,这些标记在你使用它时被替换,而不是在你创建它时,否则它只是一个文档(即字符串)。模板存储与令牌作为令牌&这些令牌评估时,你…评估它。

每个人都举了一个可怕的例子:

var a = 'asd';
return `Worthless ${a}!`

这很好,但如果我已经知道a,我只会返回'一文不值asd'或返回'一文不值'+a。有什么意义?认真对待。好吧,重点是懒惰;加号少,可读性强。太好了。但这不是模板!恕我直言。MHO才是最重要的!问题,恕我直言,是模板在声明时被求值,所以,如果你这样做,恕我直言:

var tpl = `My ${expletive} template`;
function go() { return tpl; }
go(); // SPACE-TIME ENDS!

由于没有声明expletive,它输出类似于My undefined template的内容。超级。实际上,至少在Chrome中,我甚至不能声明模板;它抛出一个错误,因为没有定义咒骂。我需要的是能够在声明模板后进行替换:

var tpl = `My ${expletive} template`;
function go() { return tpl; }
var expletive = 'great';
go(); // My great template

然而,我不明白这是怎么可能的,因为这些都不是真正的模板。即使你说我应该使用标签,不,它们不起作用:

> explete = function(a,b) { console.log(a); console.log(b); }
< function (a,b) { console.log(a); console.log(b); }
> var tpl = explete`My ${expletive} template`
< VM2323:2 Uncaught ReferenceError: expletive is not defined...

这一切都让我相信模板文字的名字是可怕的错误,应该被称为他们真正的名字:heredocs。我猜“字面”部分应该提示我(在,不可变)?

我遗漏了什么吗?是否有一种(好的)方法来创建一个可重用的模板文字?


我给你,可重用的模板文字

> function out(t) { console.log(eval(t)); }
  var template = `\`This is
  my \${expletive} reusable
  template!\``;
  out(template);
  var expletive = 'curious';
  out(template);
  var expletive = 'AMAZING';
  out(template);
< This is
  my undefined reusable
  template!
  This is
  my curious reusable
  template!
  This is
  my AMAZING reusable
  template!

这里是一个简单的“helper”函数…

function t(t) { return '`'+t.replace('{','${')+'`'; }
var template = t(`This is
my {expletive} reusable
template!`);

...让它“更好”。

我倾向于称它们为模板肠,因为它们产生扭曲感觉的区域。


当前回答

可以使用以下函数动态解析模板,提供新数据。

这使用了javascript的一个不太常见的特性,称为Tagged Template Literal


function template(...args) {
  return (values) =>
    args[0].reduce(
      (acum, current, index) => 
        acum.concat(
          current, values[index] === undefined ? '' : values[index]
        ),
      ''
    )
}

const person = 'Lydia';
const age = 21;

template `${person} is ${age} years old... yes He is ${age}`(['jose', 35, 38]); //?

其他回答

当我看到它的时候,我非常头疼。javascript中的文字模板非常酷,但它们****可重用或带有动态值。但解决方法非常简单。事实上,在花了几天时间编写解析器、格式化器和其他解决方案后,我不得不踢自己几次。最后在我放弃了这个想法,打算使用胡子或其他模板模块后,它击中了我.....

const DT = function dynamicTemplate(source) { return (new Function(`return \`${source}\``))() }

//let a = 1, b = 2;
//DT("${a} + ${b} equals ${a + b}")
// prints '1 + 2 equals 3'

这就是她写的全部内容。

我只是发布了一个npm包,它可以简单地完成这项工作。 深受这个答案的启发。

const Template = require('dynamic-template-string');

var tpl = new Template('hello ${name}');

tpl.fill({name: 'world'}); // ==> 'hello world';
tpl.fill({name: 'china'}); // ==> 'hello china';

它的实现非常简单。希望你会喜欢。


module.exports = class Template {
  constructor(str) {
    this._func = new Function(`with(this) { return \`${str}\`; }`);
  }

  fill(data) {
    return this._func.call(data);
  }
}

我遗漏了什么吗?是否有一个[好]方法来使一个可重用的模板文字?

也许我遗漏了一些东西,因为我对这个问题的解决方案对我来说太明显了,以至于我很惊讶没有人在这样一个古老的问题中写下这个答案。

对于这个问题,我几乎只有一句话:

function defer([first, ...rest]) {
  return (...vals) => rest.reduce((acc, str, i) => acc + vals[i] + str, first);
}

这是所有。当我想重用一个模板并推迟替换的解析时,我只做:

function defer([first, ...rest]) { return (...vals) => rest.reduce((acc, str, i) => acc + vals[i] + str, first); } t = defer`My template is: ${null} and ${null}`; a = t('simple', 'reusable'); // 'My template is: simple and reusable' b = t('obvious', 'late to the party'); // 'My template is: obvious and late to the party' c = t(null); // 'My template is: null and undefined' d = defer`Choose: ${'ignore'} / ${undefined}`(true, false); // 'Choose: true / false' console.log(a + "\n" + b + "\n" + c + "\n" + d + "\n");

应用此标记将返回一个'函数'(而不是'字符串'),该函数将忽略传递给字面量的任何参数。然后可以稍后用新参数调用它。如果一个参数没有相应的替换,它就变成了“undefined”。


扩展的回答

这段简单的代码是功能性的,但是如果您需要更详细的行为,可以应用相同的逻辑,并且有无限的可能性。你可以:

利用原始参数:

您可以在结构中存储传递给文字的原始值,并在应用模板时以创造性的方式使用它们。它们可以成为标志、类型验证器、函数等。下面是一个使用它们作为默认值的例子:

function deferWithDefaults([first,…,…默认值){ 返回(…values) => rest。Reduce ((acc, curr, i) => { 返回acc + (i < values。长度呢?值[i]: defaults[i]) + curr; },第一); } 我的模板是:${'extendable'}和${'versatile'}; A = t('awesome'); // '我的模板是:awesome and versatile' console.log(一个);

写一个模板工厂:

通过将此逻辑包装在一个函数中,该函数期望作为参数的自定义函数可以应用于约简(当连接模板字面量的片段时),并返回具有自定义行为的新模板。

然后,你可以,例如,编写模板,自动转义或清除参数时,编写嵌入式html, css, sql, bash…

使用这个naïve(我重复一遍,naïve!) sql模板,我们可以构建这样的查询:

const createTemplate = fn => function (strings, ...defaults) { const [first, ...rest] = strings; return (...values) => rest.reduce((acc, curr, i) => { return acc + fn(values[i], defaults[i]) + curr; }, first); }; function sqlSanitize(token, tag) { // this is a gross simplification, don't use in production. const quoteName = name => (!/^[a-z_][a-z0-9_$]*$/ .test(name) ? `"${name.replace(/"/g, '""')}"` : name); const quoteValue = value => (typeof value == 'string' ? `'${value.replace(/'/g, "''")}'` : value); switch (tag) { case 'table': return quoteName(token); case 'columns': return token.map(quoteName); case 'row': return token.map(quoteValue); default: return token; } } const sql = createTemplate(sqlSanitize); q = sql`INSERT INTO ${'table'} (${'columns'}) ... VALUES (${'row'});` a = q('user', ['id', 'user name', 'is"Staff"?'], [1, "O'neil", true]) // `INSERT INTO user (id,"user name","is""Staff""?") // VALUES (1,'O''neil',true);` console.log(a);

接受命名参数进行替换:基于已经给出的内容,这是一个不那么困难的练习。在另一个答案中有一个实现。 使返回对象的行为像一个“字符串”:嗯,这是有争议的,但可能会导致有趣的结果。在另一个答案中。 解析调用站点全局命名空间中的参数:

我给你,可重用的模板文字

这是OP展示的附录,使用了邪恶的命令,我的意思是eval。这可以在没有eval的情况下完成,只需将传递的变量名搜索到全局(或窗口)对象中即可。我不演示怎么做,因为我不喜欢它。闭包是正确的选择。

UPDATED:下面的答案仅限于单个变量名,因此,'Result ${a+b}'这样的模板在这种情况下无效。然而,你总是可以使用模板值:

format("This is a test: ${a_b}", {a_b: a+b});

最初的回答:

基于之前的答案,但创建了一个更“友好”的实用函数:

var format = (template, params) => {
    let tpl = template.replace(/\${(?!this\.)/g, "${this.");
    let tpl_func = new Function(`return \`${tpl}\``);

    return tpl_func.call(params);
}

你可以像这样开具发票:

format("This is a test: ${hola}, second param: ${hello}", {hola: 'Hola', hello: 'Hi'});

结果字符串应该是:

'This is a test: Hola, second param: Hi'

简化@metamorphasi提供的答案;

const fillTemplate = function(templateString, templateVars){ var func = new函数(…Object.keys(templateVars), "return ' "+templateString +" ';") 返回func(…Object.values (templateVars)); } / /样本 var hosting = "overview/id/d:${id}"; var domain = {Id:1234,用户:22}; var result = fillTemplate(主机,域); console.log(结果);