9

我正在试验 HTML 字符串模板文字(使用 NodeJS 的电子)。

它运作良好,但看起来一团糟。我想念的是 HTML 和逻辑之间的分离。

这是我所拥有的

module.exports = {
  snippet: (value1, value2) => {
    return `
<div>
  <h1>Hello ${value1 + ' - ' + value2}</h1>
</div>
`;
  },
};

建议输出

我希望我能做的如下(不起作用):

module.exports = {
  snippet: (value1, value2) => {
    return require('snippet.html')(value1, value2);
  },
};
<div>
  <h1>Hello ${value1 + ' - ' + value2}</h1>
</div>

为什么它不起作用

字符串是扁平的,不能带参数

我可以从 HTML 文件中获取 HTML 作为字符串。问题是它将是“扁平的”,我不能再使用模板文字中的逻辑了。

eval 是邪恶而缓慢的

我已经读过可以将 HTML 作为字符串运行eval以使其再次工作。我还读到它对安全性不利,而且速度很慢。

其他注意事项

  • 我使用nodeJS所以我需要module.exports在js文件中使用。
  • 我使用嵌套片段,因此模板文字仍然可以正常工作很重要。它应该可以带变量、函数等。
  • 我不介意新的 ES6 语法。
  • 我知道 Vue、React 和其他框架。我想保持它的小,这次没有设置服务器的麻烦。

更新 - 稍微好一点,但还不够

我现在已经从模板中分离出逻辑。尽管如此,两者都在同一个文件中,这使得 html 的可读性降低。

module.exports = {
  snippet: (value1, value2) => {
    /* Possible to put whatever logic in here */
    return html(value1, value2);
  },
};

function html(value1, value2) {
  return `
<div>
  <h1>Hello ${value1 + ' - ' + value2}</h1>
</div>
`;
}
4

2 回答 2

4
  1. 我知道 Vue、React 和其他框架。我想保持它的,这次没有设置服务器的麻烦。

    开发您自己的模板框架绝不是一项轻松的任务。我不知道你的意思是什么,但是当你把这个短语和像VueReactother frameworks这样的怪物放在一起时,我想你不知道小而简单的模板库。我不确定当前的情况,但是上次我检查了这个列表中的库或者这个列表中的库非常小且易于使用

  2. 或者,也许您会发现这种方法引人注目https://jonsuh.com/blog/javascript-templating-without-a-library/

  3. 您实际上不必module.exports在最新的NodeJS版本上使用

  4. 如果你对你的,Update但唯一的问题是模板与片段在同一个文件中,那么是什么阻止你拆分文件?

    // app.js
    console.log(require('./snippet.js').snippet(1,2))
    
    // snippet.js
    module.exports = {
      snippet: (value1, value2) => {
        /* Possible to put whatever logic in here */
        return require('./template.js')(value1, value2)
      },
    }
    
    // template.js
    module.exports = (value1, value2) => `
    <div>
      <h1>Hello ${value1 + ' - ' + value2}</h1>
    </div>
    `
    

作为旁注:

  1. eval 是邪恶而缓慢的

    来自MDN

    eval()是一个危险的函数,它以调用者的权限执行它传递的代码。如果您使用可能受到恶意方影响eval()的字符串运行,您最终可能会在具有您网页/扩展程序权限的用户机器上运行恶意代码。

    所以如果你只通过它运行你自己的代码eval()是完美的保存......但是,当然,当你实现模板时,这可能意味着在某些时候你会希望允许用户输入模板,这会带来麻烦. 所以可能,是的,eval在你的情况下是不安全的。

    我只是想指出这eval是极其危险的,但在所有情况下都不是绝对的邪恶。

    它也没有必要慢。当您说时,您必须始终问自己:“与什么相比?在什么情况下?”。阅读这篇文章,你就会明白为什么它变慢。但可以不代表必须

  2. 从同一段:

    更重要的是,第三方代码可以看到调用 eval() 的范围,这可能导致类似Function不易受到的攻击。

    您可以考虑使用Function. 它仍然很危险。但少之又少。


在第二个想法 - 你应该使用Function,或者干脆放弃编写模板引擎的想法。因为它的不安全性并不重要:使用模板文字的想法是不安全Function的:

const template = (value1, value2) => `
<div>
  <h1>Hello ${value1 + ' - ' + value2}</h1>
</div>
`
some_node.innerHTML = template("</h1><script>console.log("malicious injection")</script><h1>", "something else")

顺便说一句innerHTML也是不安全的

于 2020-06-03T19:49:20.853 回答
2

模板有变量,因此它应该是一个函数。你已经举了一个例子:

function html(value1, value2) {
  return `
<div>
  <h1>Hello ${value1 + ' - ' + value2}</h1>
</div>
`;
}

到目前为止,一切都很好。您想将逻辑与模板分开。您不能 100% 将它们分开,因为您的逻辑需要知道要使用哪些模板,并且您的模板需要以某种方式应用您的逻辑结果。但是你可以把交汇点做得很薄。考虑一下:

function templateHello(data) {
  return `
<div>
  <h1>Hello ${computeValues(data)}</h1>
</div>
`;
}

function computeValues(data) {
    return data.value1 + "-" + data.value2;
}

请注意,已更改:

  • 函数的名字,html不是一个具体的名字,因为你所有的模板都返回html
  • 你的 h1 的内容,因为你是绝对正确的,业务逻辑不应该出现在模板中
  • 创建了一个计算 h1 内部文本的函数

让我们更进一步:

class Template{
    first(data) {return `...`;}
    second(data) {return `...`;}
}

不错,不是吗?您当然可以使用更有意义的名称。不要将业务逻辑放在模板中,函数调用除外。以这种方式:

  • 您将拥有一些纯逻辑的辅助功能
  • 您的模板将不包含逻辑,除了那些辅助函数的调用
  • 您将能够将模板函数作为函数调用
  • 模板和逻辑之间的交汇点层将是模板内的函数调用

您可以将模板函数用作另一个模板函数的子模板,例如:

function firstHelper(templateObject, data) {
    var output = "";
    for (let item of data.items) {
        output += templateObject.second(item);
    }
    return output;
}

class Template {
    first(data) {
        return `<p>${firstHelper(this, data)}</p>`;
    }
    second(item) {
        return `<a href="${item.href}">${item.text}</a>`;
    }
}

var t = new Template();
t.first({items: [{href: "a", text: "b"}, {href: "c", text: "d"}]});

当然,您可以根据需要实现帮助类、继承模板类和覆盖方法。

于 2020-06-04T08:51:16.153 回答