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的一个功能,我从来不知道-我知道字符串模板文字,但不知道你可以用它们调用函数没有括号!

作为感谢,我在这里分享我的typescript改编,这使得它很容易用typescript知道的命名变量来制作一个可重用的模板——它允许任何类型,因为它们会自动转换为字符串,但如果你不喜欢这个策略,你可以自己调整。


/**
 * Use this with a template literal in order to create reusable string template;
 * use interpolation to add strings for each variable you want to use in the template.
 * 
 * e.g.:
 * 
 *  const reUsableStringTemplate = stringTpl`${'name'} and ${'otherName'} are going to ${'place'}`;
 * 
 * You can then call it with:
 * 
 *  const filled = reUsableStringTemplate({name: 'John', otherName: 'Jane', place: 'Paris'});
 *  // John and Jane are going to Paris
 * 
 * reUsableStringTemplate will have types and know the names of your variables
 * 
 * @returns String template function with full typescript types
 */
export function stringTpl<keys extends string>(parts: TemplateStringsArray, ...keys: keys[]) {
  return (opts: Record<keys, any>) => {
    let outStr = '';
    for (let i = 0; i < parts.length; ++i) {
      outStr += parts[i];
      const key = keys.shift();
      if (key && key in opts) {
        outStr += opts[key];
      } else {
        outStr += key ?? '';
      }
    }
    return outStr;
  };
}

其他回答

为了使这些文字像其他模板引擎一样工作,需要一种中间形式。

最好的方法是使用Function构造函数。

const templateString = "Hello ${this.name}!";
const templateVars = {
    name: "world"    
}

const fillTemplate = function(templateString, templateVars){
    return new Function("return `"+templateString +"`;").call(templateVars);
}

console.log(fillTemplate(templateString, templateVars));

与其他模板引擎一样,您可以从其他地方(如文件)获取该字符串。

使用此方法可能会出现一些问题(例如,更难添加模板标记)。由于后期的插值,您也不能使用内联JavaScript逻辑。这也可以通过一些思考来补救。

你可以在函数中放入一个模板字符串:

function reusable(a, b) {
  return `a is ${a} and b is ${b}`;
}

你可以用带标签的模板做同样的事情:

function reusable(strings) {
  return function(... vals) {
    return strings.map(function(s, i) {
      return `${s}${vals[i] || ""}`;
    }).join("");
  };
}

var tagged = reusable`a is ${0} and b is ${1}`; // dummy "parameters"
console.log(tagged("hello", "world"));
// prints "a is hello b is world"
console.log(tagged("mars", "jupiter"));
// prints "a is mars b is jupiter"

其思想是让模板解析器从变量“slots”中分离出常量字符串,然后返回一个函数,该函数每次都根据一组新的值将它们重新组合在一起。

我解决了这个插值模板使用:

function flatKeys(inputObject: any): {[key: string]: any} {
    const response: {[key: string]: any} = {};
  function iterative(currentObject: any, parentKeys: string[]=[]) {
    const llaves = Object.keys(currentObject);
    for (let i=0; i<llaves.length; i++) {
        const llave: string = llaves[i];
      const valor = currentObject[llave];
      const llavesNext = parentKeys.concat(llave);
      if (typeof valor == 'object') {
        iterative(valor, llavesNext);
      } else {
        response[llavesNext.join('.')] = valor;
      }
    }
  }
  iterative(inputObject);
  return response;
}

function interpolate(template: string, values: any, defaultValue='') {
  const flatedValues = flatKeys(values);
  const interpolated = template.replace(/\${(.*?)}/g, function (x,g) {
    const value = flatedValues[g];
    if ([undefined, null].indexOf(value) >= 0) {
      return defaultValue;
    }
    return value;
  });
  return interpolated;
}

const template = "La ${animal.name} tomaba ${alimento.name} con el ${amigos.0.names}";
const values = {
    animal: {
    name:"Iguana"
  },
  alimento: {
    name: "café"
  },
  amigos: [
    { name: "perro" },
    true
  ]
};

const interpolated = interpolate(template, values);

console.log(interpolated);

如果你使用的是Angular,你可以像下面这样使用@ngx-translate/core包:

import { TranslateDefaultParser } from '@ngx-translate/core';

export class SomeClass {
    public parser = new TranslateDefaultParser();
    test() {
        // outputs "This is my great reusable template!"
        this.parser.interpolate('This is my {{expletive}} reusable template!', { expletive: 'great' });
    }
    ...
}

const fillTemplate =(模板,值)=> { 模板= template.replace (/(?<=\${)\ w + (? =}) / g, v = >“。”+ v); 返回功能。应用(这个,””、“返回”模板“+ +”;“])打电话给(值); }; console.log(fillTemplate("The man ${man}是${brother}的兄弟",{man: "John",兄弟:"Peter"})); //约翰是彼得的兄弟