これは、テンプレートを愛する蛇カウボーイのフランクです。
こんにちは 私の開発-テキストテンプレート「Snakeskin」用のプログラミング言語についてお話しします。 このプロジェクトは3年以上前で、すべての小児期の病気で、彼は無事に病気になった(そして治った)ので、結果を共有したいと思います。
Gitter-ここでは、興味のある質問をすることができます
ちょっとした歴史
私がYandexで働いていたとき(4年前)、同僚とのコーヒーポイントでの熱い議論の主なトピックの1つはテンプレート開発者でした。既存のソリューションの長所と短所について議論し、一部は独自に開発しました。
部門のメインセクションはTemplateToolkit2でした。これは、特にPerl開発者の間で人気のあるテンプレートエンジンであり、クライアントでは最も単純なMicroTemplate(John Rezig作)が使用されました。 そのときでさえ、XSLTのようなエンジンは積極的に強制されましたが、いくつかの理由(この記事の範囲外であるため)は、私たちに適していないものでした。 ハンドルバー、ダスト、クロージャーテンプレート、そしてもちろんバイクなど、他の人と時々実験を行いました。これらすべてが、プロジェクト内のテンプレートエンジンの動物園全体の存在につながりました。
私のお気に入りはGoogle Closure Templatesでした。テンプレートは文字列を返すだけの関数として位置付けられていたため、プログラマーとしても身近でした。 しかし、些細なフィルターを追加するためにJavaコードを編集する必要があることは非常に腹立たしく、放送速度はそれほど暑くありませんでした(本当に感じました)。
そして、私は自分のクロージャーテンプレートを作りたかった ブラックジャックと売春婦 :当然、JSで記述される必要があり、その結果、Javaを知る必要なく、変更に対してオープンになります。 さらに、私は静的ブロックベースのテンプレート継承モデルが好きで、これをDjango Templatesで調べました(そのため、名前-Pythonへの参照)-既存の継承システムの基礎を形成しました。
私は約3日でプロトタイプをスケッチしました。これは、700行のコードの常連のひどいハードコードでした。 その結果で少し遊んだり、同僚と共有したり、フィードバックを受け取ったり、何らかのフィードバックを受け取ったりして、先に進むことにしました。 このケースのリファクタリング、バグの修正、追加 新しい 機会の。 1週間の開発の後、バージョン2をリリースしました。実際、レギュラーと同じハードコードですが、より安定した機能を備えています。 すでに使用されている可能性があります。
結果にしばらく取り組んで数十のアップデートをリリースした後、私は手をこすり、「物事を正しくする時が来た」と考えてコンピューターに座って、約1か月後に第3バージョンをリリースしました:ES6でハードコードを捨ててコードを書き直しました(当時は普通の翻訳者がいなかったので、私も自分の翻訳者をコピーしました(再び、レギュラーにひどいハードコードを付けました-はい、レギュラーが好きです))、構文解析と多くの新機能を備えたツリーの構築を追加しました。
このバージョンは、安定した強力なもので、実際、ステロイドのクロージャーテンプレートでした。 その結果に満足し、個人のプロジェクトでSnakeskinを使用し始めました。時々、新しいアップデートとパッチをリリースしました。
後でHAMLとJadeに会って、構文へのアプローチが気に入ったため、Snakeskinに似たものを追加することにしました(このソリューションの結果はJadeのような構文になりました)。 数ヶ月にわたる活発な開発の後、私は4番目のバージョンをリリースしました。これは、言語の歴史における真のマイルストーンとなり、さらなる開発を決定しました。 5番目と6番目は4番目のバージョンの修正にすぎませんでしたが、 重大な変更が必要であり、 SemVerのバージョンパターンとしてSemVerを選択したため、メジャーバージョンを混乱させる必要がありました。
私はかなり長い間SS6を使用し、さまざまなプロジェクトで私の友人や同僚もそれを使用し始めました-結果として、しばらくして、苦情のリストが蓄積されました-非常に長くはありませんが、それでも多くの機能があり、それらは非常に混oticとして現れました、およびディレクティブ間の「競合」が表示されるようになりました。 この理由は、初期の言語仕様がないことでした。「ウィッシュリスト」が登場するにつれて開発が進みました。
私はあなたがもうそのように生きることはできないと決めました-あなたはすべてを標準化し、ゴミを取り除く必要があります。 開発は1年半続きました(ただし、最大で6か月間アクティブでした-空き時間の不足が影響を受けました)が、最終的に、私たちはSnakeskin:version 7で最も安定した思慮深いリリースを得ました。 そして私は彼を心から誇りに思っています。
初見
Snakeskinに最も適しているのは、CoffeeScriptやTypeScriptのように、JSに対する単なる「砂糖」であるという定義であるように思えますが、テンプレートを書くというかなり狭い専門性があります。 もちろん、アプリケーション全体をSSで作成することは完全に可能ですが、そうではなく、あまり便利ではありません。 SSは、メイン言語(主にJS)での使用を目的としています。
select.ss
- namespace select - template main(options) < select - forEach options => el < option value = ${el.value} {el.label}
select.js
import { select } from 'select.ss'; class Select { constructor(options) { this.template = select.main(options); } } const newSelect = new Select([{value: 1, label: ''}, {value: 2, label: ''}])
ここで、JSのメインファイルでは、Snakeskinのファイルがモジュールとして接続されています(たとえば、 WebPackのプラグインはこのようなシームレスな統合を提供します)。 それから名前空間select
をインポートし、クラスSelect
を宣言します。 Select
インスタンスを作成するとき、 main
テンプレート( main
テンプレートが変換された)を実行し、その作業の結果をtemplate
プロパティに割り当てますnewSelect
場合は次のようになります。
<select> <option value="1"> </option> <option value="2"> </option> </select>
ご覧のとおり、SSはJS(具体的にはES5)に変換されるため、メインコードで非常に使いやすくなります。
Snakeskinを作成し始めた理由について話すと、主な動機はテンプレートコードを変更せずにサーバーとクライアントで同時に使用できる強力なコード再利用機能を備えたテンプレート言語を使用することでした。 そして、もちろん、言語とアイデアの新しい要件が「しかし、私にそのような機能を追加する必要があります」というスタイルで現れ始めました。これらすべてが、創造的かつ論理的に意味のあるもので、スネークスキンをあなたが今見ているようにしました。
たとえば、「時代の要件」の1つは、独自のテンプレート言語(AngularやReactなど)を持つフレームワークやライブラリとのシームレスな統合の必要性でした。そして今ではSnakeskinが非常にうまく機能しています。
SSを使用して角度パターンを作成する例:
- namespace myApp - template main() < label Name: < input type = text | ng-model = yourName | placeholder = Enter a name here < hr < h1 Hello {{yourName}}!
作業結果main
<label> Name: </label> <input type="text" ng-model="yourName" placeholder="Enter a name here"> <hr> <h1> Hello {{yourName}}! </h1>
Snakeskinはコードの量を大幅に削減し、レイアウト要素の再利用(継承、構成、不純物など)を可能にし、Angularはデータバインディングを実装します。 技術的な観点から、SSはAngularが使用するテンプレートを生成します。
どこで使えますか
- サーバーの標準化 -ここではすべてが簡単です。SSをモジュールとして接続し、ファイルをコンパイルします-ノード。 jsはそのテンプレートを関数として使用します:
'use strict'; const http = require('http'); const ss = require('snakeskin'); // // - const tpls = ss.compileFile('./myTpls.ss'); http.createServer((req, res) => { res.writeHead(200, {'Content-Type': 'text/html'}); // foo res.write(tpls.foo('bar', 'bla')); res.end(); }).listen(8888);
もちろん、実際にはExpressやKoaのようなサーバーフレームワークになりますが、重要ではありません。 また、テンプレートは(そしてできれば) GulpまたはGruntの プラグインを使用して事前に翻訳し、受信したファイルを接続することもできます。または、上記のようにWebPackを使用します。
静的サイト生成 :プラグインには、翻訳時にコンパイル済みテンプレートを呼び出し、その作業の結果を返すオプションがあります。 プラグイン自体がメインテンプレートを計算するか、明示的に指定できます。
- クライアント上でJSに翻訳されたテンプレートを使用する :「コンパイル済み」モジュールは、外部
<script>
を介して、またはモジュールとして接続できます(Webpack、Browserify、RequireJSまたはその他のモジュール管理システムを使用)。
言語の概要
ここで基本的な概念について説明します。まだ質問がある場合は、 ドキュメントまたはGitterへようこそ。
メイン
パターン
何度も述べたように、Snakeskinテンプレートは翻訳後にJavaScript関数になります。
- namespace myApp - template main() Hello world!
放送後、次のようになります。
if (exports.myApp === 'undefined') { var myApp = exports.myApp = {}; } exports.myApp.main = function main() { return 'Hello world!'; }
もちろん、これは単純化されたコードですが、全体的には次のようになります。
構文
SSは2種類の構文をサポートしています。
- Classic :ディレクティブは中括弧で囲まれています。 ブロックのもの(他のSSコードを含む可能性がある)は、明示的に閉じる必要があります。
{namespace myApp} {template main(name = 'world')} Hello {name}! {/template}
このモードは、コントロールスペースを含むテキストの生成に便利です。たとえば、 Pythonコード マークダウン
注 :中括弧がよく使用されるテキストを生成するために、SSには特別なメカニズムがあります。
- Jadeのような :制御スペースに基づいており、Jadeに似ています(そのため、名前)。 それを使用した上記の例は次のようになります。
- namespace myApp - template main(name = 'world') Hello {name}!
この構文の主な利点は、簡潔さと明快さです。 XMLのような構造の生成に最適です。
SSは混合構文もサポートしています。
- namespace myApp {template hello(name = 'world')} Hello {name}! {/template} - template main(name) += myApp.hello(name)
コード再利用ツール
継承
SSでは、各テンプレートはクラスです。つまり、メソッドとプロパティがあり、別のテンプレートから継承できます。 子テンプレートは、継承された親メソッドとプロパティをオーバーライドし、新しいものを追加できます。
テンプレートの継承の例 。
- namespace myApp /// sayHello base /// SS , /// -- , /// - block base->sayHello(name = 'world') Hello {name}! - template base() - doctype < html < head /// head /// , /// - block head < title /// `title`, - title = ' ' ? < body - block body /// sayHello += self.sayHello() /// - block child->sayHello() /// sayHello - super Hello people! /// - block child->go() Let's go! /// child base - template child() extends myApp.base /// - title = ' ' /// - block body - super += self.go()
child
実行結果 :
<!DOCTYPE html> <html> <head> <title> </title> </head> <body> Hello world! Hello people! Let's go! </body> </html>
テンプレート、入力パラメーター、テンプレートのデコレーター、さまざまな修飾子を継承する場合、 ここで詳細を読むことができます。
構図
Snakeskinのすべてのテンプレートは関数であるため、当然、どのテンプレートも他のテンプレートを呼び出すことができます: call
ディレクティブはこれに役立ちます。
- namespace myApp - template hello(name = 'world') Hello {name}! - template main(name) - call myApp.hello(name) /// += myApp.hello(name)
値としてのパターン
Snakeskinでは、テンプレートをオブジェクトの変数またはプロパティに割り当てたり、関数に引数として渡したりすることができます。
- namespace myApp - template wrap(content) < .wrapper {content} - template main(name) += myApp.wrap() < .hello Hello world!
main
結果
<div class="wrapper"> <div class="hello"> Hello world! </div> </div>
モジュール
Snakeskinで記述された各ファイルはモジュールです。グローバル変数はその中にカプセル化され、すべてのテンプレートがエクスポートされます。 モジュールは、 include
ディレクティブを使用して他のモジュールをプラグインできます。
したがって、コードを簡単に論理部分に分割し、プラグインライブラリ(および場合によってはフレームワーク)を作成し、一般に「分割して征服する」というルールを容赦なく従うことができます。
math.ss
- namespace math - template sum(a, b) {a + b}
app.ss
- namespace myApp - include './math' - template main() 1 + 2 = += math.sum(1, 2)
myApp.mainを呼び出した結果
1 + 2 = 3
素敵なパン
組み込みディレクティブの豊富なセット
Snakeskinには、
if
、for
、var
、return
など、JSのステートメントと意味的に同等のディレクティブがあります。 テンプレート言語に固有のディレクティブと、XMLに似た構造のマークアップを簡素化するディレクティブ:tag
、attr
、doctype
、comment
など。 非同期テンプレート生成のディレクティブ:await
、yield
、parallel
、waterfall
; その他多数 。
ホステスへの注意 :SnakeskinはまだJavaScriptではないため、ニュアンスの一部のディレクティブはJSでの同様の演算子の動作とは異なる場合があります。 たとえば、
var
介しvar
宣言された変数にはブロックスコープがあります(これがES2015からのletの仕組みです)。with
ディレクティブは、JSから同じ演算子のアーキテクチャ上の欠陥を排除します。これは、SS内での使用を非常に「良い習慣」にし、コードの記述を単純化し、高速化します。
-
フィルターは、ほとんどのテンプレートエンジンに何らかの形で存在しますが、SSでは言語のコアの一部であるため、変数を作成するとき、ループで、ブロックやテンプレートへの引数を宣言するとき、ディレクティブで...どこでも。
- namespace myApp - template main((str|trim), name = ('World'|lower)) - var a = {foo: 'bar'} |json
SSにはすぐに使用できる便利な組み込みフィルターが数多くあります。それらが十分でない場合は、独自のフィルターを 簡単に追加できます。
JSとの双方向モジュラー統合
SSテンプレートはJSプログラムにインポートでき、SnakeskinはJavaScriptモジュールをインポートできます(
import
ディレクティブを使用)。すべての主要なタイプのモジュールをサポートしています: umd 、 amd 、 commonjs 、 native 、 global
- namespace myApp - import { readdirSync } from 'fs' /// ./foo - template main((str|trim), name = ('World'|lower)) - forEach readdirSync('./foo') => dirname {dirname}
-
他のテンプレート言語のコード生成のための特別なツールの存在
たとえば、ビルドシステム(Gulpなど)のSSプラグインには、SnakeskinテンプレートがすぐにReact.Elementを返すモードがあります。
Reactのテンプレート生成
- namespace myComponent - template render() < .hello {{ this.name }}
import React from 'react'; import { myComponent } from './myComponent.ss'; const Foo = React.createClass({ render: myComponent.render });
このようなシームレスな統合のために、テンプレートがReactを使用して作成された要素を返す場合、 jsx
フラグをjsx
してjsx
プラグインをjsx
ます。
-
スティッキーリンクのサポート (親クラスリンク)
このメカニズムは、CSSプリプロセッサで使用されるメカニズムに似ています。 BEMアプローチを順守する場合に便利です。 操作の原則は次のとおりです。「&」記号で始まるタグを宣言するときにクラス名を指定すると、この記号なしで宣言された最も近い親クラスに置き換えられます。
- namespace myApp - template main() < .hello /// hello__wrap < .&__wrap /// hello__cont < .&__cont
-
多くのSnakeskinディレクティブは、補間メカニズムをサポートしています。つまり、動的テンプレート値をディレクティブにスローします。例:
- namespace myApp - template main(area) < ${area ? 'textarea' : 'input'}.b-${area ? 'textarea' : 'input'}
area
の値に応じて、結果は次のようになります(with area == true
):
<textarea class="b-textarea"> </textarea>
または( area == false
):
<input class="b-input" value=" ">
-
デコレータメカニズムのおかげで、追加のモジュールをSnakeskinに簡単に統合できます-たとえば、プリンタ:
- namespace demo - import Typograf from 'typograf' /// - JS SS - template typograf(params) - return () => target - return () => - return new Typograf(params).execute(target.apply(this, arguments)) /// index - @typograf({lang: 'ru'}) - template index() - !
- namespace myApp - async template main(db) - forEach await db.getData() => el {el} - template *foo(data) - for var i = 0; i < data.length; i++ {data.value} - if i % 1e3 === 0 - yield
「 非同期操作のディレクティブ 」のセクションも参照してください。
カスタムレンダリング
デフォルトでは、Snakeskinは4つのレンダリングモードをサポートしています。1行あたり(デフォルト)、Buffer、DocumentFragment、およびJSXです。 独自のレンダラーを追加することも可能です-たとえば、カスタム仮想DOMを生成するために。
有益なエラーメッセージ
Snakeskin Translatorには、テンプレートを翻訳するときにほとんどの構文エラーと論理エラーを見つけるのに役立つ強力なコードデバッガーがあります。
すべての主要なアセンブリシステムのサポート
良いコードベース
SnakeskinはES2015で完全に記述されており、多数のテストが含まれており、
ADVANCED
モードで最も厳しいGoogle Closure Compilerテストに合格しています。 コードは、GoogleのJSDoc標準に従って文書化されています。
-
おわりに
Snakeskinがあなたに興味を持ってくださることを心から願っています。試してみて、喜んでそれを使ってください。
この記事の執筆と編集を手伝ってくれたtrikadinに心から感謝します。 ちなみに、この男は「 Edil 」のフロントエンドとして機能し、現在はWebのメインテンプレート言語としてSnakeskinをホストしています。 彼は幸せだと言い、以前はSSなしでどのように暮らしていたのか理解していない:)
また、言語開発とサポートに関するアイデアについてjavascript.ruフォーラムチームに感謝します。
プロジェクトのGitHub-eの問題で見つかったバグについて書き、コメントまたはGitterのいずれかに表示される質問をします。いつも喜んで答えて説明します。
頑張って