#69 简单的模版引擎


  • 0
    administrators

    模版引擎是在前端是非常常用的一种工具。请你完成一个简单的模版引擎的 render 函数,它可以接受模版字符串和一个数据对象作为参数。函数执行返回渲染以后的模版字符串,例如:

    const templateStr = `
    <ul class="users">
      <% users.forEach((user) => { %>
        <li class="user-item">
          <%= 'My name is ' + user.name %>
        </li>
      <% }) %>
    </ul>
    `
    
    const data = {
      users: [
        { name: 'Jerry', age: 12 },
        { name: 'Lucy', age: 13 }, 
        { name: 'Tomy', age: 14 }
      ]
    }
    
    render(templateStr, data)
    /*返回结果:
    
    <ul class="users">
      <li class="user-item">
        My name is Jerry
      </li>
      <li class="user-item">
        My name is Lucy
      </li>
      <li class="user-item">
        My name is Tomy
      </li>
    </ul>
    
    */
    

    <%%> 之间可以放置任意的 JavaScript 代码,而 <%=%> 之间执行任意的 JavaScript 表达式并且输出在模版上;传入的 data 可以作为模版引擎执行的上下文进行数据的引用,请你完成 render 函数。

    (提示:你可以结合 执行任意表达式 来实现)


  • 3

    倒是想再玩玩编译器的。。。不过这架势基本要做一个简易js引擎才能完成了。。。

    const spliter = /\<\%.*?\%\>/g
    
    const render = (template, data) => {
      const plain = template.split(spliter)
      const dynamic = template.match(spliter).map(str => str.startsWith(`<%=`) ? `yield(${str.slice(3, -2).trim()})` : str.slice(2, -2).trim())
      const code = plain.map((txt, i) => i in dynamic ? `yield(${JSON.stringify(txt)})\n${dynamic[i]}\n` : `yield(${JSON.stringify(txt)})\n`).join('')
      const params = Object.getOwnPropertyNames(data)
      const output = []
      new Function(...params, "yield", code)(...params.map(name => data[name]), t => output.push(t))
      return output.join('')
    }
    

    用yield主要是为了防止名字冲突。。。因为几乎没人用yield作为属性名(毕竟是关键字)


  • 4
    管理员

    正则实现

    const render = (template, data) => {
    	const result = 
        	`var p=[];with(data){p.push('` + template
        	.replace(/[\r\n\t]/g,'')
            .replace(/<%=(.*?)%>/g,`');p.push($1);p.push('`)
            .replace(/<%/g,`');`)
            .replace(/%>/g,`;p.push('`)
            + `');} p.join('');`; 
    
        return eval(result);
    }
    

  • 0

    const render = (template, data) => /* TODO */
    {
      let exp0 = /<%=(.+)%>/g
      let exp1 = /<%(.+)%>/g
      template = template.replace(exp0,'`);\n echo($1) \n echo(`').replace(exp1,'`);\n $1 \n echo(`')
      template = 'echo(`' + template +'`)'
      //${template}的结果为 调用若干echo函数
      let comp = `
      let html = ''
      function echo(t){
        html+=t
      }
      ${template}
      return html
      `
      return Function(...Object.keys(data),comp)(...Object.values(data))
    }
    

    代码有点丑


  • 0

    傻瓜式解题

    const render = (template, data) => {
      const reg1 = /<%[^=](.*?)%>/g;
      const reg2 = /<%=(.*?)%>/g;
      let code = 'const r = [];';
      let match;
      let cursor = 0;
      while (match = reg1.exec(template)) {
        code += `r.push(\`${template.slice(cursor, match.index)}\`);`;
        code += match[1] + '\n';
        cursor = match.index + match[0].length;
      }
      code += `r.push(\`${template.slice(cursor)}\`);`;
      code = code.replace(reg2, ($1, match) => {
        return "${" + match + "}";
      });
      code += 'return r.join("");';
      return new Function('with(this) {' + code + '}').apply(data);
    }
    

  • 0

    const render = (tmpl, context) => {
      const l = tmpl.length
    
      let i = 0
      let s = ''
      let c = ''
    
      let res = [
        'let r = "";'
      ]
    
      while (i < l) {
        c = tmpl[i]
    
        if (c === '\n') {
          i++
    
          continue
        }
    
        if (c === '<' && tmpl[i + 1] === '%') {
          res.push('r += \'' + s + '\';')
          s = ''
    
          const j = tmpl.indexOf('%>', i + 2)
    
          if (tmpl[i + 2] === '=') {
            i = i + 3
            res.push('r += ' + tmpl.slice(i, j) + ';')
          } else {
            i = i + 2
            res.push(tmpl.slice(i, j))
          }
    
          i = j + 2
        } else {
          s += c
          i++
        }
      }
    
      s && res.push('r += \'' + s + '\';')
    
      res.push('return r;')
    
      const f = new Function('with (this) {\n' + res.join('\n') + '\n}')
    
      return f.call(context)
    }
    
    

登录后回复
 

与 ScriptOJ 的连接断开,我们正在尝试重连,请耐心等待