Maison  >  Article  >  interface Web  >  Qu'est-ce qu'un modèle front-end ? Introduction aux principes et exemples de modèles front-end

Qu'est-ce qu'un modèle front-end ? Introduction aux principes et exemples de modèles front-end

不言
不言original
2018-09-04 10:35:226723parcourir

Qu'est-ce qu'un modèle front-end ? Comment implémenter un modèle front-end ? De nombreux amis ne savent peut-être pas grand-chose à ce sujet, c'est pourquoi l'article suivant vous présentera les principes des modèles front-end et des codes d'implémentation simples.

Le développement de modèles front-end

Le modèle peut être considéré comme l'un des outils les plus couramment utilisés dans le développement front-end. Extrayez le contenu fixe de la page dans un modèle, remplissez les données dynamiques renvoyées par le serveur dans les zones réservées du modèle, et enfin assemblez-le en une chaîne HTML de page complète et transmettez-la au navigateur pour analyse.

Les modèles peuvent grandement améliorer l'efficacité du développement. Sans modèles, les développeurs devront peut-être épeler manuellement les chaînes.

var tpl = &#39;<p>&#39; + user.name + &#39;</p>&#39;;
$(&#39;body&#39;).append(tpl);

Au cours du processus de développement front-end ces dernières années, les modèles ont également changé :

1. Modèle JSP de modèle php

Au début, il n'y avait pas de séparation entre le front-end et le back-end. Le front-end n'était qu'un dossier dans le projet back-end. Durant cette période, PHP et Java fournissaient leurs propres moteurs de modèles. Prenons l'exemple de JSP : les pages des applications Web Java sont généralement des fichiers .jsp. Le contenu de ce fichier est en grande partie constitué de HTML et certains modèles ont leur propre syntaxe. Il s'agit essentiellement de texte brut, mais ce n'est ni du HTML ni du Java.

Syntaxe JSP : index.jsp

<html>
<head><title>Hello World</title></head>
<body>
Hello World!<br/>
<%
out.println("Your IP address is " + request.getRemoteAddr());
%>
</body>
</html>

Le moteur de modèle de cette période utilisait souvent le serveur pour compiler la chaîne de modèle et générer la chaîne html pour le client .

2. Modèle universel de moustache de guidon

Node a été publié en 2009. JavaScript peut également implémenter des fonctions côté serveur, ce qui facilite également grandement les développeurs. La naissance des modèles de moustache et de guidon a facilité les développeurs front-end. Les deux modèles sont désormais implémentés en utilisant JavaScript, ce modèle front-end peut être exécuté côté serveur ou client , mais la plupart sont utilisés. scénarios All js est inséré dans le modèle en fonction des données obtenues de manière asynchrone à partir du serveur pour générer un nouveau dom pour insérer le numéro de page. C’est très bénéfique pour le développement front-end et back-end.

Syntaxe moustache : index.mustache

<p>Username: {{user.name}}</p>
{{#if (user.gender === 2)}}
    <p>女</p>
{{/if}}

3. Modèle en vue JSX dans React

Vient ensuite la nouvelle vie de nos jours. , la méthode d'écriture de modèles dans Vue est différente des modèles précédents et la fonction est plus puissante. Il peut être utilisé aussi bien sur le client que sur le serveur, mais les scénarios d'utilisation sont très différents : la page change souvent en fonction des données, et le DOM généré par le modèle change, ce qui nécessite des performances élevées pour le modèle.

syntaxe vue : index.vue

<p>Username: {{user.name}}</p>
<template v-if="user.gender === 2">
    <p>女</p>
</div>

Fonction implémentée par les templates

Que ce soit du JSP aux templates vue, les templates deviennent de plus en plus simples en syntaxe et plus fonctionnels. sont de plus en plus abondants, mais les fonctions de base sont indispensables :

  1. Sortie variable (échappée/non échappée) : Pour des raisons de sécurité, les modèles convertiront essentiellement les chaînes variables par défaut. Bien entendu, la sortie avec échappement implémente également la fonction de sortie sans échappement, utilisez-la donc avec prudence.

  2. Jugement conditionnel (si autre) : une fonction souvent nécessaire en développement.

  3. Variable de boucle : bouclez dans le tableau, générant de nombreux extraits de code répétés.

  4. Imbrication de modèles : avec l'imbrication de modèles, vous pouvez réduire beaucoup de code en double et les modèles imbriqués intègrent des étendues.

Les fonctions ci-dessus couvrent essentiellement les fonctions de base de la plupart des modèles. Pour ces fonctions de base, vous pouvez explorer comment le modèle est implémenté.

Principe de mise en œuvre des modèles

Comme le titre l'indique, les modèles sont essentiellement des chaînes de texte brut. Comment les chaînes fonctionnent-elles sur les programmes js ?

Utilisation du modèle :

var domString = template(templateString, data);

Le moteur de modèle obtient la chaîne du modèle et la portée du modèle, et génère une chaîne DOM complète après compilation.

La plupart des principes d'implémentation des modèles sont fondamentalement les mêmes :

La chaîne du modèle sépare d'abord la chaîne ordinaire et la chaîne de syntaxe du modèle par divers moyens pour générer un arbre de syntaxe abstrait AST, puis elle exécute le modèle ; fragments de syntaxe Lors de la compilation, les variables du modèle sont recherchées dans les variables entrées par le moteur ; le fragment de syntaxe du modèle génère un fragment HTML ordinaire, qui est épissé avec la chaîne ordinaire d'origine pour la sortie.

En fait, la logique de compilation des modèles n'est pas particulièrement compliquée. Quant aux modèles tels que Vue qui lient dynamiquement les données, vous pouvez vous référer au lien à la fin de l'article lorsque vous avez le temps.

Implémentez rapidement un modèle simple

Prenons maintenant le modèle de moustache comme exemple pour implémenter manuellement un modèle qui implémente des fonctions de base.

Modèle de chaîne de modèle : index.txt

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Page Title</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" type="text/css" media="screen" href="main.css" />
  <script src="main.js"></script>
</head>
<body>
  <h1>Panda模板编译</h1>
  <h2>普通变量输出</h2>
  <p>username: {{common.username}}</p>
  <p>escape:{{common.escape}}</p>
  <h2>不转义输出</h2>
  <p>unescape:{{&common.escape}}</p>
  <h2>列表输出:</h2>
  <ul>
  {{#each list}}
    <li class="{{value}}">{{key}}</li>
  {{/each}}
  </ul>
  <h2>条件输出:</h2>
  {{#if shouldEscape}}
    <p>escape{{common.escape}}</p>
  {{else}}
    <p>unescape:{{&common.escape}}</p>
  {{/if}}
</body>
</html>

Modèle de données correspondant :

module.exports = {
  common: {
    username: &#39;Aus&#39;,
    escape: &#39;<p>Aus</p>&#39;
  },
  shouldEscape: false,
  list: [
    {key: &#39;a&#39;, value: 1},
    {key: &#39;b&#39;, value: 2},
    {key: &#39;c&#39;, value: 3},
    {key: &#39;d&#39;, value: 4}
  ]
};

Comment utiliser le modèle :

var fs = require("fs");
var tpl = fs.readFileSync(&#39;./index.txt&#39;, &#39;utf8&#39;);
var state = require(&#39;./test&#39;);
var Panda = require(&#39;./panda&#39;);

Panda.render(tpl, state)

Ensuite, implémentez le modèle :

1. Chaîne coupée régulière

Une fois que le moteur de modèle a obtenu la chaîne modèle, il utilise généralement des chaînes coupées régulières pour distinguer celles qui sont des chaînes statiques et celles qui le sont. bloc de code qui doit être compilé pour générer un arbre de syntaxe abstraite (AST).

// 将未处理过的字符串进行分词,形成字符组tokens
Panda.prototype.parse = function (tpl) {
  var tokens = [];
  var tplStart = 0;
  var tagStart = 0;
  var tagEnd = 0;

  while (tagStart >= 0) {
    tagStart = tpl.indexOf(openTag, tplStart);
    if (tagStart < 0) break;
    // 纯文本
    tokens.push(new Token(&#39;text&#39;, tpl.slice(tplStart, tagStart)));

    tagEnd = tpl.indexOf(closeTag, tagStart) + 2;
    if (tagEnd < 0) throw new Error(&#39;{{}}标签未闭合&#39;);
    // 细分js

    var tplValue = tpl.slice(tagStart + 2, tagEnd - 2);
    var token = this.classifyJs(tplValue);
    tokens.push(token);

    tplStart = tagEnd;
  }

  // 最后一段
  tokens.push(new Token(&#39;text&#39;, tpl.slice(tagEnd, tpl.length)));

  return this.parseJs(tokens);
};

Cette étape de fractionnement des chaînes est généralement effectuée à l'aide d'expressions régulières. Les méthodes régulières seront largement utilisées lors de la récupération ultérieure des chaînes.

Dans cette étape, vous pouvez généralement vérifier les exceptions de fermeture des balises de modèle et signaler une erreur.

2. Classification de la syntaxe du modèle

Après avoir généré AST, les chaînes ordinaires n'ont plus besoin d'être gérées et seront finalement sorties directement, en se concentrant sur la classification de la syntaxe du modèle.

// 专门处理模板中的js
Panda.prototype.parseJs = function (tokens) {
  var sections = [];
  var nestedTokens = [];
  var conditionsArray = [];
  var collector = nestedTokens;
  var section;
  var currentCondition;

  for (var i = 0; i < tokens.length; i++) {
    var token = tokens[i];
    var value = token.value;
    var symbol = token.type;

    switch (symbol) {
      case &#39;#&#39;: {
        collector.push(token);
        sections.push(token);

        if(token.action === &#39;each&#39;){
          collector = token.children = [];
        } else if (token.action === &#39;if&#39;) {
          currentCondition = value;
          var conditionArray;
          collector = conditionArray = [];
          token.conditions = token.conditions || conditionsArray;

          conditionsArray.push({
            condition: currentCondition,
            collector: collector
          });
        }
        break;
      }
      case &#39;else&#39;: {
        if(sections.length === 0 || sections[sections.length - 1].action !== &#39;if&#39;) {
          throw new Error(&#39;else 使用错误&#39;);
        }

        currentCondition = value;
        collector = [];

        conditionsArray.push({
          condition: currentCondition,
          collector: collector
        });

        break;
      }
      case &#39;/&#39;: {
        section = sections.pop();

        if (section && section.action !== token.value) {
          throw new Error(&#39;指令标签未闭合&#39;);
        }

        if(sections.length > 0){
          var lastSection = sections[sections.length - 1];
          if(lastSection.action === &#39;each&#39;){
            collector = lastSection.chidlren;
          } else if (lastSection.action = &#39;if&#39;) {
            conditionsArray = [];
            collector = nestedTokens;
          }
        } else {
          collector = nestedTokens;
        }

        break;
      }
      default: {
        collector.push(token);
        break;
      }
    }
  }

  return nestedTokens;
}

Dans l'étape précédente, nous avons généré un AST. Cet AST est ici un tableau de jetons de segmentation de mots :

[
    Token {},
    Token {},
    Token {},
]

Ce jeton est chaque chaîne et le type de celui-ci. le jeton est enregistré séparément. L'action, le sous-jeton, le jeton de condition et d'autres informations.

/**
 * token类表示每个分词的标准数据结构
 */
function Token (type, value, action, children, conditions) {
  this.type = type;
  this.value = value;

  this.action = action;
  this.children = children;
  this.conditions = conditions;
}

在这一步要将循环方法中的子token嵌套到对应的token中,以及条件渲染子token嵌套到对应token中。

这步完成之后,一个标准的带有嵌套关系的AST完成了。

3. 变量查找与赋值

现在开始根据token中的变量查找到对应的值,根据相应功能生成值得字符串。

/**
 * 解析数据结构的类
 */
function Context (data, parentContext) {
  this.data = data;
  this.cache = { '.': this.data };
  this.parent = parentContext;
}

Context.prototype.push = function (data) {
  return new Context(data, this);
}

// 根据字符串name找到真实的变量值
Context.prototype.lookup = function lookup (name) {
  name = trim(name);

  var cache = this.cache;

  var value;
  // 查询过缓存
  if (cache.hasOwnProperty(name)) {
    value = cache[name];
  } else {
    var context = this, names, index, lookupHit = false;

    while (context) {
      // user.username
      if (name.indexOf('.') > 0) {
        value = context.data;
        names = name.split('.');
        index = 0;

        while (value != null && index < names.length) {
          if (index === names.length - 1) {
            lookupHit = hasProperty(value, names[index]);
          }

          value = value[names[index++]];
        }
      } else {
        value = context.data[name];
        lookupHit = hasProperty(context.data, name);
      }

      if (lookupHit) {
        break;
      }

      context = context.parent;
    }

    cache[name] = value;
  }

  return value;
}

为了提高查找效率,采用缓存代理,每次查找到的变量存储路径方便下次快速查找。

不同于JavaScript编译器,模板引擎在查找变量的时候找不到对应变量即终止查找,返回空并不会报错。

4. 节点的条件渲染与嵌套

这里开始讲模板语法token和普通字符串token开始统一编译生成字符串,并拼接成完整的字符串。

// 根据tokens和context混合拼接字符串输出结果
Panda.prototype.renderTokens = function (tokens, context) {
  var result = '';
  var token, symbol, value;

  for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
    value = undefined;
    token = tokens[i];
    symbol = token.type;

    if (symbol === '#') value = this.renderSection(token, context);
    else if (symbol === '&') value = this.unescapedValue(token, context);
    else if (symbol === '=') value = this.escapedValue(token, context);
    else if (symbol === 'text') value = this.rawValue(token);

    if (value !== undefined) result += value;
  }

  return result;
}

5. 绘制页面

页面字符串已经解析完成,可以直接输出:

Panda.prototype.render = function (tpl, state) {
  if (typeof tpl !== 'string') {
    return new Error('请输入字符串!');
  }

  // 解析字符串
  var tokens = this.cache[tpl] ? tokens : this.parse(tpl);
  // 解析数据结构
  var context = state instanceof Context ? state : new Context(state);
  // 渲染模板
  return this.renderTokens(tokens, context);
};

输出页面字符串被浏览器解析,就出现了页面。

以上只是简单的模板实现,并没有经过系统测试,仅供学习使用,源码传送门。成熟的模板引擎是有完整的异常处理,变量查找解析,作用域替换,优化渲染,断点调试等功能的。

总结

前端模板这块能做的东西还很多,很多框架都是集成模板的功能,配合css,js等混合编译生成解析好样式和绑定成功事件的dom。

另外实现模板的方式也有很多,本文的实现方式参考了mustache源码,模板标签内的代码被解析,但是是通过代码片段分类,变量查找的方式来执行的,将纯字符串的代码变成了被解释器执行的代码。

另外向vue这种可以实现双向绑定的模板可以抽空多看一看。

相关推荐:

javascript - 大家前端js模板是用underscore还是handlebars呢?

学习前端模板引擎 jade (一)_html/css_WEB-ITnose

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!

Déclaration:
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

Articles Liés

Voir plus