Templalienを実際に使ってみる

Templalien jQueryを使用したテンプレートエンジンもどき - 文殊堂を実際に使ってみます。

<html lang="ja">
<head>
<title>Templalien</title>
<link type="text/css" rel="stylesheet" media="screen, projection" href="http://assets2.twitter.com/stylesheets/screen.css"/>
<script src="http://jqueryjs.googlecode.com/files/jquery-1.2.6.js" type="text/javascript"></script>
<script type="text/javascript">
<!--
function Templalien(template){
  this._template = jQuery(template);
};
Templalien.prototype.merge = function(context){
  var template = this._template.clone();
  jQuery.each(context,function(selector,func){
      var elem = selector ? template.find(selector) : template;
      func(elem);
    });
  return template;
};
$(function(){
  // Templalienインスタンスを生成して時間を計る
  var initStarted = Date.now();
  var entry = new Templalien([
    '<tr class="hentry_hover">'
    ,'  <td class="thumb vcard author">'
    ,'     <a class="url"><img id="profile-image" class="photo fn"/></a>'
    ,'     <a class="anchor"></a>'
    ,'  </td>'
    ,'  <td class="content">'
    ,'    <strong><a></a></strong>'
    ,'    <span class="entry-content"></span>'
    ,'    <span class="meta entry-meta">'
    ,'      <a rel="bookmark" class="entry-date"><abbr class="published"></abbr></a>'
    ,'        from <a id="_twitter_client">web</a>'
    ,'    </span>'
    ,'  </td>'
    ,'  <td width="10" align="right">'
    ,'    <div class="status_actions" >'
    ,'      <a href="#">'
    ,'        <img border="0" title="Favorite this update" src="http://assets2.twitter.com/images/icon_star_empty.gif" id="status_star_809356512" alt="Icon_star_empty"/>'
    ,'      </a>'
    ,'      <a href="#">'
    ,'        <img border="0" title="reply to Hedachi" src="http://assets1.twitter.com/images/reply.png" alt="reply to Hedachi"/>'
    ,'      </a>'
    ,'    </div>'
    ,'  </td>'
    ,'</tr>'
    ].join('')
  );
  $('#log').append(document.createTextNode('init:' +(Date.now() - initStarted)));
  $('#log').append('<br>');
  // contextを作ってやる
  var record ={
    "status_id": "status_820087354", 
    "user_name": "monjudoh", 
    "nick_name": "文殊堂", 
    "profile_image": "http://s3.amazonaws.com/twitter_production/profile_images/51603464/purplenize_normal.png", 
    "entry_content": 
      [
        {"type": "text", "text": "@"},
        {"type": "reply", "user_name": "t_ishida"}, 
        {"type": "text", "text": "一応、この間こんなの作った。"}, 
        {"type": "link", "text": "http://d.hatena.ne.jp/monju...", "href": "http://d.hatena.ne.jp/monjudoh/20080526/1211797706"}, 
        {"type": "text", "text": ""}
      ],
    "published": new Date("Mon May 26 2008 19:29:42 GMT+0900"), 
    "twitter_client": false, 
    "reply-url": false
  }
  var use_name = record['user_name'];
  var userUrl = 'http://twitter.com/' + record['user_name'];
  var nick_name = record['nick_name'];
  var status_id = record['status_id'];
  var entry_id = status_id.replace('status_','');
  var entry_url = ['http://twitter.com/',use_name,'/statuses/',entry_id].join('');
  var published = record['published'];
  var entry_content = record['entry_content'];
  var profile_image = record['profile_image'];
  var twitter_client = record['twitter_client'];
  var context = {
    '':function(elem){elem.attr('id',status_id);}
    ,'.url':function(elem){elem.attr('href',userUrl);}
    ,'.anchor':function(elem){elem.attr('name',status_id);}
    ,'.url>img':function(elem){elem.attr('src',profile_image).attr('alt',nick_name);}
    ,'.content>strong>a':function(elem){elem.attr('href',userUrl).attr('title',nick_name).text(use_name);}
    ,'.entry-date':function(elem){elem.attr('href',entry_url);}
    ,'.published':function(elem){
      elem.text([published.getHours(),published.getMinutes(),published.getSeconds()].join(':'));
    }
    ,'.status_actions':function(elem){elem.attr('id','status_actions_'+entry_id);}
    ,'.entry-content':function(elem){
      $.each(entry_content,function(i,value){
        switch(value['type']){
          case 'text':
            elem.append( document.createTextNode(value['text']) );
            break;
          case 'reply':
            elem
              .append( $('<a/>').text(value['user_name']).attr('href','/'.concat(value['user_name'])) )
              .append( document.createTextNode(' ') );
            break;
          case 'link':
            elem
              .append( $('<a/>').text(value['href']).attr('href',value['href']) )
              .append( document.createTextNode(' ') );
            break;
        }
      });
    }
    ,'#_twitter_client':function(elem){
      if(twitter_client){
        elem
          .attr('href',twitter_client.url)
          .removeAttr('id')
          .text(twitter_client.name);
      }else{
        elem.replaceWith(elem.text());
      }
    }
  };
  // 表示確認
  $('#timeline').append(entry.merge(context));
  // 100回マージして時間を計る
  var mergeStarted = Date.now();
  for(var i = 0;i < 100;i++){
    entry.merge(context);
  }
  $('#log').append(document.createTextNode('merge*100:' +(Date.now() - mergeStarted)));
});
-->
</script>
</head>
<body>
<div id="content"><table cellspacing="0" id="timeline" class="doing"></table></div>
<div id="log"></div>

</body>
</html>

これを表示してみるとTwitterのエントリが1つ表示されます。
部分HTMLを渡して作ったテンプレートentryに、
entry.merge(context)で値を注入してエントリのDOMツリーを作っています。
インスタンス生成には16ms、merge100回には約3000msかかっています。

DOMツリー探索にかかる時間

  var context = {
    '':function(elem){}
    ,'.url':function(elem){}
    ,'.anchor':function(elem){}
    ,'.url>img':function(elem){}
    ,'.content>strong>a':function(elem){}
    ,'.entry-date':function(elem){}
    ,'.published':function(elem){}
    ,'.status_actions':function(elem){}
    ,'.entry-content':function(elem){}
    ,'#_twitter_client':function(elem){}
  };

このようにしてDOM操作の部分をごそっと消してみましたが、
それでも、merge100回には1200ms程度はかかっているので、
改善の余地がありそうです。

続く