50行JS编写一个模版引擎

342 阅读1分钟

虽然现在前端都是Vue,React,Angular当道,模版引擎使用的比较少了。但是还是有必要了解一下其中的原理。说不定下一次面试就碰到了呢 如果帮助到了你 请点个赞哦

模版引擎的通用结构代码

   <div>
     <%= a %> //加上‘=’直接输出变量
     <% for(let i=0; i < supplies.length; i++) { %> 
         <li><%= supplies[i] %></li>
     <% } %> //不加‘=’可以写入任意的JavaScript表达式
   </div>

模版编译

 const compile = function (template) {
      if (!template) {
        return;
      }
      const EvalExpr = /<%=(.+?)%>/g;
      const expr = /<%([\s\S]+?)%>/g;
      //$1表示第一个匹配到的内容
      //以此类推$2,$3,$4...表示第n个匹配到的内容
      let compileTemplate = template
      //直接替换模版中的变量
       .replace(EvalExpr, "`); \n  echo( $1 ); \n  echo(`")
       //可以用于输出JavaScript表达式
       .replace(expr, "`); \n $1 \n  echo(`");
     return "echo(`" + compileTemplate + "`);";
  }

调用模版编译方法

 const render = compile(
    `<div>
          <%= a %>
          <% for(let i=0; i < supplies.length; i++) { %> 
              <li><%= supplies[i] %></li>
          <% } %> 
    </div>`);

得到的编译模版

image.png

模板渲染

我们需要在调用compile函数返回一个render函数 处理编译后的模版 render函数需要根据用户提供的数据进行渲染

const compile = function (template) {
   if (!template) {
    return;
  }
  const EvalExpr = /<%=(.+?)%>/g;
  const expr = /<%([\s\S]+?)%>/g;

  //$1表示第一个匹配到的内容
  //以此类推$2,$3,$4...表示第n个匹配到的内容
  let compileTemplate = template
    //直接替换模版中的变量
    .replace(EvalExpr, "`); \n  echo( $1 ); \n  echo(`")
    //可以用于输出JavaScript表达式
    .replace(expr, "`); \n $1; \n  echo(`");

  compileTemplate =  "echo(`" + compileTemplate + "`);";
  
  const parseDataSource = function (dataSource) {
    let variables = "";
    if (dataSource instanceof Object) {
      variables = "let ";
      const dataKeys = Object.keys(dataSource);
      if (dataKeys.length === 0) {
        return "";
      }
      dataKeys.forEach((item, index) => {
        return (variables += `${item}=${JSON.stringify(
          dataSource[item]
        )}${index === dataKeys.length - 1 ? ";" : ","}`);
      });
    }
    return variables;
  };
          
  return function (data) {
     //echo输出html
    let script = `(function parse(dataSource) {
      'use strict';
      let output = '';
      ${parseDataSource(data)}
      function echo(html) {
        output += html;
      }
      ${compileTemplate}
      return output;
    }())`;
    return eval(script);
  };

使用模版渲染方法

 const render = compile(
    `<div>
          <%= a %>
          <% for(let i=0; i < supplies.length; i++) { %> 
              <li><%= supplies[i] %></li>
          <% } %> 
    </div>`);
 render({
     a:1,
     supplies:[1,2,3]
 })

render函数生成的script字符串

image.png

render函数返回的html

image.png

结语

匆匆忙忙的写完 有任何的issue 可以在评论区留言哦 在线改