Maison > interface Web > js tutoriel > Méthode d'écriture du moteur de modèle JS simple

Méthode d'écriture du moteur de modèle JS simple

一个新手
Libérer: 2017-10-20 10:30:03
original
2459 Les gens l'ont consulté

Front

Il existe de très nombreux moteurs de modèles js. J'utilisais souvent art-template, et parfois j'utilise aussi vue comme moteur de modèles.

Jusqu'à...

Au début de l'année, j'étais encore dans l'équipe de projet précédente. A cette époque, la spécification du code était Vous ne pouvez pas utiliser [code externe]. sans autorisation, embarrassant.

S'il y a un besoin, écrivez-le, mais plus tard, il n'a pas été utilisé pour certaines raisons. Plus tard, nous avons divisé la chaîne de production et construit moi-même un ensemble de builds. Après l'avoir utilisé pendant quelques mois, je me suis senti très à l'aise, j'ai réécrit ce petit morceau de code selon des normes plus populaires et je l'ai partagé avec tout le monde.

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

Grammar

La première est de choisir la syntaxe du modèle, la syntaxe ejs est le premier choix, car le le public n'a pas besoin de l'apprendre. Des choses comme les moteurs de modèles de directives.

Si vous avez écrit jsp ou asp/asp.net, vous pouvez commencer directement.

Comment l'utiliser ?

Je veux l'utiliser comme ça

<body>
        <p id="root"></p>
        <script id="tplContent" type="text/html">
        <ul>
            <% for(var i=0; i<data.length; i++){
                var item = data[i];
                if(item.age < 30){%>
                    <li>我的名字是<%=item.name%>,我的年龄是<%=item.age%></li>
                <%}else{%>
                    <li>my name is <%=item.name%>,my age is a sercet.</li>
                <%}%>
            <% } %>
        </ul>
        </script>
        <script src="../build/mini-tpl.min.js"></script>
        <script>
            var data = [{ name: &#39;tom&#39;, age: 12 }, { name: &#39;lily&#39;, age: 24 }, { name: &#39;lucy&#39;, age: 55 }];
            var content = document.getElementById(&#39;tplContent&#39;).innerHTML;
            var result = miniTpl(content, data);
            document.getElementById(&#39;root&#39;).innerHTML = result;
        </script>
    </body>
Copier après la connexion

Si vous souhaitez l'utiliser comme ça, analysons comment y parvenir.

nouvelle fonction

    1 const content = &#39;console.log("hello world");&#39;;    
    2 
    3 let func = new Function(content);    
    4 
    5 func(); // hello world
Copier après la connexion
new Function ([arg1[, arg2[, ...argN]],] functionBody)
Copier après la connexion

functionBody Une JavaScript语句的<strong>字符串</strong>chaîne

JavaScript qui contient la fonction définition >. <p class="panel panel-default" style="padding: 20px;"><br/>Les fonctions générées à l'aide du constructeur Function ne créent pas de fermetures dans le contexte dans lequel elles sont créées ; elles sont généralement créées dans la portée globale. </p>Lorsque ces fonctions sont exécutées, elles ne peuvent accéder qu'à leurs propres variables locales et variables globales, et ne peuvent pas accéder à la portée du contexte généré par le constructeur de fonction appelé. (MDN)<p></p>En d'autres termes : <ol class=" list-paddingleft-2"><li><p></p>Vous pouvez utiliser new Function pour créer dynamiquement une fonction afin d'exécuter une instruction js de définition de fonction générée dynamiquement. </li><li><p></p>La fonction générée par new Function a une portée globale. </li><li><p><code>把变量放到全局(扯淡) Il existe ensuite trois types de paramètres : 函数传参, 用call/apply把值传给函数的this,

.

Au début, j'utilisais call pour transmettre la valeur, mais maintenant que j'y pense, ce n'était pas très élégant, alors j'ai changé pour la transmettre par paramètres. Ça y est :
const content = &#39;console.log(data);&#39;;
    
    let func = new Function(&#39;data&#39;, content);
    
    func(&#39;hello world&#39;); // hello world
Copier après la connexion

Ça y est, le prototype est là. Décomposons-le ci-dessous.

Modèle divisé

Regardez d'abord le modèle :
<% for(var i=0; i<data.length; i++){        
var item = data[i];        
if(item.age < 30){%>
            <li>我的名字是<%=item.name%>,我的年龄是<%=item.age%></li>
        <%}else{%>
            <li>my name is <%=item.name%>,my age is a sercet.</li>
        <%}%>
    <% } %>
Copier après la connexion

partie logique js <%%>, enveloppée par , variable js L'espace réservé <%= %> est enveloppé par , et le reste est la partie chaîne HTML

ordinaire à épisser.

En d'autres termes, il existe trois types de pièces qu'il faut trouver à l'aide d'expressions régulières :
  1. <%%>

    Le contenu js de la partie logique
  2. <%=%>

    Le contenu js de la partie espace réservé
  3. 纯文本Autre

    contenu

js部分Le deuxième item, js placeholder La partie appartient également au texte épissé. Pour qu'ils puissent être assemblés, c'est-à-dire 拼接部分,

.

Extraction régulière

Bien sûr, choisissez les expressions régulières !

Ici, je vais d'abord développer le contenu des pseudo-tableaux et la façon dont la console du navigateur traite les pseudo-tableaux :

Sans aller trop loin, disons juste la conclusion :

Tant qu'il y a un attribut length de type int, il y a est un attribut function splice

de type. Le navigateur pensera alors qu'il s'agit d'un tableau.

Si les autres propriétés à l'intérieur sont triées par index, elles peuvent même être affichées dans la console comme les éléments d'un tableau.

Cette méthode de jugement s'appelle typage du canard

Si quelque chose ressemble à un canard et cancane comme un canard, alors c'est un canard 0_o

Retour au texte, ceci. nécessite d'extraire plusieurs fois la partie logique js et le texte du modèle.

Pour chaque extraction, le contenu extrait doit être obtenu. Cette fois, le dernier élément d'index est mis en correspondance (utilisé pour lever le contenu du texte). J'ai donc choisi RegExp.prototype.exec .

Par exemple, RegExp.prototype.exec renvoie une collection (pseudo-tableau), et son type est le suivant :
属性/索引描述
[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 });
    }
Copier après la connexion

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

    /**
     * 模板 + 数据 =》 渲染后的字符串
     * 
     * @param {string} content 模板
     * @param {any} data 数据
     * @returns 渲染后的字符串
     */
    function render(content, data) {
        data = data || {};
        var list = [&#39;var tpl = "";&#39;];
        var codeArr = transform(content);  // 代码分割项数组

        for (var i = 0, len = codeArr.length; i < len; i++) {
            var item = codeArr[i]; // 当前分割项

            // 如果是文本类型,或者js占位项
            if (!item.type) {
                var txt = &#39;tpl+="&#39; +
                    item.txt.replace(/<%=(.*?)%>/g, function (g0, g1) {
                        return &#39;"+&#39; + g1 + &#39;+"&#39;;
                    }) + &#39;"&#39;;
                list.push(txt);
            }
            else {  // 如果是js代码
                list.push(item.txt);
            }
        }
        list.push(&#39;return tpl;&#39;);

        return new Function(&#39;data&#39;, list.join(&#39;\n&#39;))(data);
    }
Copier après la connexion

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

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal