Maison > interface Web > js tutoriel > le corps du texte

Une brève analyse des méthodes de mise en œuvre de base du moteur de modèle JavaScript_Connaissances de base

WBOY
Libérer: 2016-05-16 15:15:32
original
1136 Les gens l'ont consulté

Le modèle sépare les données et la présentation, ce qui facilite la maintenance de la logique et des effets de la présentation. À l’aide de l’objet Function de JavaScript, créez étape par étape un moteur de conversion de modèles extrêmement simple

Présentation du modèle
Le modèle fait généralement référence à du texte intégré à une sorte de code de langage de programmation dynamique. Les données et les modèles peuvent être combinés sous une certaine forme pour produire des résultats différents. Les modèles sont généralement utilisés pour définir le formulaire d'affichage, ce qui peut rendre la présentation des données plus riche et plus facile à maintenir. Par exemple, voici un exemple de modèle :

<ul>
 <% for(var i in items){ %>
 <li class='<%= items[i].status %>'><%= items[i].text %></li>
 <% } %>
</ul>
Copier après la connexion
Copier après la connexion

S'il existe les données des éléments suivants :

items:[
 { text: 'text1' ,status:'done' },
 { text: 'text2' ,status:'pending' },
 { text: 'text3' ,status:'pending' },
 { text: 'text4' ,status:'processing' }
]
Copier après la connexion

En le combinant d'une manière ou d'une autre, le code HTML suivant peut être généré :

<ul>
 <li class='done'>text1<li>
 <li class='pending'>text2<li>
 <li class='pending'>text3<li>
 <li class='processing'>text4<li>
</ul>
Copier après la connexion

Si vous souhaitez obtenir le même effet sans utiliser de modèle, c'est-à-dire afficher les données ci-dessus en conséquence, vous devez procéder comme suit :

var temp = '<ul>';
for(var i in items){
 temp += "<li class='" + items[i].status + "'>" + items[i].text + "</li>";
}
temp += '</ul>';
Copier après la connexion
Copier après la connexion

On peut constater que l'utilisation de modèles présente les avantages suivants :

Écriture HTML simplifiée
Ayez plus de contrôle sur la présentation des données grâce à des éléments de programmation (tels que des boucles et des branches conditionnelles)
Sépare les données et l'affichage, ce qui rend la logique d'affichage et les effets plus faciles à maintenir
Moteur de modèles
Un programme qui combine des données et des modèles pour générer le résultat final en analysant des modèles est appelé moteur de modèles. Il existe de nombreux types de modèles et de nombreux moteurs de modèles correspondants. Un modèle plus ancien s'appelle ERB, qui est utilisé dans de nombreux frameworks Web, tels que ASP.NET, Rails... L'exemple ci-dessus est un exemple d'ERB. Il existe deux concepts fondamentaux dans ERB : évaluer et interpoler. En apparence, évaluer fait référence à la partie contenue dans <% %> et interpoler fait référence à la partie contenue dans <%= %>. Du point de vue du moteur de modèle, la partie en évaluation ne sera pas directement sortie vers le résultat et est généralement utilisée pour le contrôle du processus tandis que la partie en interpolation sera directement sortie vers le résultat ;

Du point de vue de la mise en œuvre du moteur de modèles, il doit s'appuyer sur les fonctionnalités de compilation dynamique ou d'interprétation dynamique du langage de programmation pour simplifier la mise en œuvre et améliorer les performances. Par exemple : ASP.NET utilise la compilation dynamique de .NET pour compiler des modèles en classes dynamiques et utilise la réflexion pour exécuter dynamiquement le code dans la classe. Cette implémentation est en réalité plus compliquée car C# est un langage de programmation statique, mais en utilisant JavaScript, vous pouvez utiliser Function pour implémenter un moteur de modèle simple avec très peu de code. Cet article implémentera un moteur de modèle ERB simple pour montrer la puissance de JavaScript.

Conversion de texte de modèle
Pour l'exemple ci-dessus, examinez la différence entre utiliser et ne pas utiliser de modèles :

Rédaction du modèle :

<ul>
 <% for(var i in items){ %>
 <li class='<%= items[i].status %>'><%= items[i].text %></li>
 <% } %>
</ul>
Copier après la connexion
Copier après la connexion

Écriture sans modèle :

var temp = '<ul>';
for(var i in items){
 temp += "<li class='" + items[i].status + "'>" + items[i].text + "</li>";
}
temp += '</ul>';
Copier après la connexion
Copier après la connexion

En y regardant bien, les deux méthodes sont en réalité très "similaires" et se retrouvent dans un certain sens de correspondance biunivoque. Si le texte du modèle peut être transformé en code à exécuter, la conversion du modèle peut alors être réalisée. Il y a deux principes dans le processus de conversion :

Lorsque vous rencontrez du texte ordinaire, il est directement concaténé en chaînes
Lors d'une interpolation (c'est-à-dire <%= %>), le contenu est traité comme une variable et épissé dans la chaîne
Lorsqu'il rencontre une évaluation (c'est-à-dire <% %>), il est directement traité comme du code
Transformez l'exemple ci-dessus selon les principes ci-dessus et ajoutez une fonction générale :

var template = function(items){
 var temp = '';
 //开始变换
 temp += '<ul>';
 for(var i in items){
 temp += "<li class='" + items[i].status + "'>" + items[i].text + "</li>";
 }
 temp += '</ul>';
}
Copier après la connexion

Enfin, exécutez cette fonction et transmettez les paramètres de données :

var result = template(items);
Copier après la connexion

Fonction dynamique javascript
On peut voir que la logique de conversion ci-dessus est en fait très simple, mais le problème clé est que le modèle change, ce qui signifie que le code du programme généré doit également être généré et exécuté au moment de l'exécution. Heureusement, JavaScript possède de nombreuses fonctionnalités dynamiques, dont Function. Nous utilisons généralement le mot-clé function pour déclarer des fonctions dans js, et Function est rarement utilisé. En js, function est une syntaxe littérale. Le runtime de js convertira la fonction littérale en un objet Function, donc Function fournit en fait un mécanisme plus bas et plus flexible.

La syntaxe pour créer directement une fonction à l'aide de la classe Function est la suivante :

var function_name = new Function(arg1, arg2, ..., argN, function_body)
Copier après la connexion

Par exemple :

//创建动态函数 
var sayHi = new Function("sName", "sMessage", "alert(\"Hello \" + sName + sMessage);");
//执行 
sayHi('Hello','World');
Copier après la connexion

Le corps de la fonction et les paramètres peuvent être créés via des chaînes ! Tellement cool ! Grâce à cette fonctionnalité, le texte du modèle peut être converti en une chaîne de corps de fonction, afin que des fonctions dynamiques puissent être créées et appelées dynamiquement.

Idées de mise en œuvre
Utilisez d'abord des expressions régulières pour décrire l'interpolation et l'évaluation, et les parenthèses sont utilisées pour la capture de groupe :

var interpolate_reg = /<%=([\s\S]+&#63;)%>/g;
var evaluate_reg = /<%([\s\S]+&#63;)%>/g;
Copier après la connexion

Afin de faire correspondre en permanence l'ensemble du modèle, ces deux expressions régulières sont fusionnées, mais notez que toutes les chaînes qui peuvent correspondre à l'interpolation peuvent correspondre à l'évaluation, l'interpolation doit donc avoir une priorité plus élevée :

var matcher = /<%=([\s\S]+&#63;)%>|<%([\s\S]+&#63;)%>/g
Copier après la connexion

Concevez une fonction de conversion de modèles, les paramètres d'entrée sont des chaînes de texte de modèle et des objets de données

var matcher = /<%=([\s\S]+&#63;)%>|<%([\s\S]+&#63;)%>/g
//text: 传入的模板文本字串
//data: 数据对象
var template = function(text,data){ ... }
Copier après la connexion

使用replace方法,进行正则的匹配和“替换”,实际上我们的目的不是要替换interpolate或evaluate,而是在匹配的过程中构建出“方法体”:

var matcher = /<%=([\s\S]+&#63;)%>|<%([\s\S]+&#63;)%>/g
//text: 传入的模板文本字串
//data: 数据对象
var template = function(text,data){
 var index = 0;//记录当前扫描到哪里了
 var function_body = "var temp = '';";
 function_body += "temp += '";
 text.replace(matcher,function(match,interpolate,evaluate,offset){
 //找到第一个匹配后,将前面部分作为普通字符串拼接的表达式
 function_body += text.slice(index,offset);
 
 //如果是<% ... %>直接作为代码片段,evaluate就是捕获的分组
 if(evaluate){
  function_body += "';" + evaluate + "temp += '";
 }
 //如果是<%= ... %>拼接字符串,interpolate就是捕获的分组
 if(interpolate){
  function_body += "' + " + interpolate + " + '";
 }
 //递增index,跳过evaluate或者interpolate
 index = offset + match.length;
 //这里的return没有什么意义,因为关键不是替换text,而是构建function_body
 return match;
 });
 //最后的代码应该是返回temp
 function_body += "';return temp;";
}
Copier après la connexion

至此,function_body虽然是个字符串,但里面的内容实际上是一段函数代码,可以用这个变量来动态创建一个函数对象,并通过data参数调用:

var render = new Function('obj', function_body);
return render(data);
Copier après la connexion

这样render就是一个方法,可以调用,方法内部的代码由模板的内容构造,但是大致的框架应该是这样的:

function render(obj){
 var temp = '';
 temp += ...
 ...
 return temp;
}
Copier après la connexion

注意到,方法的形参是obj,所以模板内部引用的变量应该是obj:

<script id='template' type='javascript/template'>
 <ul>
 <% for(var i in obj){ %>
  <li class="<%= obj[i].status %>"><%= obj[i].text %></li>
 <% } %>
 </ul>
</script>
Copier après la connexion

看似到这里就OK了,但是有个必须解决的问题。模板文本中可能包含\r \n \u2028 \u2029等字符,这些字符如果出现在代码中,会出错,比如下面的代码是错误的:

temp += '
 <ul>
 ' + ... ;
Copier après la connexion

我们希望看到的应该是这样的代码:

temp += '\n \t\t<ul>\n' + ...;
Copier après la connexion

这样需要把\n前面的转义成\即可,最终变成字面的\\n。

另外,还有一个问题是,上面的代码无法将最后一个evaluate或者interpolate后面的部分拼接进来,解决这个问题的办法也很简单,只需要在正则式中添加一个行尾的匹配即可:

var matcher = /<%=([\s\S]+&#63;)%>|<%([\s\S]+&#63;)%>|$/g;
Copier après la connexion

相对完整的代码

var matcher = /<%=([\s\S]+&#63;)%>|<%([\s\S]+&#63;)%>|$/g


//模板文本中的特殊字符转义处理
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
var escapes = {
  "'":   "'",
  '\\':   '\\',
  '\r':   'r',
  '\n':   'n',
  '\t':   't',
  '\u2028': 'u2028',
  '\u2029': 'u2029'
 };

//text: 传入的模板文本字串
//data: 数据对象
var template = function(text,data){
 var index = 0;//记录当前扫描到哪里了
 var function_body = "var temp = '';";
 function_body += "temp += '";
 text.replace(matcher,function(match,interpolate,evaluate,offset){
 //找到第一个匹配后,将前面部分作为普通字符串拼接的表达式
 //添加了处理转义字符
 function_body += text.slice(index,offset)
  .replace(escaper, function(match) { return '\\' + escapes[match]; });

 //如果是<% ... %>直接作为代码片段,evaluate就是捕获的分组
 if(evaluate){
  function_body += "';" + evaluate + "temp += '";
 }
 //如果是<%= ... %>拼接字符串,interpolate就是捕获的分组
 if(interpolate){
  function_body += "' + " + interpolate + " + '";
 }
 //递增index,跳过evaluate或者interpolate
 index = offset + match.length;
 //这里的return没有什么意义,因为关键不是替换text,而是构建function_body
 return match;
 });
 //最后的代码应该是返回temp
 function_body += "';return temp;";
 var render = new Function('obj', function_body);
 return render(data);
}

Copier après la connexion

调用代码可以是这样:

<script id='template' type='javascript/template'>
 <ul>
 <% for(var i in obj){ %>
  <li class="<%= obj[i].status %>"><%= obj[i].text %></li>
 <% } %>
 </ul>
</script>

...

var text = document.getElementById('template').innerHTML;
var items = [
 { text: 'text1' ,status:'done' },
 { text: 'text2' ,status:'pending' },
 { text: 'text3' ,status:'pending' },
 { text: 'text4' ,status:'processing' }
];
console.log(template(text,items));

Copier après la connexion

可见,我们只用了很少的代码就实现了一个简易的模板。

遗留的问题
还有几个细节的问题需要注意:

  • 因为<%或者%>都是模板的边界字符,如果模板需要输出<%或者%>,那么需要设计转义的办法
  • 如果数据对象中包含有null,显然不希望最后输出'null',所以需要在function_body的代码中考虑null的情况
  • 在模板中每次使用obj的形参引用数据,可能不太方便,可以在function_body添加with(obj||{}){...},这样模板中可以直接使用obj的属性
  • 可以设计将render返回出去,而不是返回转化的结果,这样外部可以缓存生成的函数,以提高性能
É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