Handlebars 템플릿 엔진의 each 플러그인 및 원본 읽기
13032 단어 each
만약 효과가 좋지 않다면, 한 칸으로 이동할 수 있다
Handlebars 템플릿 엔진은 현재 가장 유행하는 템플릿 엔진 중 하나로서 이미 개발 과정에서 우리에게 무수한 편의를 제공하였다.무의미한 템플릿 엔진으로서Handlebars는 극소의helper 함수만 제공하여 템플릿 엔진 자체를 복원한다. 이것이 바로 그가 효율에서 약간 앞서는 원인일지도 모른다. 여기에 하나 있다네티즌 테스트 Handlebars가 만행 효율에서 jade, EJS보다 약간 낫다는 것을 나타낸다.물론 템플릿 엔진이라는 것은 효율을 제외하고 개발 효율, 미관도 또한 매우 중요한 템플릿 엔진의 우열을 평가하는 지표이다. 예를 들어 많은 개발자들이 Jade가 매우 간결하고 개발이 시원하다고 생각한다.우안은 이곳에서 플래그를 세우고 싶지 않다.Handlebars가 왜 효율에 있어서 이런 장점을 가지는지에 대해 우안은 여기서 계속 깊이 들어가지 않는다. 관심 있는 신발은 참고할 수 있다원본 코드 물론 Handlebars가 제공하는 기능이 너무 적다는 사용자도 적지 않다. 예를 들어
var arr = [
{name:'John',age:11},
{name:'Amy',age:12},
{name:'Lucy',age:11}
];
<ol>
{{#each arr}}
<li>Name:{{name}},Age:{{age}}<li>
{{/each}}
</ol>
출력 결과:<ol>
<li>Name:John,Age:11<li>
<li>Name:Amy,Age:12<li>
<li>Name:Lucy,Age:11<li>
</ol>
이것은 매우 일반적인 수조 순환 출력입니다. 2.객체를 순환합니다var obj = {
name: 'John',
age: 11,
sex: 'male',
id: '000001'
};
<ul>
{{#each obj}}
<li>{{@key}}:{{this}}<li>
{{/each}}
</ul>
출력 결과:<ul>
<li>name:John<li>
<li>age:11<li>
<li>sex:male<li>
<li>id:000001<li>
</ul>
여기 at key가 바로 대상의 속성 이름입니다.
원리에 관해서는 뒤의 코드 해석에서 약간의 해석이 있을 것이다.내부 중첩 순환
var list = [
{name:'John',sports:'basketball',scores:[2,2,2,2]},
{name:'Amy',sports:'tennis',scores:[1,2,3,4]}
];
<table>
{{#each list}}
<tr>
<td>{{name}}</td>
<td>
{{#each scores}}
{{../sports}}:{{this}}<br/>
{{/each}}
</td>
</tr>
{{/each}}
</table>
출력 결과:<table>
<tr>
<td>John</td>
<td>
basketball:2<br/>
basketball:2<br/>
basketball:2<br/>
basketball:2<br/>
</td>
</tr>
<tr>
<td>Amy</td>
<td>
tennis:1<br/>
tennis:2<br/>
tennis:3<br/>
tennis:4<br/>
</td>
</tr>
</table>
여기는 플러그인 순환입니다. 첫 번째 each 순환list층입니다. 속성은name,sports,scores가 있습니다. 두 번째 each 순환scores입니다. 이때의this는 수조 scores의 모든 score를 가리킵니다{{../sports}}
상부 구조 중의sports를 가리킨다.같은 대상에서 상부 데이터에 접근하는 것은 마치 이해하기 좋은 것 같다. 예를 들어 최상위 인용을 보류하고 아래로 찾는 것과 같다.그러나 사실 이곳의 경로 차원은 한 대상의 차원 관계(나만 그렇게 생각하는 것은 아니겠지)가 아니라 여러 개의 each 순환의 끼워 넣는 차원이다. 다음 예에서 알 수 있다.4. 다중 삽입 순환 여기에는 전국 각 성, 직할시의 지명과 우편번호의 수조zone, 그리고 구역별로 구분된 수조catZone이 있다..var zone = [{"label":" ","code":110000},
{"label":" ","code":120000},
{"label":" ","code":130000},
{"label":" ","code":140000},
{"label":" ","code":150000},
{"label":" ","code":210000},
{"label":" ","code":220000},
{"label":" ","code":230000},
{"label":" ","code":310000},
{"label":" ","code":320000},
{"label":" ","code":330000},
{"label":" ","code":340000},
{"label":" ","code":350000},
{"label":" ","code":360000},
{"label":" ","code":370000},
{"label":" ","code":410000},
{"label":" ","code":420000},
{"label":" ","code":430000},
{"label":" ","code":440000},
{"label":" ","code":450000},
{"label":" ","code":460000},
{"label":" ","code":500000},
{"label":" ","code":510000},
{"label":" ","code":520000},
{"label":" ","code":530000},
{"label":" ","code":540000},
{"label":" ","code":610000},
{"label":" ","code":620000},
{"label":" ","code":630000},
{"label":" ","code":640000},
{"label":" ","code":650000},
{"label":" ","code":710000},
{"label":" ","code":810000},
{"label":" ","code":820000}
];
var catZone = [{'label':" ",'code':[310000,320000,330000]},
{'label':" ",'code':[340000,360000]},
{'label':" ",'code':[110000,120000,130000,140000,150000]},
{'label':" ",'code':[410000,420000,430000]},
{'label':" ",'code':[350000,440000,450000,460000]},
{'label':" ",'code':[210000,220000,230000]},
{'label':" ",'code':[610000,620000,630000,640000,650000]},
{'label':" ",'code':[500000,510000,520000,530000,540000]},
{'label':" ",'code':[810000,820000,710000]}
];
var data = {zone:zone,catZone:catZone};
현재 각 지명을 구역에 따라 표로 만들고자 합니다. 우선catZone을 순환해서 그 중의 코드 수조를 꺼내서 두 번째를 하고 zone 수조에서 코드가 현재 코드의 지명을 찾아야 합니다. 그러나 코드는 인덱스가 아니기 때문에 직접 얻을 수 없기 때문에 계속해서 zone(이때.././zone는 데이터의 zone)를 순환합니다.zone의 코드와 두 번째 each 순환의 코드(./this가 상층의this를 가리키는 것)가 같은지 비교합니다. <table class="table table-bordered">
{{! each }}
{{#each catZone}}
<tr>
<th><label><input type="checkbox" />{{label}}</label></th>
<td>
{{! each }}
{{#each code}}
{{! each }}
{{#each ../../zone}}
{{! equal helper, , options.inverse}}
{{#equal code ../this}}
<label class='pull-left'><input type="checkbox" data-code="{{code}}"/>
{{label}}
</label>
{{/equal}}
{{/each}}
{{/each}}
</td>
</tr>
{{/each}}
</table>
최종 효과는 다음과 같다.장쑤 성
장쑤 성
화동
장시 성
화북
북경 천진 하북 산서 내몽고
중국
호남성
화남
복건 광동 광서 해남
동북
랴오닝 성
서북
섬서성 감숙성 청해 영하 신강
서남
중경 사천 귀주 운남 티베트
홍콩, 마카오, 대만
홍콩 마카오 대만
원본 해독
위의 예에서 알 수 있듯이 자체적으로 가지고 있는 Helper:each는 매우 강력한 보조 함수이다.그룹과 대상을 순환할 수 있을 뿐만 아니라, 경로 문자로 표시되는 플러그인 관계도 지원한다.마지막 예에서 알 수 있듯이 이런 플러그인 인덱스는 위에서 아래로 내려가는 관계가 아니라 현재 층에서 출발하여 상부 데이터를 찾는 방법(현재 층으로 기록된parent, 여러 층은parent.parent...인덱스를 통해)으로 데이터 구조의 가벼움과 기능을 실현할 수 있다.이어서 우리는 원본 코드층에서 구체적인 실현을 보았다.javascriptinstance.registerHelper('each', function(context, options) { if (!options) { throw new Exception('Must pass iterator to #each'); }var fn = options.fn, inverse = options.inverse; var i = 0, ret = "", data;
var contextPath; if(options.data & & options.ids) {//주 1 contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]) +'.'}//function으로 전달되면 context = context () if (isFunction (context)) {context = context.call (this);}
if (options.data) {//주 2 data =createFrame (options.data);}
if(context & & typeof context =='object') {//상하문(매개 변수)이 수조 if(isArray(context)) {for(var j = context.length;i
현재 요소의 컨텍스트 색인
//at first
현재 요소가 첫 번째 요소인지 여부
//at last
현재 원소가 마지막 원소 데이터인지 여부입니다.index = i; data.first = (i === 0); data.last = (i === (context.length - 1));//contextPath와 같은 형태를 구축합니다.1의 컨텍스트 경로 if(contextPath) {data.contextPath = contextPath + i;}//모든ret를 합쳐서 렌더링된 html 문자열//주3ret=ret+fn(context[i], {data:data});}else {//상하문이 object일 때 for.in으로 object for (var key in context) {//원형 체인 속성 제거 if (context.hasOwnProperty (key)) {if (data) {//
//at 키는 context가 object일 때
, 현재 속성의 키 데이터입니다.key = key; data.index = i; data.first = (i === 0);//contextPath와 같은 형태를 구축합니다.key의 상하문 경로 if(contextPath) {data.contextPath = contextPath + key;}ret = ret + fn(context[key], { data: data }); i++; } } } }//each에 렌더링할 내용이 없으면 inverse 방법if(i==0) {ret=inverse(this);}
return ret;});
each-helper , , 。 , , , 。<br> 1:contextPath( )
javascript function appendContextPath(contextPath, id) { return (contextPath ? contextPath + '.' : '') + id; } appendContextPath data contextPath = id.index.id.index.id.index....(index Array Object key) 2:frame( )
javascript var createFrame = function(object) { var frame = Utils.extend({}, object); frame._parent = object; return frame; }; Handlebars , , , fn, fn , _parent 。 ,Handlebars , :
javascriptvar AST = {/생략/IdNode: function(parts, locInfo) {LocationInfo.call(this, locInfo);this.type = "ID"; var original = "",
dig = [],
depth = 0,
depthString = '';
for (var i = 0, l = parts.length; i < l; i++) {
var part = parts[i].part;
original += (parts[i].separator || '') + part;
if (part === ".." || part === "." || part === "this") {
if (dig.length > 0) {
throw new Exception("Invalid path: " + original, this);
} else if (part === "..") {
depth++;
depthString += '../';
} else {
this.isScoped = true;
}
} else {
dig.push(part);
}
}
this.original = original;
this.parts = dig;
this.string = dig.join('.');
this.depth = depth;
this.idName = depthString + this.string;
// an ID is simple if it only has one part, and that part is not
// `..` or `this`.
this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
this.stringModeValue = this.string;
}
/* */
};
[AST](https://github.com/wycats/handlebars.js/blob/master/lib/handlebars/compiler/ast.js) Handlebasr compiler , , IdNode , contextPath id。 IdNode , Handlebars , , 。<br> 3:fn( ) , ,Handlebars compiler , fn, context fn, HTML ret
javascriptvar fn = this.createFunctionContext(asObject);JavaScriptCompiler.prototype = {/생략/createFunctionContext: function(asObject) {var varDeclarations ='; var locals = this.stackVars.concat(this.registers.list);
if (locals.length > 0) {
varDeclarations += ", " + locals.join(", ");
}
// Generate minimizer alias mappings
for (var alias in this.aliases) {
if (this.aliases.hasOwnProperty(alias)) {
varDeclarations += ', ' + alias + '=' + this.aliases[alias];
}
}
var params = ["depth0", "helpers", "partials", "data"];
if (this.useDepths) {
params.push('depths');
}
// Perform a second pass over the output to merge content when possible
var source = this.mergeSource(varDeclarations);
if (asObject) {
params.push(source);
return Function.apply(this, params);
} else {
return 'function(' + params.join(',') + ') {
' + source + '}';
}
}
}``` 여기 구체적인 코드 스탬프여기 번역 자체는 매우 복잡한 일이다. 명확한 구조, 완전한 규범이 필요할 뿐만 아니라 일정한 최적화와 불필요한 수단도 필요하다. 나는 여기서 말하지 않겠다(사실은 나도 모른다. 555~).createFunctionContext 반환값이 컴파일된 후의 Function으로 목적을 달성한 것을 알 수 있습니다.
결어
현재 전단 기술의 발전이 신속하고 템플릿 엔진에 대한 요구가 갈수록 높아지며 기능이 갈수록 복잡해진다.Handlebars는 우안이가 제가 아주 좋아하는 템플릿 엔진입니다. 첫 번째로 원본을 읽기로 결정한 엔진이기도 합니다(상당히 힘들어요). 읽는 과정에서 우안이는 원본을 보면서 크롬에서 호출 창고를 끊어서 읽는 속도가 괜찮은 것 같습니다. 원본을 읽고 싶은 아동화는 한번 입어보세요~-
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
【each 문 중첩 거동】 each 안에 each 안에 each왜 each 안에 each를 넣고 싶어졌는가 하면 이런 식으로 계층의 카테고리 기능을 작성하는 과정에서 필요하다고 생각했습니다. 내용이 이런 느낌으로 어려워지고 있습니다. 카테고리 수는 모두 400 가까이 ... 유...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.