模板渲染

9 2月

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>

发表评论

电子邮件地址不会被公开。 必填项已用*标注