JavaScriptテンプレートエンジン実装の原理例の詳細

9127 ワード

この例では、JavaScriptテンプレートエンジンの実装原理について説明します.皆さんの参考にしてください.具体的には以下の通りです.
1、入門例
まず、簡単なテンプレートを見てみましょう.

 
  <h2>
   <a href="{{href}}" rel="external nofollow" >
    {{title}}
   </a>
  </h2>
  <img src="{{imgSrc}}" alt="{{title}}">
 

{{ xxx }}に含まれる変数は、私たちが置き換える変数です.
次にajaxまたは他の方法でデータを得ることができます.ここでは、以下のようにデータを定義します.

var data = [
  {
   title: "Create a Sticky Note Effect in 5 Easy Steps with CSS3 and HTML5",
   href: "http://net.tutsplus.com/tutorials/html-css-techniques/create-a-sticky-note-effect-in-5-easy-steps-with-css3-and-html5/",
   imgSrc: "https://d2o0t5hpnwv4c1.cloudfront.net/771_sticky/sticky_notes.jpg"
  },
  {
   title: "Nettuts+ Quiz #8",
   href: "http://net.tutsplus.com/articles/quizzes/nettuts-quiz-8-abbreviations-darth-sidious-edition/",
   imgSrc: "https://d2o0t5hpnwv4c1.cloudfront.net/989_quiz2jquerybasics/quiz.jpg"
  }
 ];


OK、今の問題は私たちがどのようにデータをテンプレートにインポートするかです.
1つ目は、replaceを使用して中の変数を直接置き換えることです.

template = document.querySelector('#template').innerHTML,
result = document.querySelector('.result'),
i = 0, len = data.length,
fragment = '';
for ( ; i < len; i++ ) {
  fragment += template
   .replace( /\{\{title\}\}/, data[i].title )
   .replace( /\{\{href\}\}/, data[i].href )
   .replace( /\{\{imgSrc\}\}/, data[i].imgSrc );
}
result.innerHTML = fragment;


2つ目は、1つ目に比べて柔軟で、正則置換を採用していますが、初級先端では、正則をよく把握していない人が多く、一般的には少ないです.具体的には、次のようになります.

template = document.querySelector('#template').innerHTML,
result = document.querySelector('.result'),
attachTemplateToData;
//           ,                    (          ,             )。
attachTemplateToData = function(template, data) {
    var i = 0,
      len = data.length,
      fragment = '';
    //             ,      
    function replace(obj) {
      var t, key, reg;
       
       //            ,      key      ,    
      for (key in obj) {
        reg = new RegExp('{{' + key + '}}', 'ig');
        t = (t || template).replace(reg, obj[key]);
      }
      return t;
    }
    for (; i < len; i++) {
      fragment += replace(data[i]);
    }
    return fragment;
  };
result.innerHTML = attachTemplateToData(template, data);


1つ目と比較すると、2つ目のコードは多く見えますが、機能はさらに強くなりました.1つ目は、変数名を書き直すたびに、変数名が多いと面倒になり、エラーが発生しやすいことです.2つ目はこれらの悩みがありません.
2、テンプレートエンジンに関する知識
上記の例を通じて、テンプレートエンジンについて初歩的な認識があるはずです.次に、関連知識を説明します.
2.1テンプレートの保存
テンプレートは通常textarea/inputなどのフォームコントロール、またはscriptなどのラベルに配置されます.例えば、上記の例では、scriptラベルに載せています.
2.2テンプレート取得
一般的にIDで取得されますが、document.getElementById("ID"):

//textarea input  value,     innerHTML
var html = /^(textarea|input)$/i.test(element.nodeName) ? element.value : element.innerHTML;

上は一般的なテンプレート取得方法で、textarea/inputラベルの下に置いてもscriptラベルの下に置いても取得できます.
2.3テンプレート関数
一般にtemplateFun("id", data);であり、idはテンプレート文字列を格納する要素idであり、dataはロードする必要があるデータである.
2.4テンプレート解析コンパイル
テンプレート解析とは,主にテンプレート中のJavaScript文とhtmlを分離し,コンパイルするとテンプレート文字列を最終テンプレートにコンパイルすることである.上記の例は比較的簡単で、テンプレートエンジンのコアにはまだ触れていません.
2.5テンプレート区切り文字
なお、テンプレートエンジンによって使用される区切り記号は異なる可能性がありますが、上記の例では{{}}を使用していますが、Jquery tmplでは使用されています.
3、jQuery tmpl実現原理解析
jQuery tmplはjQueryの作者が書いたもので、コードは短くて精悍です.全部で20行以上ですが、機能は私たちの上のものよりずっと強いです.まずソースコードを見てみましょう.

(function(){
 var cache = {};
 this.tmpl = function tmpl(str, data){
  var fn = !/\W/.test(str) ?
   cache[str] = cache[str] ||
    tmpl(document.getElementById(str).innerHTML) :
   new Function("obj",
    "var p=[],print=function(){p.push.apply(p,arguments);};" +
    "with(obj){p.push('" +
    str
     .replace(/[\r\t
]/g, " ") .split(")[^\t]*)'/g, "$1\r") .replace(/\t=(.*?)%>/g, "',$1,'") .split("\t").join("');") .split("%>").join("p.push('") .split("\r").join("\\'") + "');}return p.join('');"); return data ? fn( data ) : fn; }; })();

初めて見ると少しぼんやりしていて、全く理解できないコードではないでしょうか.大丈夫です.後でソースコードを説明します.まず使用するテンプレートを見てみましょう.

 
  • JavaScriptコードが混在しているため、このテンプレートは入門例のテンプレートよりも複雑であることがわかります.JavaScriptコードは含まれています.置換する変数は区切られています.
    次にコードにコメントします.しかし、注釈を見ても、すぐに理解できるとは限らない.一番いい方法は、自分で実際に操作することだ.
    
    //                 
    (function(){
     //     ,           ,   ,            
     var cache = {};
     // tmpl   this ,   this   window
     this.tmpl = function tmpl(str, data){
      //              ,          id       ,
      //    id  ,       ,        tmpl;
      //        ,   new Function()    
      var fn = !/\W/.test(str) ?
       cache[str] = cache[str] ||
        tmpl(document.getElementById(str).innerHTML) :
       new Function("obj",
         //           ,   +    
        "var p=[],print=function(){p.push.apply(p,arguments);};" +
        "with(obj){p.push('" +
        str
          //        \t
    \r .replace(/[\r\t
    ]/g, " ")              // \t .split(")[^\t]*)'/g, "$1\r")              // html ",xxx," , :\t=users[i].url%> ',users[i].url,'       // , .replace(/\t=(.*?)%>/g, "',$1,'")              // , JavaScript "\t", \t ');       // html p , javascript push 。       .split("\t").join("');")              // , JavaScript "%>", %> p.push('       // html ');, p.push(' html , JavaScript .split("%>").join("p.push('")       //       .split("\r").join("\\'")     // p 。 + "');}return p.join('');"); return data ? fn( data ) : fn; }; })();

    上記のコードでは、new Function の使用方法について説明します.new Function()に関数として文字列を渡すbodyは、JavaScript関数を構築する.プログラミングではあまり使われませんが、時には役に立つはずです.
    次はnew Functionの基本的な使い方です.
    
    //            body(   ),    string;
    //                   (  )
    var myFunction = new Function('users', 'salary', 'return users * salary');
    
    

    最後の文字列は次の形式です.
    
     var p = [],
      print = function() {
       p.push.apply(p, arguments);
      };
     with(obj) {
      p.push('   
    ');
    for (var i = 0; i < users.length; i++) {
    p.push('
  • ', users[i].name, '
  • ');
    }
    p.push(' ');
    }
    return p.join('');
    中のprint関数は私たちのテンプレートでは使用されていません.
    指摘したいのは、pushを採用する方法はIE 6-8のブラウザの下で+=の形式より速いが、現在のブラウザでは+=が文字列をつなぐ最も速い方法である.実測により、現代のブラウザでは+=が配列push法よりも速く、v 8エンジンでは+=方式が配列接合より4.7倍速いことが明らかになった.そのため、現在、javascriptエンジンの特性に基づいて2つの異なる文字列接合方式を採用しているより高度なテンプレートエンジンもあります.
    次のコードはテンセントのartTemplateから抜粋し、ブラウザのタイプに応じて異なる接続方法を選択します.機能が強いほど、考慮される問題も多くなります.
    
    var isNewEngine = ''.trim;// '__proto__' in {}
    var replaces = isNewEngine
    ? ["$out='';", "$out+=", ";", "$out"]
    : ["$out=[];", "$out.push(", ");", "$out.join('')"];
    
    

    挑戦:興味のあるのは+=を変えて上記のコードを実現することができます.
    まとめ
    テンプレートエンジンの原理をまとめると、htmlの対応するidを先に取得してinnerHTMLを取得し、スタートラベルとクローズラベルを利用して文字列を切り分けるのですが、実はテンプレートを2つの部分に分けて、一部はhtml部分で、一部は論理部分で、each、ifなどの特殊な記号を区別することで文字列を関数的な文字列につなぎます.2つの部分をそれぞれ処理した後、再びつなぎ合わせ、最後につなぎ合わせた文字列をnew Function()で必要な関数に変換します.
    現在、テンプレートエンジンの種類が多く、機能もますます強くなり、異なるテンプレート間の実現原理は大きく異なり、それぞれ優劣がありますので、必要に応じて選択してください.
    参考記事:
    1、Quick Tip: Create a Makeshift JavaScript Templating Solution
    2、JavaScriptテンプレートエンジンの応用シーン及び実現原理
    3、JavaScript独自のテンプレートエンジンを構築する
    JavaScriptに関する詳細は、「javascriptオブジェクト向け入門チュートリアル」、「JavaScript切り替え特効とテクニックまとめ」、「JavaScript検索アルゴリズムテクニックまとめ」、「JavaScriptエラーとデバッグテクニックまとめ」、「JavaScriptデータ構造とアルゴリズムテクニックまとめ」、『JavaScript遍歴アルゴリズムとテクニック総括』及び『JavaScript数学演算用法総括』
    JavaScriptプログラムの設計に役立つことを願っています.