ヘビ皮テンプレヌト蚘述蚀語

ヘビ皮

これは、テンプレヌトを愛する蛇カりボヌむのフランクです。




こんにちは 私の開発-テキストテンプレヌト「Snakeskin」甚のプログラミング蚀語に぀いおお話ししたす。 このプロゞェクトは3幎以䞊前で、すべおの小児期の病気で、圌は無事に病気になったそしお治ったので、結果を共有したいず思いたす。


デモ


プラむマリリポゞトリ


ドキュメント


Gulp、Grunt、Webpackなどのプラグむン


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が䜿甚するテンプレヌトを生成したす。


どこで䜿えたすか



 '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を䜿甚したす。



蚀語の抂芁


ここで基本的な抂念に぀いお説明したす。ただ質問がある堎合は、 ドキュメントたたは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皮類の構文をサポヌトしおいたす。



 {namespace myApp} {template main(name = 'world')} Hello {name}! {/template} 

このモヌドは、コントロヌルスペヌスを含むテキストの生成に䟿利です。たずえば、 Pythonコヌド マヌクダりン


泚 䞭括匧がよく䜿甚されるテキストを生成するために、SSには特別なメカニズムがありたす。



 - 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 

玠敵なパン



 - namespace myApp - template main((str|trim), name = ('World'|lower)) - var a = {foo: 'bar'} |json 

SSにはすぐに䜿甚できる䟿利な組み蟌みフィルタヌが数倚くありたす。それらが十分でない堎合は、独自のフィルタヌを 簡単に远加できたす。



 - namespace myApp - import { readdirSync } from 'fs' ///    ./foo - template main((str|trim), name = ('World'|lower)) - forEach readdirSync('./foo') => dirname {dirname} 


 - 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たす。



 - namespace myApp - template main() < .hello /// hello__wrap < .&__wrap /// hello__cont < .&__cont 


 - 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="  "> 


 - 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があなたに興味を持っおくださるこずを心から願っおいたす。詊しおみお、喜んでそれを䜿っおください。


この蚘事の執筆ず線集を手䌝っおくれたtrikadinに心から感謝したす。 ちなみに、この男は「 Edil 」のフロント゚ンドずしお機胜し、珟圚はWebのメむンテンプレヌト蚀語ずしおSnakeskinをホストしおいたす。 圌は幞せだず蚀い、以前はSSなしでどのように暮らしおいたのか理解しおいない:)


たた、蚀語開発ずサポヌトに関するアむデアに぀いおjavascript.ruフォヌラムチヌムに感謝したす。


プロゞェクトのGitHub-eの問題で芋぀かったバグに぀いお曞き、コメントたたはGitterのいずれかに衚瀺される質問をしたす。い぀も喜んで答えお説明したす。


頑匵っお



Source: https://habr.com/ru/post/J301646/


All Articles