mustache庫的機(jī)理
image.png
- 將模板字符串編譯為tokens形式
- 將tokens結(jié)合數(shù)據(jù)阔加,解析為dom字符串
一鹉梨、tokens
tokens是js的嵌套數(shù)組:
image.png
當(dāng)模板字符串中有循環(huán)存在時动漾,它將被編譯成嵌套更深的tokens创译。
二店溢、模板字符串解析tokens的過程
-
定義一個Scanner類酒唉,類中包含兩個方法:scan和scanUntil
image.png
scan:路過指定內(nèi)容矩桂,沒有返回值
scanUntil:讓指針進(jìn)行掃描,知道遇見指定內(nèi)容結(jié)束痪伦,并返回結(jié)束之前路過的文字
-
class Scanner{
constructor(templateStr){
this.templateStr = templateStr
this.pos = 0 // 指針
this.tail = templateStr // 指針尾侄榴,剛開始時指向原字符串
}
scan(tag){
if(this.tail.indexOf(tag)===0){
// tag有多少位就讓tail后移多少位
this.pos += tag.length
this.tail = this.templateStr.substring(this.pos)
}
}
scanUntil(stopTag){
// 記錄執(zhí)行本方法時pos的值
let pos_up = this.pos
// 當(dāng)指針尾不等于要跳過的目標(biāo)字符串時,指針一直向后移動
while(this.tail.indexOf(stopTag)!==0 && this.pos<this.templateStr.length){ // && 后面的用來防止while死循環(huán)
this.pos++
// 改變尾巴為從當(dāng)前指針這個字符网沾,到剩下的全部字符
this.tail = this.templateStr.substr(this.pos)
}
// 返回掃描到的值
return this.templateStr.substring(pos_up, this.pos)
}
}
-
將解析出來的字符串變成tokens
渲染方法render()讓模板字符串變成tokens數(shù)組(如下圖)癞蚕;
image.png
折疊tokens,將#和/之間的數(shù)組整合起來作為子數(shù)組(利用collector收集器變量):
-
function parseTokens(tokens){
let res = [] // 存儲最終返回結(jié)果
let sections = [] // 一個棧結(jié)構(gòu),來表示是否有嵌套數(shù)組
let collector = res // 收集器辉哥,用來收集嵌套數(shù)組,指向結(jié)果數(shù)組桦山,因?yàn)閿?shù)組是引用類型,所以collector和res指向同一個數(shù)組
for(let i=0; i<tokens.length; i++){
let token = token[i]
switch(token[0]){
case '#':
sections.push(token)
collector = token[2] = [] // 當(dāng)有嵌套數(shù)組時將collector值初始化為目標(biāo)收集的嵌套數(shù)組
break;
case '/':
sections.pop()
collector = sections.length>0?sections[sections.length-1][2]:res
break;
default:
collector.push(token) // 將每一項(xiàng)token存入collector數(shù)組
}
}
return res;
}
- 將tokens結(jié)合數(shù)據(jù),解析為dom字符串
其中有一個問題是度苔,js不能將帶有點(diǎn)符號的數(shù)據(jù)(如 {{item.label}} )解析出來匆篓,所以需要編寫lookup函數(shù):
- 將tokens結(jié)合數(shù)據(jù),解析為dom字符串
// lookup函數(shù)從一個對象中讀取出嵌套的值,如下lookup(dataObj, 'a.b.c')值為123
let dataObj = {
a: {
b: {
c: 123
}
}
}
function lookup(dataObj, keyName){
// 如果keyName里面有點(diǎn)符號
if(keyName.indexOf('.')!==-1){
let keys = keyName.split('.')
let temp = dataObj
for(let i=0; i<keys.length; i++){
temp = temp[keys[i]]
}
return temp
}
return dataObj[keyName]
}
最后判斷token[0]的值,若是"text"則與結(jié)果字符串直接拼接寇窑,若是"name"則取data中對應(yīng)的值之后再拼接鸦概,若是"#"說明有嵌套數(shù)組則要遞歸。