适用页面嵌入 dom 业务,比如一个问卷弹窗等,类似 template.js、ejs 语法,不依赖 jq,zepto,vue,可直接插入 dom 中

适用页面嵌入 dom 业务,比如一个问卷弹窗等,类似 template.js、ejs 语法,不依赖 jq,zepto,vue,可直接插入 dom 中

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>js模板渲染函数</title>
  </head>
  <body>
    <script>
      // js模板渲染函数
      var _tpl = {
        /**
         * 转成可执行的js语句字符串
         * @param {string} content 模板
         * @param {any} data 数据
         * @returns 渲染后的字符串
         */
        render(content, data) {
          data = data || {};
          var list = ['var tpl = "";'];
          /*第二步*/
          var codeArr = this.transform(content); // 代码分割项数组
          /*第四步*/
          for (var i = 0, len = codeArr.length; i < len; i++) {
            var item = codeArr[i]; // 当前分割项
            if (item.type == 1) {
              // js逻辑
              list.push(item.txt);
            } else if (item.type == 2) {
              // js占位
              var txt = "tpl+=" + item.txt + ";";
              list.push(txt);
            } else {
              //文本 ,把文本中的",转义一下
              var txt = 'tpl+="' + item.txt.replace(/"/g, '\\"') + '";';
              list.push(txt);
            }
          }
          list.push("return tpl;");
          // list结构
          // var tpl = "";
          // tpl += "\n        ";
          // for (var i = 0; i < data.length; i++) {
          //   tpl += "\n          ";
          //   if (data[i].name !== "egg") {
          //     tpl += "\n          <h1>hello-->";
          //     tpl += data[i].name;
          //     tpl += "</h1>\n          ";
          //   }
          //   tpl += "\n        ";
          // }
          // tpl += "\n        ";
          // return tpl;
          return new Function("data", list.join("\n"))(data);
          // 最后转成dom
          // <h1>hello-->nodejs</h1>
          // <h1>hello-->koa</h1>
        },
        /**
         * 从原始模板中提取 文本/js 部分
         * @param {string} content
         * @returns {Array<{type:number,txt:string}>}
         */
        transform(content) {
          // <%for(var i =0;i<data.length;i++){%><%if(data[i].name!=='egg'){%><h1><%=data[i].name%></h1><%}%><%}%>
          // 匹配出来的(看fe助手)
          var arr = []; //返回的数组,用于保存匹配结果
          // \s 匹配任意空白符
          // \S 匹配任意空白符之外的任意字符
          // ? 匹配前面的子表达式0次或一次,
          // * 匹配前面的子表达式0次或多次,
          // []字符集合,匹配所包含的任意一个字符
          // . 是任意字符 可以匹配任何单个字符 , 比如,正则表达式r.t 可以匹配这些字符串:rat、rut、r t,但是不匹配root
          // .*? 表示匹配任意字符到下一个符合条件的字符,比如,正则表达式a.*?xxx 可以匹配 abxxx axxxxx abbbbb
          // .* 表示任何字符的0个或多个,具有贪婪性质,直到不能匹配为止
          // .? 表示任何字符的0个或1个
          // .* 会根据以后的正则表达式,进行回溯,也就是从头在匹配一遍,.*?,一个匹配后,继续往下,不会进行回溯,有最小匹配的性质,因为?是非贪婪匹配,
          // var reg = /<%([\s\S]*?)%>/g; //用于匹配js代码的正则
          var reg = /<%(.*?)%>/g;
          var match; //当前匹配到的match
          var nowIndex = 0; //当前匹配到的索引
          // match[1]是表达式或者占位符 for(var i =0;i<data.length;i++){ 或者 }
          // match不为null,为null就是文本
          // exec:方法在一个指定字符串中执行一个搜索匹配
          while ((match = reg.exec(content))) {
            // 保存当前匹配项之前的普通文本/占位
            this.appendTxt(arr, content.substring(nowIndex, match.index));
            //保存当前匹配项
            var item = {
              type: 1, // 类型  1 js逻辑 2:js 占位  null : 文本
              txt: match[1] // 内容
            };
            if (match[1].substr(0, 1) == "=") {
              // 如果是js占位
              item.type = 2;
              item.txt = item.txt.substr(1);
            }
            arr.push(item);
            //更新当前匹配索引
            nowIndex = match.index + match[0].length;
          }
          // arr结构
          // 0: {txt: "\n        "}
          // 1: {type: 1, txt: "for(var i =0;i<data.length;i++){"}
          // 2: {txt: "\n          "}
          // 3: {type: 1, txt: "if(data[i].name!=="egg"){"}
          // 4: {txt: "\n          <h1>hello-->"}
          // 5: {type: 2, txt: "data[i].name"}
          // 6: {txt: "</h1>\n          "}
          // 7: {type: 1, txt: "}"}
          // 8: {txt: "\n        "}
          // 9: {type: 1, txt: "}"}
          // 10: {txt: "\n        "}
          // 上面的代码,就是把表达式匹配出来,把文本匹配出来,用type字段表示类型
          //保存文本尾部,意思就是最后一个表达式结尾如果有文字,也要匹配进来
          this.appendTxt(arr, content.substr(nowIndex));
          return arr;
        },
        /**
         * 普通文本添加到数组,对换行部分进行转义
         * @param {Array<{type:number,txt:string}>} list
         * @param {string} content
         */
        appendTxt(list, content) {
          /*第三步*/
          // 把回车和换行都替换成换行
          content = content.replace(/\r?\n/g, "\\n");
          // 这些都是文本
          list.push({ txt: content });
        }
      };
      /*第一步*/
      var data = [{ name: "nodejs" }, { name: "koa" }, { name: "egg" }];
      var content = `
        <%for(var i =0;i<data.length;i++){%>
          <%if(data[i].name!=="egg"){%>
          <h1>hello--><%=data[i].name%></h1>
          <%}%>
        <%}%>
        `;
      var result = _tpl.render(content, data);
      var $wrap = document.createElement("div");
      $wrap.className = "container";
      $wrap.innerHTML = result;
      document.getElementsByTagName("body")[0].appendChild($wrap);
    </script>
  </body>
</html>

暂无评论