前端模板引擎(jQuery模板)jsrender的基本使用

jsrender官網(wǎng)號(hào)稱簡(jiǎn)單直觀谊惭、強(qiáng)大輕快可擴(kuò)展鳄梅。壓縮體積也只有8.9kb,可以單獨(dú)在瀏覽器或node中使用驶忌,也可以配合jQuery使用。

jsrender使用 {{}} 來(lái)做分界笑跛,如果{{}}與你現(xiàn)有的模板引擎沖突你可以使用api來(lái)自定義付魔,比如:

$.views.settings.delimiters("<%", "%>");

//原本
<p>{{:name}}</p>
//修改后
<p><%:name%></p>

一聊品、基本語(yǔ)法
1. 基本變量標(biāo)簽 {{:name}}

基本變量需要使用冒號(hào) ":" 作為標(biāo)識(shí),一般是簡(jiǎn)單對(duì)象的某個(gè)屬性几苍。比如傳入一個(gè)簡(jiǎn)單對(duì)象data

//模板
<script type="text/x-jsrender" id="j-specCard">
   <table>
    <tr>
        <td>Name: {{:name}}</td>
        <td>Age: {{:age}}</td>
    </tr>
   </table>
</script>
//邏輯
(function(jq, g) {
//傳入一個(gè)簡(jiǎn)單對(duì)象
    var data = {
            'name': 'alice',
            'age': 18
        },
        //獲取模板
        jsRenderTpl = $.templates('#j-specCard'),
        //末班與數(shù)據(jù)結(jié)合
        finalTpl = jsRenderTpl(data);

    $('.box').html(finalTpl);
})(jQuery, window);

//傳入一個(gè)多層級(jí)對(duì)象complexData

<body>
    <div class="box"></div>
    <script type="text/x-jsrender" id="j-specCard">
        <table>
            <tr>
                <td>Name: {{:personalInfo.name}}</td>
                <td>Age: {{:personalInfo.age}}</td>
            </tr>
        </table>
        <p>{{:top.middle1}}</p>
        <p>{{:top.middle.bottom}}</p>
    </script>
</body>
<script src="../lib/jquery-1.11.2.min.js"></script>
<script src="../lib/jsrender.js"></script>
<script>
(function(jq, g) {

    var complexData = {
            'personalInfo': {
                'name': 'alice',
                'age': 18
            },
            'top': {
                'middle': {
                    'bottom': 'this is bottom value'
                },
                'middle1': 'this is middle1 value'
            }
        },
        //獲取模板
        jsRenderTpl = $.templates('#j-specCard'),
        //末班與數(shù)據(jù)結(jié)合
        finalTpl = jsRenderTpl(complexData);

    $('.box').html(finalTpl);
})(jQuery, window)
</script>

如上所見翻屈,不管傳入的對(duì)象有多復(fù)雜,都能按照層級(jí)去到屬性妻坝,只是把最外層的對(duì)象名省略掉了伸眶。

2. 對(duì)基本變量有轉(zhuǎn)譯功能標(biāo)簽{{>name}}

轉(zhuǎn)譯功能標(biāo)簽使用大于號(hào) ">" 作為標(biāo)識(shí),可以讓數(shù)據(jù)原樣輸出惠勒,可以防止注入攻擊赚抡。比如

<body>
    <div class="box"></div>
    <script type="text/x-jsrender" id="j-specCard">
        <p>{{:name}}</p>
        <p>{{>name}}</p>
    </script>
</body>
<script src="../lib/jquery-1.11.2.min.js"></script>
<script src="../lib/jsrender.js"></script>
<script>
(function(jq, g) {

    var data = {
            'name': '<b style="font-size:24px;">i am alice</b><script>alert("我是注入腳本攻擊")<\/script>'
        },
        //獲取模板
        jsRenderTpl = $.templates('#j-specCard'),
        //末班與數(shù)據(jù)結(jié)合
        finalTpl = jsRenderTpl(data);

    $('.box').html(finalTpl);

})(jQuery, window)
</script>
Paste_Image.png

雖然{{>}}有轉(zhuǎn)譯功能爬坑,我們不一定必須用他纠屋,因?yàn)橛行?biāo)簽我們還是需要顯示的,視具體情況而定

3. 啟用與定義全局變量的標(biāo)簽{{*}}與 {{*:}},不支持代碼語(yǔ)法盾计,如果是代碼售担,那么會(huì)咦字符串的形式輸出

在jsrender語(yǔ)法中,不僅可以處理簡(jiǎn)單的對(duì)象署辉,也支持表達(dá)式族铆,比如

<p>{{*:name.firstName + '  ' + name.lastName </p>
 var data = {
        'name': {
            'firstName': 'cury',
            'lastName': 'steven'
        }
    }

但是為了保證封裝性,并不是任何表達(dá)式都可以處理的哭尝,比如全局變量window就不行哥攘。比如

<p>{{:name.firstName + '  ' + name.lastName + '  ' + window.describe}}</p>
window.describe = " he is a basketball player";

這樣的代碼會(huì)報(bào)錯(cuò)的 Uncaught TypeError: Cannot read property 'describe' of undefined。即使這樣材鹦,jsrender給我提供了手動(dòng)開啟的方式逝淹,只需要調(diào)用api,并且使用{{*:}}標(biāo)簽就可以使用了桶唐。比如

<body>
    <div class="box"></div>
    <script type="text/x-jsrender" id="j-specCard">
    <!--除了可以在js中定義全局變量栅葡,在模板中也是可以的-->
        {{* window.count = 1}}
        <p>{{*:count + 2}}</p>
        <p>{{:name.firstName + '  ' + name.lastName + '  ' + window.describe}}</p>
        <!-- <p>{{*:describe + '  ' + name + count}}</p> -->
    </script>
</body>
<script src="../lib/jquery-1.11.2.min.js"></script>
<script src="../lib/jsrender.js"></script>
<script>
(function(jq, g) {

    window.describe = " he is a basketball player";

    $.views.settings.allowCode(true);
    var data = {
        'name': {
            'firstName': 'cury',
            'lastName': 'steven'
        }
    },
        //獲取模板
        jsRenderTpl = $.templates('#j-specCard'),
        //末班與數(shù)據(jù)結(jié)合
        finalTpl = jsRenderTpl(data);

    $('.box').html(finalTpl);

})(jQuery, window)
</script>

首先需要執(zhí)行$.views.settings.allowCode(true);,把設(shè)置開啟尤泽,然后使用{{* window.count = 1}}定義一個(gè)變量欣簇,然后再用{{*:count}}引用變量。當(dāng)然坯约,jsrender也提供了只有某一個(gè)模板支持全局變量的設(shè)置方式

var tmpl = $.templates({
    markup: "#myTemplate",
    allowCode: true
  });
4. 注釋的標(biāo)簽 {{!-- --}}

在jsrender模板中熊咽,html的注釋是不起作用的,jsrender會(huì)原樣的輸出到dom節(jié)點(diǎn)中闹丐,而dom中是認(rèn)識(shí)這個(gè)注釋的横殴,所以并不會(huì)顯示。但是呢妇智,如果注釋中有未定義的變量滥玷,那么jsrender就會(huì)報(bào)錯(cuò)氏身,比如

 <script type="text/x-jsrender" id="j-specCard">
        <p>這是jsrender模板</p>
        <!-- <p>{{*:describe + '  ' + name + count}}</p> -->
    </script>

如果我沒(méi)有定義window.describe 跟window.count變量,那么這段代碼就會(huì)報(bào)錯(cuò)惑畴,所以我們?cè)趈srender模板中要使用jsrender的注釋{{!-- --}},而且jsrender在渲染的時(shí)候是不會(huì)把注釋代碼返回的蛋欣。

5. 條件判斷語(yǔ)句 {{if}} {{else}}

jsrender 的if else 語(yǔ)法跟正常的代碼邏輯還是有點(diǎn)區(qū)別的,當(dāng)只有兩種情況的時(shí)候如贷,是沒(méi)有區(qū)別的陷虎,就是if else

<script type="text/x-jsrender" id="j-specCard">
    {{if name == 'alice'}}
    <p>Hello Alice</p>
    {{else}}
    <p>Hello tourisor</p>
    {{/if}}
</script>

但是當(dāng)有多種情況的時(shí)候,也就是if elseif elseif else的時(shí)候杠袱,可是jsrender并沒(méi)有elseif這樣的寫法尚猿,它會(huì)根據(jù)情況來(lái)判斷,如果是多重情況楣富,它會(huì)自動(dòng)把else 當(dāng)做elseif來(lái)使用

<script type="text/x-jsrender" id="j-specCard">
    {{if count == 1}}
        <p>welcome first!</p>
    {{else count == 2}}
        <p>welcome again</p>
        <p>這里的else會(huì)被當(dāng)做elseif count==2 使用</p>
    {{else}}
        <p>welcom back!</p>
    {{/if}}
</script>

if else 除了以{{if}}....{{/if}}block閉合的形式使用凿掂,也可以使用{{if xxx=true ... /}}自閉合的形式,而且還可以引入模板

<script type="text/x-jsrender" id="j-specCard">
    {{if count == 1 tmpl="#j-firstTpl" /}}
</script>
<script type="text/x-jsrender" id="j-firstTpl">
    <h3>this is first template</h3>
</script>

當(dāng)然如果你的情況比較復(fù)雜,那么你可以混著用

<script type="text/x-jsrender" id="j-specCard">
    {{if count == 1 tmpl="#j-firstTpl" }}
    {{else count == 2 tmpl="#j-secondTpl"}}
    {{else}}
    <p>no template</p>
    {{/if}}
</script>
<script type="text/x-jsrender" id="j-firstTpl">
    <h3>this is first template</h3>
</script>
<script type="text/x-jsrender" id="j-secondTpl">
    <h3>this is second template</h3>
</script>

有了引用模板的功能纹蝴,我們就完全可以把比較通用的代碼提取出來(lái)寫一個(gè)模板庄萎,減少代碼的冗余。

6. 使用{{for}}循環(huán)對(duì)數(shù)據(jù)進(jìn)行循環(huán)或?qū)?duì)象進(jìn)行遍歷

jsrender的for循環(huán)會(huì)默認(rèn)把數(shù)組或?qū)ο蟮牡谝粚友h(huán)遍歷掉塘安,我們只要管里面的數(shù)據(jù)就行了糠涛,而且使用了循環(huán)之后的模板也可以單獨(dú)寫成一個(gè)模板,在for循環(huán)中引用,循環(huán)數(shù)組的時(shí)候可以使用{{:#index}}來(lái)獲取當(dāng)前數(shù)組的下標(biāo)兼犯,并且index是從0開始忍捡。
傳入模板的數(shù)據(jù)結(jié)構(gòu)

<script>
(function(jq, g) {

    var animal = {
        'kind': 4,
        'price':{
            'cow': 19999,
            'pig': 1888
        },
        'list': [
            {
                'name': 'cow',
                'count': 4,
                'foot': 4
            },
            {
                'name': 'chicken',
                'count': 5,
                'foot': 2
            }
        ]
    },
        //獲取模板
        jsRenderTpl = $.templates('#j-specCard'),
        //末班與數(shù)據(jù)結(jié)合
        finalTpl = jsRenderTpl(animal);

    $('.box').html(finalTpl);

})(jQuery, window)
</script>
<script type="text/x-jsrender" id="j-specCard">
    {{!-- animal 對(duì)象已經(jīng)被默認(rèn)遍歷,所以屬性前不用加animal.就可訪問(wèn)到 --}}
    <h3>there have {{:kind}} kinds animal</h3>
    <p>and the cow price is {{:price.cow}}</p>
    <p>and the cow price is {{:price.pig}}</p>

    {{!--
    也可以這樣對(duì)對(duì)象進(jìn)行for循環(huán)
    {{for price}}
    <p>and the cow price is {{:cow}}</p>
    <p>and the cow price is {{:pig}}</p>
    {{/for}}
    --}}
    <ul>
        {{!-- 對(duì)對(duì)象數(shù)組進(jìn)行循環(huán) --}}

        {{for list}}
        <li>{{:#index + 1}}. this animal call {{:name}}, and has {{:count}}, it has {{:foot}} foots</li>
        {{/for}}

        {{!--
            也可以使用模板引入作為循環(huán)的模板
            {{for list tmpl="#j-listTpl" /}}
         --}}

    </ul>

</script>
<script type="text/x-jsrender" id="j-listTpl">
    <li>this animal call {{:name}}, and has {{:count}}, it has {{:foot}} foots</li>
</script>

當(dāng)循環(huán)數(shù)組或者遍歷對(duì)象的時(shí)候切黔,如果在{{for}}{{/for}}中間加上{{else}}砸脊,還可以對(duì)遍歷的對(duì)象進(jìn)行判斷,如果該對(duì)象或者屬性不存在绕娘,那么就顯示其他的內(nèi)容脓规。

{{!-- 遍歷的時(shí)候順便判斷merbers是否存在 --}}
{{for members}}
    <div>{{:name}}</div>
{{else}} 
    <div>No members!</div>
{{/for}}
7. 使用{{props}}遍歷對(duì)象并且獲取到對(duì)象的key/value

當(dāng)我們遍歷對(duì)象需要使用到對(duì)象的key值時(shí),使用props可以獲取到key/value值险领,而且也可以在for循環(huán)中進(jìn)行對(duì)象的遍歷,在數(shù)據(jù)循環(huán)中獲取可以使用#data獲取到當(dāng)前的對(duì)象侨舆,當(dāng)然也可以使用引入外部模板來(lái)當(dāng)做循環(huán)模板。
傳入模板的數(shù)據(jù)結(jié)構(gòu)

<script>
(function(jq, g) {

    var animal = {
        'kind': 4,
        'price':{
            'cow': 19999,
            'pig': 1888
        },
        'list': [
            {
                'name': 'cow',
                'count': 4,
                'foot': 4
            },
            {
                'name': 'chicken',
                'count': 5,
                'foot': 2
            }
        ]
    },
        //獲取模板
        jsRenderTpl = $.templates('#j-specCard'),
        //末班與數(shù)據(jù)結(jié)合
        finalTpl = jsRenderTpl(animal);

    $('.box').html(finalTpl);

})(jQuery, window)
</script>

模板

<script type="text/x-jsrender" id="j-specCard">
    {{!-- 簡(jiǎn)單的對(duì)象遍歷 --}}
    {{props price}}
    <p>and the {{:key}} price is {{:prop}}</p>
    {{/props}}

    <ul>
        {{!-- 在數(shù)據(jù)循環(huán)中再進(jìn)行對(duì)象的遍歷绢陌,病獲取到key/prop --}}

        {{for list}}
        <li>{{:#index + 1}}. 
            {{props #data}}
                <b>{{:key}}</b> is {{>prop}}  
            {{/props}}
        </li>
        {{/for}}

        {{!--
            也可以使用模板引入作為循環(huán)的模板
            {{for list tmpl="#j-listTpl" /}}
         --}}

    </ul>

</script>
<script type="text/x-jsrender" id="j-listTpl">
    <li>{{:#index + 1}}. 
        {{props #data}}
            <b>{{:key}}</b> is {{>prop}}  
        {{/props}}
    </li>
</script>

當(dāng)然挨下,在prop也可以在遍歷對(duì)象的時(shí)候?qū)?duì)象進(jìn)行判斷,只要在prop變遷中加入{{else}}脐湾,如果對(duì)象為undefined或?qū)ο鬄榭粘舭剩敲淳蛨?zhí)行else邏輯。

{{props address}}
    <b>{{>key}}:</b>{{>prop}} <br />
{{else}} 
    The address is blank (no properties)!
{{/props}}
8. 使用{{include}}引入外部模板或者改變模板的上下文

雖然我們可以在{{for}}循環(huán)中或者{{if}}標(biāo)簽中直接引入模板,但是{{include}}引入模板才是符合我們的認(rèn)知愁铺,應(yīng)該什么標(biāo)簽該干什么事來(lái)的鹰霍。

<script type="text/x-jsrender" id="j-specCard">
    {{if case == 1}}
        {{include tmpl="#j-case1Tpl" /}}
    {{else case == 2}}
        {{include tmpl="#j-case2Tpl" /}}
    {{else}}
        <p>no data</p>
    {{/if}}

</script>
<script type="text/x-jsrender" id="j-case1Tpl">
    <h3>{{:data.title}}</h3>
    <p>{{:data.text}}</p>
</script>
<script type="text/x-jsrender" id="j-case2Tpl">
    <h3>{{:data.title}}</h3>
    <p>{{:data.text}}</p>
</script>

模板數(shù)據(jù)

    var condition = {
        'case': 1,
        'data': {
            'title': 'this is first case',
            'text': 'case one text'
        }
    },
        //獲取模板
        jsRenderTpl = $.templates('#j-specCard'),
        //末班與數(shù)據(jù)結(jié)合
        finalTpl = jsRenderTpl(condition);

    $('.box').html(finalTpl);

使用{{include}}標(biāo)簽引入模板顯得比較語(yǔ)義化,雖然并沒(méi)有什么差別茵乱。除了引入模板茂洒,{{include}}還可以在引入模板的同時(shí)引入對(duì)象或者數(shù)組,來(lái)改變所引入模板的上下文

<script type="text/x-jsrender" id="j-specCard">
    {{if case == 1}}
        {{include tmpl="#j-case1Tpl" /}}
    {{else case == 2}}
        {{include data tmpl="#j-case1Tpl" /}}
    {{else}}
    {{else case == 3}}
        {{include data1 tmpl="#j-case2Tpl" /}}
    {{else}}
        <p>no data</p>
    {{/if}}

</script>
<script type="text/x-jsrender" id="j-case1Tpl">
    {{!-- for循環(huán)會(huì)默認(rèn)取到傳進(jìn)來(lái)的對(duì)象 使用data.title是訪問(wèn)不到的 --}}
    {{!-- 傳進(jìn)來(lái)的對(duì)象必須手動(dòng)循環(huán) --}}
    {{for}}
        <h3>{{:title}}</h3>
        <p>{{:text}}</p>
    {{/for}}
    {{!--
        或者這樣使用
        <h3>{{:#data.title}}</h3>
        <p>{{:#data.text}}</p>
    --}}
</script>
<script type="text/x-jsrender" id="j-case2Tpl">
    {{!-- :length 可以獲取當(dāng)前數(shù)組的長(zhǎng)度 --}}
    {{:length}}
    {{!-- 傳進(jìn)來(lái)的數(shù)組必須手動(dòng)循環(huán) --}}
    {{for #data}}
        <h3>{{:title}}</h3>
        <p>{{:text}}</p>
    {{/for}}
    {{!-- 
        <h3>{{*:extraData.title}}</h3>
        <p>{{*:extraData.text}}</p>
    --}}
</script>

傳入的模板數(shù)據(jù)

    var condition = {
        'case': 2,
        'data': {
            'title': 'this is first case',
            'text': 'case one text'
        },
        'data1': [
            {
                'title': 'i am outer fisrt title',
                'text': 'it is me,diffrent light'
            },
            {
                'title': 'i am outer second title',
                'text': 'it is me,diffrent light'
            }
        ]
    };

而引入外部全局對(duì)象或者數(shù)組瓶竭,發(fā)現(xiàn)循環(huán) /遍歷不了督勺,過(guò)后再研究

9、使用{{converters:value}}把value轉(zhuǎn)換成所需要的格式

當(dāng)后端給我們返回的數(shù)據(jù)格式跟頁(yè)面所以展示的格式不一樣的時(shí)候斤贰,我們就需要對(duì)數(shù)據(jù)進(jìn)行轉(zhuǎn)換智哀。比如說(shuō)后端返回時(shí)間的毫秒數(shù),可是頁(yè)面卻要顯示 年-月-日的格式或者是后端返回小寫的字符荧恍,頁(yè)面卻要顯示成大寫的字符瓷叫,這個(gè)時(shí)候轉(zhuǎn)換器就派上用場(chǎng)了。
jsrender提供了api $.views.converters()來(lái)注冊(cè)轉(zhuǎn)換方法块饺。

    <script type="text/x-jsrender" id="j-myPersonalInfoTpl">
        <div>
            <h3>{{upper:name}}</h3>
            <p>{{:age}}</p>
        </div>
    </script>

    $.views.converters({
        upper: function(val){
            return val.toUpperCase();
        }
    })
    
    //使用jQuery選擇器獲取script標(biāo)簽聲明的jsrender模板并傳入數(shù)據(jù)跟一些方法渲染模板
    var myPersonalTpl = $("#j-myPersonalInfoTpl").render(info);
10赞辩、使用{{:~helper(value)}}對(duì)傳入的參數(shù)value做處理

當(dāng)我們拿到的數(shù)據(jù)不符合展示的需求是,我們需要對(duì)數(shù)據(jù)進(jìn)行處理授艰,那么我們可以使用輔助函數(shù),把原始當(dāng)參數(shù)傳入世落,返回我們需要的數(shù)據(jù)淮腾。
jsrender提供了$.views.helper()方法來(lái)注冊(cè)輔助函數(shù)。并使用~當(dāng)前綴來(lái)調(diào)用輔助函數(shù)屉佳。

<script type="text/x-jsrender" id="j-myPersonalInfoTpl">
        <div>
            <h3>{{:~hello(firstName, lastName)}}</h3>
            <p>{{:age}}</p>
        </div>
</script>

    var info = {
        firstName: 'alice',
        lastName: 'Jogh',
        age: 18
    };

    $.views.helpers({
        hello: function(fisrtName, lastName){
            return 'Hello ' + fisrtName + ' ' + lastName;
        }
    })
    
    //使用jQuery選擇器獲取script標(biāo)簽聲明的jsrender模板并傳入數(shù)據(jù)渲染模板
    var myPersonalTpl = $("#j-myPersonalInfoTpl").render(info);

二谷朝、常用API
1. $.templates()

$.templates()方法是用來(lái)注冊(cè)或者編譯模板的,使用的情況有以下幾種武花。

  • 把html字符串編譯編譯成模板圆凰。
  • 獲取使用script標(biāo)簽聲明的模板,并返回一個(gè)模板對(duì)象
  • 把html字符串或者在script標(biāo)簽中聲明的模板注冊(cè)成命名模板
  • 獲取之前就存在的命名模板
  • 在nodejs中体箕,根據(jù)文件路徑獲取一個(gè)模板對(duì)象

我們正常使用的方式就是使用$.templates()方法把html字符串編譯成模板专钉,返回一個(gè)模板對(duì)象,然后調(diào)用該對(duì)象的render方法并傳入數(shù)據(jù)累铅,就可以得到一份完整的html字符串代碼跃须。比如:

var info = {
        name: 'alice',
        age: 18
    };
   
    //獲取模板
    var jsRenderTpl = $.templates('<div><h3>{{:name}}</h3><p>{{:age}}</p></div');
    //模板與數(shù)據(jù)結(jié)合
    var finalTpl = jsRenderTpl.render(info);
    //也可以這樣用 jsRenderTpl(info);

或者我們也可以給模板定義一個(gè)名稱

    //定義一個(gè)命名模板
    $.templates('myPersonalInfoTpl', '<div><h3>{{:name}}</h3><p>{{:age}}</p></div');
    //模板與數(shù)據(jù)結(jié)合
    var finalTpl = $.render.myPersonalInfoTpl(info);

當(dāng)然,我們也可以把html字符串單獨(dú)寫在script標(biāo)簽中娃兽,然后根據(jù)id來(lái)獲取

<script type="text/x-jsrender" id="j-myPersonalInfoTpl">
    <div>
        <h3>{{:name}}</h3>
        <p>{{:age}}</p>
    </div>
</script>
</body>
<script src="../lib/jquery-1.11.2.min.js"></script>
<script src="../lib/jsrender.js"></script>
<script>
(function(jq, g) {
    //定義一個(gè)命名模板
    $.templates('myPersonalInfoTpl', '#j-myPersonalInfoTpl');
    //模板與數(shù)據(jù)結(jié)合
    var finalTpl = $.render.myPersonalInfoTpl(info);
    $('.box').html(finalTpl);

})(jQuery, window)
</script>

更想當(dāng)然菇民,還可以在一個(gè)templates()方法里面注冊(cè)多個(gè)命名模板

//定義一個(gè)命名模板
    $.templates({
        'myPersonalInfoTpl': '#j-myPersonalInfoTpl',
        'externalTpl': '<p>this is externalTpl</p>'
    });
    //模板與數(shù)據(jù)結(jié)合
    var finalTpl = $.render.myPersonalInfoTpl(info);
    var externalTpl = $.render.externalTpl();

    $('.box').html(finalTpl).append(externalTpl);

還可以指定一些只供這個(gè)模板使用的一些方法

    <script type="text/x-jsrender" id="j-myPersonalInfoTpl">
        <div>
            <h3>{{upper:~append(name, ' stev')}}</h3>
            <p>{{:age}}</p>
        </div>
    </script>

    //定義一個(gè)命名模板,并指定只供這個(gè)模板使用的轉(zhuǎn)換方法與輔助方法
    $.templates("myPersonalInfoTpl", {
        markup: "#j-myPersonalInfoTpl",
        converters: {
            upper: function(val) {
                return val.toUpperCase();
            }
        },
        helpers: {
            append: function(a, b) {
                return a + b;
            }
        }
    });
    //模板與數(shù)據(jù)結(jié)合
    var finalTpl = $.render.myPersonalInfoTpl(info);
2、渲染模板的render()方法

當(dāng)我們使用$.templates()方法注冊(cè)一個(gè)模板對(duì)象時(shí),最后還是需要把模板對(duì)象跟數(shù)據(jù)結(jié)合得到最終的html字符串的第练,render()的使用方式有以下三種

  1. 當(dāng)模板對(duì)象myPersonalTpl是使用$.templates()注冊(cè)的模板時(shí)阔馋,只能使用myPersonalTpl.render(data)的方式來(lái)渲染模板
    //定義一個(gè)匿名模板
    var myPersonalTpl = $.templates("#j-myPersonalInfoTpl");
    //模板與數(shù)據(jù)結(jié)合
    var finalTpl = myPersonalTpl(info);
  1. 當(dāng)模板對(duì)象myPersonalTpl是以命名模板的方式注冊(cè)時(shí),需要使用$.render.myPersonalTpl(data)或者$.render['myPersonalTpl'](data)的方式來(lái)渲染模板
//定義一個(gè)命名模板
    $.templates("myPersonalInfoTpl","#j-myPersonalInfoTpl");
    //模板與數(shù)據(jù)結(jié)合
    var finalTpl = $.render.myPersonalInfoTpl(info);
  1. 當(dāng)使用jQuery娇掏、并且模板是在script標(biāo)簽中聲明時(shí)垦缅,還可以直接使用$("#personTemplate").render(data),并不需要調(diào)用$.templates()方法來(lái)注冊(cè)模板驹碍。
var myHelpers = {
        upper: function(val){
            return val.toUpperCase();
        }
    }
    //使用jQuery選擇器獲取script標(biāo)簽聲明的jsrender模板并傳入數(shù)據(jù)跟一些方法渲染模板
    var myPersonalTpl = $("#j-myPersonalInfoTpl").render(info, myHelpers);

    $('.box').html(finalTpl);

我們還可以在渲染模板的同時(shí)壁涎,傳入一些函數(shù)供模板使用

$.render.myTmpl(data, helpersOrContext)
3、 使用$.views.helpers()方法注冊(cè)輔助函數(shù)

當(dāng)我們拿到的數(shù)據(jù)跟頁(yè)面顯示的數(shù)據(jù)有差別志秃、或者是我們需要把原數(shù)據(jù)轉(zhuǎn)化成其他格式的數(shù)據(jù)時(shí)怔球,我們使用使用一些輔助函數(shù)來(lái)實(shí)現(xiàn),jsrender提供了兩種方式浮还,一種是helper函數(shù)竟坛,一種是converters轉(zhuǎn)換器。我們先講輔助函數(shù)helper钧舌。helper方法是以 "~" 為前綴作為方法的標(biāo)示担汤,在加上方法名,然后把值當(dāng)參數(shù)傳進(jìn)去

//example
{{:~myHelperValue}}
{{:~myHelperFunction(name, title)}}
{{for ~myHelperObject.mySortFunction(people, "increasing")}} ... {{/for}}

輔助函數(shù)helper注冊(cè)方式有以下三種:

  • 使用$.views.helpers()注冊(cè)全局的helper方法洼冻。
    當(dāng)我們需要一些比較通用的方法時(shí)崭歧,可以提取出來(lái)寫到公用的js文件中去,以后就不用重新寫一遍了撞牢。
    <div id="box"></div>
    <script id="myPersonInfo" type="text/x-jsrender">
        <h3>my name is {{:~concat(firstName, lastName)}}</h3>
        <p>{{:~util.addPrefix(age)}}</p>
    </script>
    <script>
        var info = {
            firstName: 'bolin',
            lastName: 'liao',
            age: 18
        };

        // 定義全局的helper方法
        $.views.helpers({
            concat: function(first , last){
                return first + '  ' + last;
            },
            util: {
                prefix: 'age is  ',
                addPrefix: function(age){
                    return this.prefix + age;
                }
            }
        });

        var html = $('#myPersonInfo').render(info);
        $('#box').html(html);
    </script>
  • 寫局部的輔助方法
    當(dāng)我們的某一個(gè)頁(yè)面有多處使用一個(gè)輔助方法率碾,但是又不夠通用,不必寫到common文件去時(shí)屋彪,我們可以寫只供這個(gè)頁(yè)面使用的輔助方法所宰。
        // 定義局部的helper方法
        var myHelper = {
            concat: function(first , last){
                return first + '  ' + last;
            },
            util: {
                prefix: 'age is  ',
                addPrefix: function(age){
                    return this.prefix + age;
                }
            }
        }

        //并在渲染模板的時(shí)候把myHelper當(dāng)做參數(shù)傳進(jìn)去
        var html = $('#myPersonInfo').render(info, myHelper);
  • 只給特定的模板寫輔助函數(shù),其實(shí)也就是在定義模板的時(shí)候把helper傳進(jìn)去
        //注冊(cè)一個(gè)命名模板畜挥,并指定helper方法
        $.templates({
            myPersonInfo: {
                markup: '#myPersonInfo',
                helpers: {
                    concat: function(first , last){
                        return first + '  ' + last;
                    },
                    util: {
                        prefix: 'age is  ',
                        addPrefix: function(age){
                            return this.prefix + age;
                        }
                    }
                }
            }
        });

        //渲染模板,命名模板只能使用$.render調(diào)用
        var html = $.render.myPersonInfo(info);
        $('#box').html(html);
4仔粥、使用$.views.converters()注冊(cè)轉(zhuǎn)換器

在jsrender中,轉(zhuǎn)換器主要是方便對(duì)數(shù)據(jù)或表達(dá)式的的結(jié)果進(jìn)行處理或者格式化蟹但,jsrender本身自帶了三個(gè)轉(zhuǎn)換器躯泰,比如:

{{html:'<b>bolin</b>'}} //- 對(duì)html標(biāo)簽進(jìn)行編碼,原樣輸出 :<b>bolin</b>
{{>'<b>bolin</b>'}} //同上矮湘,html的別名
{{url:"<_>_\"_'"}} // 對(duì)特殊字符進(jìn)行編碼 基本是 <,>,",'...    :%3C_%3E_%22_'
{{attr:value}} //對(duì)標(biāo)簽的屬性值進(jìn)行編碼,也是字符的轉(zhuǎn)換
& → &< → <> → >\x00 → ?' → '" → "` → `= → =

當(dāng)然斟冕,僅僅是這三個(gè)轉(zhuǎn)換時(shí)不夠用的,jsrender提供了自定義轉(zhuǎn)換器的方法缅阳。$.views.converters()磕蛇。比如我想要定義一個(gè)時(shí)間格式化的轉(zhuǎn)換器跟大小寫轉(zhuǎn)換的轉(zhuǎn)換器景描。

    <script id="myPersonInfo" type="text/x-jsrender">
        <h3>{{upper: 'my name is ' + name}}</h3>
        <p>now is {{dateFormat: time}}</p>
    </script>
    <script>
        var info = {
            name: 'bolin liao',
            time: new Date()
        };

        //注冊(cè)全局轉(zhuǎn)化器
        $.views.converters({
            dateFormat: function(val){

                var time = new Date();

                time.setTime(val || 0); //設(shè)置需要格式化的時(shí)間
                return (time.getMonth() + 1) + '月' + time.getDate() + '日';
            },
            upper: function(val){
                console.log(val);
                return val.toUpperCase();
            }
        });

        //渲染模板,命名模板只能使用$.render調(diào)用
        var html = $('#myPersonInfo').render(info);
        $('#box').html(html);
    </script>

你會(huì)發(fā)現(xiàn),其實(shí)轉(zhuǎn)換器跟輔助函數(shù)差不多嘛秀撇,只是使用的方法不一樣而已超棺。雖然實(shí)現(xiàn)都差不多,但還是有點(diǎn)區(qū)別的呵燕,轉(zhuǎn)換器也分全局定義跟局部定義棠绘,局部定義的轉(zhuǎn)換只要把模板當(dāng)做參數(shù)傳進(jìn)去就好了,所定義的轉(zhuǎn)換器只能在此模板中生效再扭。

    <script id="myPersonInfo" type="text/x-jsrender">
        <h3>{{upper: 'my name is ' + name}}</h3>
        <p>now is {{dateFormat: time}}</p>
    </script>
     //myText中的轉(zhuǎn)換器不起作用氧苍,并報(bào)錯(cuò)
    <script id="myText" type="text/x-jsrender">
        <h3>{{upper: title}}</h3>
        <p>now is {{dateFormat: time}}</p>
    </script>

    //注冊(cè)局部轉(zhuǎn)換器,指定myPersonInfo模板可用
    $.views.converters({
        dateFormat: function(val){

            var time = new Date();

            time.setTime(val || 0); //設(shè)置需要格式化的時(shí)間
            return (time.getMonth() + 1) + '月' + time.getDate() + '日';
        },
        upper: function(val){
            console.log(val);
            return val.toUpperCase();
        }
    }, $.templates('#myPersonInfo'));

    //渲染模板,命名模板只能使用$.render調(diào)用
    var html = $('#myPersonInfo').render(info);
    $('#box').html(html);

    //不可用
    var html = $('#myText').render(text);
    $('#box').append(html);

除了使用在{{convert:}}標(biāo)簽中之外泛范,還可以在{{if}},{{for}}標(biāo)簽中使用让虐,可以說(shuō)是在任何標(biāo)簽中使用,語(yǔ)法如下:

{{someTag ... convert=...}}
//if語(yǔ)句
{{if convert='inList' item itemList}}...{{/if}}
//for語(yǔ)句
{{for people convert='even'}}

在其他標(biāo)簽中使用時(shí)罢荡,只是需要把轉(zhuǎn)化器賦值給convert赡突,當(dāng)然也可以把輔助方法賦值給convert,比如

{{:name convert=~hlp.bold}}

但是呢区赵,如果你使用{{>name convert=~hlp.bold}}的話惭缰,是會(huì)報(bào)錯(cuò)的,還是老老實(shí)實(shí)使用輔助方法的形式笼才,除了以上的使用方式之外漱受,convert還提供了一些比較好的功能,比如使用this.tagCxt.props可以獲取到標(biāo)簽中定義的屬性患整,使用this.tagCxt.view.data可以獲取到標(biāo)簽中所有的變量拜效。

    <script id="myPersonInfo" type="text/x-jsrender">
        <h3>{{concat: first last age desc="123" myAttr="test"}}</h3>
    </script>

    //注冊(cè)局部轉(zhuǎn)換器,指定myPersonInfo模板可用
    $.views.converters({
        concat: function(){

            console.log(this.tagCtx.props);//Object {desc: "123", myAttr: "test"}

            console.log(this.tagCtx.view.data);//Object {first: "bolin", last: " liao", age: 18}
            
        }
    });
5各谚、使用$.views.tags()注冊(cè)自定義標(biāo)簽

jsrender 提供了一些內(nèi)置的標(biāo)簽,但往往是不夠用的到千,所以提供了$.views.tags()方法來(lái)定義一些比較靈活的標(biāo)簽昌渤。自定義標(biāo)簽比較靈活,能控制憔四、訪問(wèn)的元素也比較多膀息,比如寫在該標(biāo)簽里面的args、props了赵、甚至整個(gè)view model對(duì)象里面的全部數(shù)據(jù)潜支。使用$.views.tags注冊(cè)自定義標(biāo)簽的語(yǔ)法有以下四種

  1. $.views.tags('myTag', tagOptions); 當(dāng)自定義的標(biāo)簽需要要模板與方法時(shí),一般會(huì)使用這種方式來(lái)注冊(cè)自定義標(biāo)簽柿汛,我們可以在render方法里面處理參數(shù)或者屬性冗酿,然后渲染模板,this.tagCxt對(duì)象下,有當(dāng)前view model的數(shù)據(jù)供訪問(wèn)
Paste_Image.png
    <script id="myPersonInfo" type="text/x-jsrender">
        {{myPersonInfo name age addPrefix=false /}}
    </script>

    var info = {
        name: 'bolin',
        age: 20
    };
    
    $.views.tags({
        'myPersonInfo': {
            render: function(){
                //這里可以獲取到自定義標(biāo)簽里面的屬性或者參數(shù)
                //可以使用 this.tagCtx.args, this.tagCtx.props, this.tagCtx.view.data 訪問(wèn) view model里面的任何數(shù)據(jù)
                
                console.log(this.tagCtx);
                if (this.tagCtx.props.addPrefix) {

                    return '<h3>Hello, ' + this.tagCtx.args[0] + '</h3><p>' + this.tagCtx.args[1] + '</p>';
                }else{

                    //使用this.tagCtx.render({name:'lbl'})來(lái)渲染定義的模板裁替,并把模板需要的數(shù)據(jù)傳進(jìn)去
                    return this.tagCtx.render({name:'lbl'}) + '<h3>' + this.tagCtx.args[0]  +'</h3><p>' + this.tagCtx.args[1] + '</p>';
                }
            },
            template: '<h2>{{:name}}</h2>'
        }
    });

當(dāng)然项玛,如果我們不需要模板,那么就只定義一個(gè)方法就好了弱判。

    $.views.tags('myPersonInfo', function(){
                
        if (this.tagCtx.props.addPrefix) {

            return '<h3>Hello, ' + this.tagCtx.args[0] + '</h3><p>' + this.tagCtx.args[1] + '</p>';
        }
    });

當(dāng)然襟沮,也可能你的自定義標(biāo)簽只需要模板,并不需要方法來(lái)處理昌腰,那么也是沒(méi)問(wèn)題的开伏,當(dāng)然模板里面也是可以訪問(wèn)各種參數(shù)、屬性的,只是需要使用~tag.tagCtx對(duì)象訪問(wèn)遭商。

    <script id="myPersonInfo" type="text/x-jsrender">
        {{myPersonInfo name age=age /}}
    </script>

    $.views.tags('myPersonInfo', {
        template: '<h3>{{:~tag.tagCtx.args[0]}}</h3><p>{{:~tag.tagCtx.props.age}}</p>'
    });

    //也可以這樣
    <script id="myPersonInfo" type="text/x-jsrender">
        {{myPersonInfo name age addPrefix=false}}
            <h3>{{:~tag.tagCtx.args[0]}}</h3>
            <p>no info</p>
        {{/myPersonInfo}}
    </script>

    //也可以使用tag.tagCtx.content獲取到自定義標(biāo)簽中的內(nèi)容
    $.views.tags('myPersonInfo', {
        template: '{{if ~tag.tagCtx.props.addPrefix == true}}\
                    <h3>Hello, {{:~tag.tagCtx.args[0]}}</h3>\
                    <p>{{:~tag.tagCtx.props.age}}</p>\
                    {{else tmpl=~tag.tagCtx.content}}\
                    {{:~tag.tagCtx.content}}\
                    {{/if}}'
    });

  //或者這樣
    <script id="teamTemplate" type="text/x-jsrender">
      <p><b>{{:title}}</b></p>
      <ul>
        {{range members start=1 end=2}} 
          <li>
            {{:name}}
          </li>
        {{/range}}
      </ul> 
    </script>

    $.views.tags("range", {
        template: 
            "{{for ~tag.tagCtx.args[0]}}" +
            "{{if #index >= ~tag.tagCtx.props.start && #index <= ~tag.tagCtx.props.end}}" +
            "{{include tmpl=~tag.tagCtx.content/}}" +
            "{{/if}}" +
            "{{/for}}"
    });

當(dāng)然也可以為某個(gè)模板注冊(cè)私有的自定義標(biāo)簽

    $.views.tags({
      myTag1: ...,
      myTag2: ...
    }, parentTemplate);
6固灵、使用$.views.settings.debugMode()開啟調(diào)試模式

當(dāng)我們使用jsrender寫代碼時(shí),難免會(huì)報(bào)一些錯(cuò)株婴,然后直接在控制臺(tái)拋出錯(cuò)誤異常怎虫。但是我們想對(duì)錯(cuò)誤信息做一些處理,比如直接把異常輸出到頁(yè)面困介,或者自定義錯(cuò)誤信息為字符竄大审,或者拋出錯(cuò)誤的時(shí)候,先調(diào)用函數(shù)處理再拋出錯(cuò)誤座哩。jsrender提供了$.views.settings.debugMode()傳入不同的參數(shù)來(lái)改變?nèi)謷伋霎惓5那闆r徒扶。

  1. false 內(nèi)容不會(huì)被渲染并且在控制臺(tái)拋出異常 (默認(rèn))
  2. true 拋出的異常會(huì)渲染在頁(yè)面中,在控制臺(tái)中沒(méi)有異常拋出
  3. "string" 字符串會(huì)替代錯(cuò)誤信息渲染在頁(yè)面中根穷,在控制臺(tái)沒(méi)有異常拋出
  4. function 在異常拋出之前姜骡,會(huì)先經(jīng)過(guò)傳入的方法處理,如果此方法沒(méi)有return屿良,那么就會(huì)把錯(cuò)誤信息渲染到頁(yè)面中圈澈,如果有return,那么頁(yè)面中就會(huì)渲染return 的信息
    $.views.settings.debugMode(function(err){

        var errMsg = '';

        if(err){

            errMsg = 'here has error the err is ' + err;
        }
        return errMsg;
    });

當(dāng)然尘惧,除了為全局處理錯(cuò)誤信息之外康栈,也可以使用onError屬性為某一個(gè)標(biāo)簽定義錯(cuò)誤信息,如果你已經(jīng)在全局設(shè)置了處理錯(cuò)誤信息的方法喷橙,并且傳入的參數(shù)有返回值(上面提到的3跟4)啥么,onError屬性是不會(huì)起作用的

    {{:address.street onError="Address unavailable"}}
    {{for phones() onError="<em>No phones</em>"}}
    {{:address.street onError=name + " has no address"}}
    {{:address.street onError=~errorMessages(1, name, 'address')}}
    {{myCustomTag ... onError=""}}

    $.views.settings.debugMode("this is global err"); //會(huì)覆蓋上面的onError屬性
    or
    $.views.settings.debugMode(function(err){

        var errMsg = '';

        if(err){

            errMsg = 'here has error the err is ' + err;
        }
        return errMsg; //如果return 就會(huì)渲染這個(gè),如果不return 就會(huì)渲染onError屬性的值
    });

當(dāng)我們使用render()方法渲染模板的時(shí)候贰逾,我們想查看某個(gè)對(duì)象或者某個(gè)屬性的值悬荣,但是并不能在模板中打斷點(diǎn),jsrender提供以下方式在渲染模板的過(guò)程中輸出對(duì)象或者屬性的值

{{dbg expression/}}  tag
{{dbg: expression}}   convert
{{:~dbg(expression)}}  helper

以上的三種方式都會(huì)把值渲染在頁(yè)面疙剑,并在控制臺(tái)中輸出值氯迂,但是呢践叠,如果值是一個(gè)對(duì)象,那么就會(huì)輸出字符串囚戚,比如

JsRender dbg breakpoint: [object Object]

我們需要看對(duì)象里面有什么屬性酵熙,但是卻給我們輸出的是字符串,顯然不方便調(diào)試驰坊,所以我們可以重寫這個(gè)調(diào)試標(biāo)簽,讓輸入值原樣輸出

    //重寫dbg調(diào)試模式
    $.views.helpers({
        dbg: function(val){

            try {
                console.log(val);
                return val;
            }catch (e) {
                console.log(e);
            }
        }
    });

    $.views.converters({
        dbg: function(val){

            try {
                console.log(val);
                return val;
            }catch (e) {
                console.log(e);
            }
        }
    });

    $.views.tags('dbg', function(val){

        try {
            console.log(val);
            return val;
        }catch (e) {
            console.log(e);
        }
    });

這樣重寫之后匾二,控制臺(tái)輸出變成這樣,就比較方便調(diào)試了

Object {name: "bolin", age: 20}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市拳芙,隨后出現(xiàn)的幾起案子察藐,更是在濱河造成了極大的恐慌,老刑警劉巖舟扎,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件分飞,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡睹限,警方通過(guò)查閱死者的電腦和手機(jī)譬猫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)羡疗,“玉大人染服,你說(shuō)我怎么就攤上這事∵逗蓿” “怎么了柳刮?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)痒钝。 經(jīng)常有香客問(wèn)我秉颗,道長(zhǎng),這世上最難降的妖魔是什么送矩? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任蚕甥,我火速辦了婚禮,結(jié)果婚禮上栋荸,老公的妹妹穿的比我還像新娘梢灭。我一直安慰自己,他們只是感情好蒸其,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著库快,像睡著了一般摸袁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上义屏,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天靠汁,我揣著相機(jī)與錄音蜂大,去河邊找鬼。 笑死蝶怔,一個(gè)胖子當(dāng)著我的面吹牛奶浦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播踢星,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼澳叉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了沐悦?” 一聲冷哼從身側(cè)響起成洗,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎藏否,沒(méi)想到半個(gè)月后瓶殃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡副签,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年遥椿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淆储。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡冠场,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出遏考,到底是詐尸還是另有隱情慈鸠,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布灌具,位于F島的核電站青团,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏咖楣。R本人自食惡果不足惜督笆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望诱贿。 院中可真熱鬧娃肿,春花似錦、人聲如沸珠十。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)焙蹭。三九已至晒杈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間孔厉,已是汗流浹背拯钻。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工帖努, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人粪般。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓拼余,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親亩歹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子匙监,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容