詳解Vue 動態(tài)添加模板的幾種方法
以下方法只適用于 Vue1.0 版本,推薦系數(shù)由高到低排列。
通常我們會在組件里的 template 屬性定義模板,或者是在 *.vue 文件里的 template 標(biāo)簽里寫模板。但是有時候會需要動態(tài)生成模板的需求,例如讓用戶自定義組件模板,或者設(shè)置組件的布局。
例如要做一個類 select 的組件,用戶傳入 options 數(shù)據(jù),通過 value prop 獲取選中值,最基本的原型如下。
Vue.component('XSelect', {
template: `
<div class="select">
<input :value="value" readonly />
<div
class="option"
v-for="option in options"
@click="value = option.value">
<span v-text="option.label"></span>
</div>
</div>`,
props: ['value','options']
})
如果此時需要增加一個 API 支持讓用戶自定義 option 部分的模板。此處用 slot 并不能解決問題。
通過 $options.template 修改
通過打印組件對象可以獲得一個信息,在 $options 里定義了一個 template 屬性,寫在 template 標(biāo)簽里的模板,最終編譯后也會在 $options.template 里。通過文檔的生命周期 可以得知,在 created 的時候, 實(shí)例已經(jīng)結(jié)束解析選項(xiàng), 但是還沒有開始 DOM 編譯 也就是說,如果用戶通過 prop 的數(shù)據(jù)我們可以獲得,但是模板其實(shí)還沒有渲染成 DOM。經(jīng)過測試,在 created 修改 this.$options.template 是可以改變最終生成的 DOM 的,同時也能拿到 props 的內(nèi)容。
那么我們可以修改下代碼,使其支持自定義模板
Vue.component('XSelect', {
props: [
'value',
'options',
{
name: 'template',
default:'<span v-text="option.label"></span>'
}
],
created() {
varoptionTpl =this.template
this.$options.template =`
<div class="select">
<input :value="value" readonly />
<div
class="option"
v-for="option in options"
@click="value = option.value">
${optionTpl}
</div>
</div>`
}
})
用戶使用是就可以傳入模板了
<x-select
:value.sync="value"
template="<span>標(biāo)簽: {{ option.label }}, 值: {{ option.value }}</span>"
:options="[
{value: 1, label: 'a'},
{value: 2, label: 'b'},
{value: 3, label: 'c'}
]">
</x-select>
可能存在的問題
我們知道 Vue 在內(nèi)部幫我們做了許多優(yōu)化,但是在這里可能會由于某些優(yōu)化導(dǎo)致動態(tài)拼接的模板無法渲染成功。例如這里我們不使用 v-for 而是手工遍歷 options 生成需要的 HTML
consttpl = options.map(opt =>`<div>${this.optionTpl}</div>`)
this.$options.template =`
<div class="select">
<input :value="value" readonly>
${tpl}
</div>`
這里會導(dǎo)致一個 BUG,如果一個頁面有多個 x-select 組件,并且 options 長度不一樣,會導(dǎo)致長的 options 的組件后面幾個選項(xiàng)渲染不出來。究其原因是 Vue 會幫我們緩存模板編譯結(jié)果。翻看代碼可以找到 vue/src/instance/internal/lifecycle.js 里有做優(yōu)化,同時提供的 _linkerCachable 本意是給 內(nèi)聯(lián)模板 使用。我們可以通過設(shè)置 this.$options._linkerCachable = false 達(dá)到我們的目的。
這樣我們就可以開發(fā)讓用戶自定義布局的組件了,用戶傳入布局參數(shù),通過手工拼接模板,設(shè)置了 _linkerCachable = false 也不會被緩存。
通過 $options.partials 動態(tài)添加 partial
使用 partials 也能達(dá)到添加自定義模板的目的,但是通常的做法是要全局注冊 partial,這么做并不優(yōu)雅。 vue-strap 就是這么做的。如果重名了會被覆蓋(初次渲染不會,但是數(shù)據(jù)更新重新渲染 DOM 時就會被覆蓋)。
通過文檔我們知道可以在組件內(nèi)部通過 partials 屬性注冊局部的 partial,因此自然而然也可以在 this.$options.partials 去動態(tài)添加了。
Vue.component('XSelect', {
template: `
<div class="select">
<input :value="value" readonly />
<div
class="option"
v-for="option in options"
@click="value = option.value">
<partial name="option"></partial>
</div>
</div>`,
props: ['template','value','options'],
partials: {
option: '<span v-text="option.label"></span>'
},
created() {
if(this.template) {
this.$options.partials.option =this.template
}
}
})
用 interpolate 渲染模板
這種方式就略顯、、、,而且效率最差。 interpolate 也是我最開始做動態(tài)渲染模板想到的方式,不推薦使用。
Vue.component('XSelect', {
template: `
<div class="select">
<input :value="value" readonly />
<div
class="option"
v-for="option in options"
@click="value = option.value"
v-html="renderOption(option)">
</div>
</div>`,
props: [
'value',
'options',
{
name: 'template',
default:'<span v-text="option.label"></span>'
}
],
methods: {
renderOption(option) {
this.option = option
returnthis.$interpolate(this.template)
}
}
})
Vue2.0
目前并沒有找到合適的解決方案。2.0 的 Vue 將 compile 工作提前,并且 compiler 也是單獨(dú)一個包(除非你直接引用的是 vue.js 文件,包含 compiler 和 runtime,那么第一種方法是適用的),那么并不能動態(tài)的生成模板。除非用戶傳入的是 render 能識別的 DOM tree。
按照這樣的思路,其實(shí)可以讓用戶傳入的模板預(yù)先編譯好,傳入到組件內(nèi),拼接 DOM tree 看起來也能解決問題。那么可以這么玩。
看看就好, 性能太渣
首先要安裝 Vue JSX 的 相關(guān)插件
組件
Vue.component({
name: 'XSelect',
render(h) {
// 這里獲得的 this.template 其實(shí)是一個函數(shù),調(diào)用該函數(shù)返回 DOM
// 因此這里的關(guān)鍵代碼是拼接一個新的函數(shù),接受 `option` 參數(shù)以及上下文
// 使用 new Function 創(chuàng)建一個新函數(shù)
return(
<divclass="select">
<inputvalue={this.value}readonly/>
{
this.options.map(option =>
<div
on-click={() => this.$emit('input', option.value) }
class="option">
{ new Function('option', 'return ' + this.template)(option)(h) }
</div>
)
}
</div>)
},
props: ['template', 'value', 'options']
})
入口文件
newVue({
el: '#app',
data () {
return{
value: ''
}
},
created() {
// 初始化需要傳入的模板,這里會被 Vue 的 JSX 插件轉(zhuǎn)成 DOM tree
this.template = h =><span>標(biāo)簽: { option.label }, 值: { option.value }</span>
},
render(h) {
return(
<x-select
v-model="value"
:template="template"
:options="[
{value: 1, label: 'a'},
{value: 2, label: 'b'},
{value: 3, label: 'c'}
]">
</x-select>)
}
})
綜上,在 Vue 1.x 里不存在 預(yù)編譯 的概念,所以想動態(tài)修改模板還是有許多方式的,甚至還可以結(jié)合 <slot></slot> 取到 slot 里的內(nèi)容拼接進(jìn)模板里。但 2.0 就麻煩了,并找不到理想的方法。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
vue+elementUI實(shí)現(xiàn)點(diǎn)擊按鈕互斥效果
這篇文章主要為大家詳細(xì)介紹了vue+elementUI實(shí)現(xiàn)點(diǎn)擊按鈕互斥效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
Nginx部署Vue.js前端項(xiàng)目的實(shí)現(xiàn)
本文主要介紹了Nginx部署Vue.js前端項(xiàng)目指南,幫助您實(shí)現(xiàn)從開發(fā)到線上部署的平滑過渡,確保用戶能夠獲得最佳的訪問體驗(yàn),感興趣的可以了解一下2024-09-09
離線搭建vue環(huán)境運(yùn)行項(xiàng)目完整步驟
這篇文章主要給大家介紹了關(guān)于離線搭建vue環(huán)境運(yùn)行項(xiàng)目的相關(guān)資料,文中通過實(shí)例代碼以及圖文介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用vue具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2023-06-06
vue element中axios下載文件(后端Python)
這篇文章主要介紹了vue element中axios下載文件(后端Python)的實(shí)例代碼,非常不錯,具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2019-05-05
vue表單綁定實(shí)現(xiàn)多選框和下拉列表的實(shí)例
本篇文章主要介紹了vue表單綁定實(shí)現(xiàn)多選框和下拉列表的實(shí)例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
vue實(shí)現(xiàn)一個懶加載的樹狀表格實(shí)例
這篇文章主要介紹了vue實(shí)現(xiàn)一個懶加載的樹狀表格實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05

