0

0

简易js模板引擎写法

一个新手

一个新手

发布时间:2017-10-20 10:30:03

|

2921人浏览过

|

来源于php中文网

原创

前面

js 模板引擎有很多很多,我以前经常用 art-template ,有时候也会拿 vue 来当模板引擎用。

直到......

年初的时候,我还在上个项目组,那时候代码规范是未经允许不能使用 【外部代码】,囧 。

有了需求,那么就去写吧,但是后来因为一些原因没用上。后来分了产线,自己搭了一套构建,用了几个月感觉挺爽,把这小段代码按照比较大众的规范重写,跟大家分享下。

 https://github.com/shalldie/mini-tpl

语法

首先是选择模板语法,ejs语法是首选,因为大众,更无需去学习指令型模板引擎的那些东西。

如果写过 jsp 或者 asp/asp.net 的可以直接上手。

怎么用它?

我要这么用


        

想要这么用,那么就分析一下怎么才能实现。

new Function

    1 const content = 'console.log("hello world");';    
    2 
    3 let func = new Function(content);    
    4 
    5 func(); // hello world
new Function ([arg1[, arg2[, ...argN]],] functionBody)

functionBody  一个含有包括函数定义的JavaScript语句的字符串

使用Function构造器生成的函数,并不会在创建它们的上下文中创建闭包;它们一般在全局作用域中被创建。
当运行这些函数的时候,它们只能访问自己的本地变量和全局变量,不能访问Function构造器被调用生成的上下文的作用域。(MDN)

也就是说:

  1. 可以用 new Function 来动态的创建一个函数,去执行某动态生成的函数定义js语句。

  2. 通过 new Function 生成的函数,作用域在全局。

  3. 那么传参有3种:把变量放到全局(扯淡)函数传参用call/apply把值传给函数的this

最初我用的是 call 来传值,如今想了想不太优雅,换成了用参数传递。也就是这样:

const content = 'console.log(data);';
    
    let func = new Function('data', content);
    
    func('hello world'); // hello world

到此为止,雏形有了。下面来拆分。

模板拆分

先看模板:

<% for(var i=0; i
            
  • 我的名字是<%=item.name%>,我的年龄是<%=item.age%>
  • <%}else{%>
  • my name is <%=item.name%>,my age is a sercet.
  • <%}%> <% } %>

     js 逻辑部分,由 包裹, js 变量的占位,由 包裹,剩下的是普通的要拼接的html字符串部分。

    也就是说,需要用正则找出的部分有3种:

    1. 逻辑部分的js内容

    2. 占位部分的js内容

      网奇企业网站管理系统CWMS2.0 英文版
      网奇企业网站管理系统CWMS2.0 英文版

      CWMS 2.0功能介绍:一、 员工考勤系统,国内首创CWMS2.0的企业员工在线考勤系统。二、 自定义URL Rewrite重写,友好的搜索引擎 URL优化。三、 代码与模板分离技术,支持超过5种类型的模板类型。包括:文章、图文、产品、单页、留言板。四、 购物车功能,CWMS2.0集成国内主流支付接口。如:淘宝、易趣、快钱等。完全可媲美专业网上商城系统。五、 多语言自动切换 中英文的说明。六、

      下载
    3. 其它的纯文本内容

    其中第2项,js占位的部分,也属于拼接文本。所以可以放在一起,就是 js部分拼接部分

    正则提取

    当然是选择正则表达式啊!

    这里先跟大家扩展一下关于伪数组方面的内容,以及浏览器的控制台如何看待伪数组:

    不扯远,直接说结论:

    只要有 int类型的 length属性,有 function类型 的 splice属性。 那么浏览器就会认为他是一个数组。

    如果里面的其它属性按照索引来排序,甚至还可以像数组里面的项那样在控制台展示出来。

     这种判断方式叫 duck typing ,如果一个东西长得像鸭子,而且叫起来像鸭子,,,那么它就是鸭子  0_o

    回到正文,这个需要多次从模板中,把 js逻辑部分 和 文本 依次提取出来。

    对于每一次提取,都要获取提取出的内容,本次匹配最后的索引项(用于提起文本内容)。所以我选择了 RegExp.prototype.exec 。

    举个例子,RegExp.prototype.exec 返回的是一个集合(伪数组),它的类型是这样的:

    属性/索引 描述
    [0] 匹配的全部字符串
    [1],...[n] 括号中的分组捕获
    index 匹配到的字符位于原始字符串的基于0的索引值
    input 原始字符串

    通过这样,就可以拿到匹配到的 js 逻辑部分,并通过 index 和本次匹配到的内容,来获取每个js逻辑部分之间的文本内容项。

    要注意,在全局匹配模式下,正则表达式会接着上次匹配的结果继续匹配新的字符串。

        /**
         * 从原始模板中提取 文本/js 部分
         * 
         * @param {string} content 
         * @returns {Array<{type:number,txt:string}>} 
         */
        function transform(content) {
            var arr = [];                 //返回的数组,用于保存匹配结果
            var reg = /<%(?!=)([\s\S]*?)%>/g;  //用于匹配js代码的正则
            var match;   				  //当前匹配到的match
            var nowIndex = 0;			  //当前匹配到的索引        
    
            while (match = reg.exec(content)) {
                // 保存当前匹配项之前的普通文本/占位
                appendTxt(arr, content.substring(nowIndex, match.index));
                //保存当前匹配项
                arr.push({
                    type: 1,  //js代码
                    txt: match[1]  //匹配到的内容
                });
                //更新当前匹配索引
                nowIndex = match.index + match[0].length;
            }
            //保存文本尾部
            appendTxt(arr, content.substr(nowIndex));
            return arr;
        }
    
        /**
         * 普通文本添加到数组,对换行部分进行转义
         * 
         * @param {Array<{type:number,txt:string}>} list 
         * @param {string} content 
         */
        function appendTxt(list, content) {
            content = content.replace(/\r?\n/g, "\\n");
            list.push({ txt: content });
        }

    得到了js逻辑项 和 文本内容 ,就可以把他们拼在一起,来动态生成一个function。要注意的是,文本内容中,包含 js占位项,这个地方要转换一下。

        /**
         * 模板 + 数据 =》 渲染后的字符串
         * 
         * @param {string} content 模板
         * @param {any} data 数据
         * @returns 渲染后的字符串
         */
        function render(content, data) {
            data = data || {};
            var list = ['var tpl = "";'];
            var codeArr = transform(content);  // 代码分割项数组
    
            for (var i = 0, len = codeArr.length; i < len; i++) {
                var item = codeArr[i]; // 当前分割项
    
                // 如果是文本类型,或者js占位项
                if (!item.type) {
                    var txt = 'tpl+="' +
                        item.txt.replace(/<%=(.*?)%>/g, function (g0, g1) {
                            return '"+' + g1 + '+"';
                        }) + '"';
                    list.push(txt);
                }
                else {  // 如果是js代码
                    list.push(item.txt);
                }
            }
            list.push('return tpl;');
    
            return new Function('data', list.join('\n'))(data);
        }

    这样就完成了简易的模板引擎,不要觉得拼字符串慢。

    在现代浏览器(IE8开始)中,特地对字符串的操作做了大量的优化,用 += 拼字符串,要比用数组 push 再 join 的方式快很多很多,即使放到IE7(IE6不清楚)中,我这里测试也是拼字符串快。。。

    热门AI工具

    更多
    DeepSeek
    DeepSeek

    幻方量化公司旗下的开源大模型平台

    豆包大模型
    豆包大模型

    字节跳动自主研发的一系列大型语言模型

    通义千问
    通义千问

    阿里巴巴推出的全能AI助手

    腾讯元宝
    腾讯元宝

    腾讯混元平台推出的AI助手

    文心一言
    文心一言

    文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

    讯飞写作
    讯飞写作

    基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

    即梦AI
    即梦AI

    一站式AI创作平台,免费AI图片和视频生成。

    ChatGPT
    ChatGPT

    最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

    相关专题

    更多
    俄罗斯Yandex引擎入口
    俄罗斯Yandex引擎入口

    2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

    178

    2026.01.28

    包子漫画在线官方入口大全
    包子漫画在线官方入口大全

    本合集汇总了包子漫画2026最新官方在线观看入口,涵盖备用域名、正版无广告链接及多端适配地址,助你畅享12700+高清漫画资源。阅读专题下面的文章了解更多详细内容。

    35

    2026.01.28

    ao3中文版官网地址大全
    ao3中文版官网地址大全

    AO3最新中文版官网入口合集,汇总2026年主站及国内优化镜像链接,支持简体中文界面、无广告阅读与多设备同步。阅读专题下面的文章了解更多详细内容。

    79

    2026.01.28

    php怎么写接口教程
    php怎么写接口教程

    本合集涵盖PHP接口开发基础、RESTful API设计、数据交互与安全处理等实用教程,助你快速掌握PHP接口编写技巧。阅读专题下面的文章了解更多详细内容。

    2

    2026.01.28

    php中文乱码如何解决
    php中文乱码如何解决

    本文整理了php中文乱码如何解决及解决方法,阅读节专题下面的文章了解更多详细内容。

    4

    2026.01.28

    Java 消息队列与异步架构实战
    Java 消息队列与异步架构实战

    本专题系统讲解 Java 在消息队列与异步系统架构中的核心应用,涵盖消息队列基本原理、Kafka 与 RabbitMQ 的使用场景对比、生产者与消费者模型、消息可靠性与顺序性保障、重复消费与幂等处理,以及在高并发系统中的异步解耦设计。通过实战案例,帮助学习者掌握 使用 Java 构建高吞吐、高可靠异步消息系统的完整思路。

    8

    2026.01.28

    Python 自然语言处理(NLP)基础与实战
    Python 自然语言处理(NLP)基础与实战

    本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

    24

    2026.01.27

    拼多多赚钱的5种方法 拼多多赚钱的5种方法
    拼多多赚钱的5种方法 拼多多赚钱的5种方法

    在拼多多上赚钱主要可以通过无货源模式一件代发、精细化运营特色店铺、参与官方高流量活动、利用拼团机制社交裂变,以及成为多多进宝推广员这5种方法实现。核心策略在于通过低成本、高效率的供应链管理与营销,利用平台社交电商红利实现盈利。

    122

    2026.01.26

    edge浏览器怎样设置主页 edge浏览器自定义设置教程
    edge浏览器怎样设置主页 edge浏览器自定义设置教程

    在Edge浏览器中设置主页,请依次点击右上角“...”图标 > 设置 > 开始、主页和新建标签页。在“Microsoft Edge 启动时”选择“打开以下页面”,点击“添加新页面”并输入网址。若要使用主页按钮,需在“外观”设置中开启“显示主页按钮”并设定网址。

    72

    2026.01.26

    热门下载

    更多
    网站特效
    /
    网站源码
    /
    网站素材
    /
    前端模板

    精品课程

    更多
    相关推荐
    /
    热门推荐
    /
    最新课程
    React 教程
    React 教程

    共58课时 | 4.2万人学习

    TypeScript 教程
    TypeScript 教程

    共19课时 | 2.5万人学习

    Bootstrap 5教程
    Bootstrap 5教程

    共46课时 | 3万人学习

    关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
    php中文网:公益在线php培训,帮助PHP学习者快速成长!
    关注服务号 技术交流群
    PHP中文网订阅号
    每天精选资源文章推送

    Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号