Node服务费渲染时需要用到模板,需要用到vm模块,本质上是将字符串放到沙箱里用js引擎运行一下,运行出最终页面发送给客户端。
创建沙盒环境并执行js很简单:
const vm = require('vm') const user = { name: 'Jack' } vm.runInNewContext('`<h2>${user.name}</h2>`', { user }) // <h2>Jack</h2>
如果要做服务端渲染引擎,还需要支持模板include,并处理xss攻击:
const vm = require('vm') const user = { name: 'Jack', scrpit: '<script />' } const templateMap = { templateA: '`<h2>${include("templateB")}</h2>`', templateB: '`<p>子模块</p>`' // 这里正常是fs.readFileSync读取出的模板内容 } const context = { user, helper: function () {}, // 模板的helper函数 include: function (name) { // 模板的include函数 return templateMap[name]() }, _: function(markup) { // 如果有<srcipt>会执行,所以需要处理xss攻击 if (!markup) return '' return String(markup) .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/'/g, ''') .replace(/"/g, '"') } } vm.runInNewContext('`<h2>${_(user.scrpit)}</h2>`', context) // <h2>&lt;script /&gt;</h2> Object.keys(templateMap).forEach(key => { // 将map里每个模板都转成沙盒内执行的函数 const temp = templateMap[key] // 防止循环引用 templateMap[key]= vm.runInNewContext(` (function() { return ${temp} }); `, context) }) console.log(templateMap['templateA']()) // <h2><p>子模块</p></h2>