TypeScriptとの類似性テンプレートエンジンを作成する

ごく最近、Microsoftは新しいTypeScriptプログラミング言語を導入しました。 確かに多くの人が、Microsoft Visual Studioやその他のエディター用のタイピングとプラグインの存在を気に入りました。 開発中の言語の有用性を評価するために、アプリケーションの開発に役立つ小さなコードを作成して、この言語をいじることに決めました。



誰もがこれに出くわした



HTML + jsテクノロジーを使用してアプリケーションを開発した人は誰でも、ビュー上のテンプレートデータの問題を解決する必要があることを知っています。 多くの解決策があります:フレームワーク(1、2、3、4など)を使用し、次のような単純なテクニックを使用します。



var StringTemplate = "<div class=\"{Flag}\" >" + "<p class=\"name\" >{Name}</p>" + "<p class=\"message\" >{Text}</p>" + "<p class=\"date\" >{Date}</p>" + "</div>", html = "", Container = document.getElementById('container2'); for (var i = 0; i < TestDataLength; i++) { html += StringTemplate .replace(/\{Flag\}/g, TestData[i].Out ? "message out" : "message") .replace(/\{Name\}/g, TestData[i].Name || "") .replace(/\{Text\}/g, TestData[i].Text || "") .replace(/\{Date\}/g, TestData[i].Date.toLocaleTimeString()); } Container.innerHTML = html;
      
      





私の意見では、後者の方法は最も明白で単純ですが、形成後に表現を変更する必要性を考慮していません。 変更する各要素にidまたはdata-idを追加し、jQueryまたはquerySelectorを使用して必要な要素を選択できますが、DOMの検索は非常にリソースを消費するプロセスです。



クロージャーはjs開発者のお気に入りの友達です!



頻繁な変更が必要なときにテンプレートを作成する最速の方法は、DOMを手動で構築することです。 これには、document.createElement()、document.createTextNode()、および(必ずしもではないが)document.createAttr()という3つのメソッドがあります。 異なるブラウザーでこれらのメソッドを使用すると、innerHTMLを使用した場合よりさらに遅くなる可能性がありますが、出力では要素へのポインターを取得し、ページコードは識別子で乱雑になりません。



砂糖


まず、Templaterモジュールと、生産性を高めてコードを読みやすくする3つの関数を作成します。

 module Templater { export function Element(TagName: string, Attributes: Object) : HTMLElement { var item:HTMLElement = document.createElement(TagName); for (var p in Attributes) if (typeof(Attributes[p]) == "string") item.setAttribute(p, Attributes[p]); else if (Attributes[p] instanceof Attr) item.setAttributeNode(Attributes[p]); return item; } export function Text(Text: string): Text { return document.createTextNode(Text); } export function Nest(Element: HTMLElement, Items: any[]): HTMLElement { var l = Items.length; for (var i = 0; i < l; i++) if (Items[i]) Element.appendChild(Items[i]); return Element; } // ...
      
      







最初の関数は、指定された名前でタグを作成し、必要な属性を設定します。Nest-要素とテキストの階層構造を作成します-document.createTextNode()の同義語です。



データ構造


TypeScriptには控えめなタイピングなどの文明の利点があるため、使用する価値があります。 したがって、フォーム上の要素とそれに関連するデータがあります。 明白なことをしましょう:

  export class Block { constructor (public Element: HTMLElement, public Binding) { } }
      
      





さて、要素に対してアクションを実行する補助構造:

  export class Template { constructor (public Create: { (Element: { (TagName: string, Attributes: Object): HTMLElement; }, Nest: { (Element: HTMLElement, Items: any[]): HTMLElement; }, Text: { (Text: string): Text; }) : Block; }, public Update: { (Element: Block, Data); }) { } public Make(Data) { var item = this.Create(Element, Nest, Text); this.Update(item, Data); return item; } }
      
      







熱心な読者は、型付きの匿名関数を引数として示していることに気付くでしょう。 これは非常に便利です。コンポーネント開発者とコンポーネントを使用する開発者の両方が、ライブラリが何を望んでいるかを知っています。



使用例


テンプレートは次の方法で作成されます。

 import T = Templater; var myItem = new T.Template( (Element, Nest, Text) => { var Name, Date, Text, Flag, Container; Nest(Container = Element('div', { 'class': Flag = document.createAttribute("class") } ), [ Name = Element('p', { 'class': 'name' }), Text = Element('p', { 'class': 'text' }), Date = Element('p', { 'class': 'date' }) ]); return new T.Block(Container, { Name: Name, Date: Date, Text: Text, Flag: Flag, Container: Container }); }, (Element: T.Block, Data) => { var b = Element.Binding; b.Name.innerText = Data.Name || ""; b.Date.innerText = Data.Date.toLocaleTimeString(); b.Text.innerText = Data.Text || ""; b.Flag.nodeValue = "message " + (Data.Out == true ? "out" : ""); });
      
      





TypeScriptのもう1つの利点として、匿名関数(引数)の短いレコード=> {code}が表示されます。 これにより、コードがはるかに読みやすくなります。

パフォーマンス上の理由から、最初の関数に引数(要素、ネスト、テキスト)を渡します。結局のところ、関数への直接参照は、モジュールの静的関数へのポインターよりも速く動作します。 これらは、この場合の状況を修正するJavascriptおよびTypescriptの機能であり、単に機能しません。

このコードは何をしますか?

最初の関数(作成)を使用して、空のドキュメント要素を作成し、ペア(要素、バインディング)を返します。後者は、テンプレートに必要なコントロールへのリンクを含む一種のViewBagを表します。 このようなタスクのQuerySelectorは不要になりました。

2番目の関数(更新)は、DataのデータをBindingの要素にバインドします



テストデータセット



古き良き.NETのように、テストデータを担当するコードを別のモジュールに移動できます。



 module Example { export class ExampleData { constructor (public Name: string, public Date: Date, public Text: string, public Out: bool) { } public static GetExampleData(count: number): any[] { var result = [], ExampleText = "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...", NameA = "John Doe", NameB = "Jane Doe", StartDate = new Date(), Interval = 300; for (var i = 0; i < count; i++) { result.push(new ExampleData( i % 2 == 1 ? NameA : NameB, StartDate, ExampleText, i % 2 == 1)); StartDate = new Date(StartDate.getTime() + Interval); } return result; } } }
      
      





TypeScriptを使用する場合、この言語のthisキーワードがCのようなプログラミング言語と同じ意味であることを喜ぶことは止められません。クラスオブジェクトの現在のインスタンスへのリンクであり、「関数呼び出しのコンテキストによって異なります」ではありません。



打ち上げ



コードの機能を確認するために、メイン関数は残ります:

 window.onload = () => { var TestDataLength = 1500, TestData = Example.ExampleData.GetExampleData(TestDataLength), Container = document.getElementById('container1'); var TestStart = new Date(); for (var i = 0; i < TestDataLength; i++) Container.appendChild(myItem.Make(TestData[i]).Element); var TestEnd = new Date(); console.log("Test completed", { time: TestEnd.getTime() - TestStart.getTime(), count: TestDataLength }); var StringTemplate = "<div class=\"{Flag}\" >" + "<p class=\"name\" >{Name}</p>" + "<p class=\"message\" >{Text}</p>" + "<p class=\"date\" >{Date}</p>" + "</div>"; var html = ""; Container = document.getElementById('container2'); TestStart = new Date(); for (var i = 0; i < TestDataLength; i++) { html += StringTemplate .replace(/\{Flag\}/g, TestData[i].Out ? "message out" : "message") .replace(/\{Name\}/g, TestData[i].Name || "") .replace(/\{Text\}/g, TestData[i].Text || "") .replace(/\{Date\}/g, TestData[i].Date.toLocaleTimeString()); } Container.innerHTML = html; TestEnd = new Date(); console.log("Test with string templater completed", { time: TestEnd.getTime() - TestStart.getTime(), count: TestDataLength }); };
      
      





そして、簡単なコマンドを実行します:

tsc templater.ts





少し待ってから、コンパイラーはjsでコンパイル済みファイルを作成します。これは、元のファイルとサイズと可読性が実質的に同じです。 マイクロソフトに感謝します!



ソースコードリンク



All Articles