VueJS 如何編譯服務器端遠程模板【異步組件+簡單方法】

說明

有些時候你可能須要從後臺獲取模板,並在前臺在本身編譯,這在用 AngularJS 1.x 的時候彷佛很常見,能夠直接用 ng-include 搞定,在 Vue 1.x 的時候也能夠直接用 partial 搞定。
可是在 Vue 2.x 中,官方取消了 partial 這個 API,根據狀況推薦使用 component 代替,參見這裏html

需求

那我如今有個需求,就是從後臺獲取一個字符串模板(假設裏面包含 v-model 等 vue 指令),模板須要拿到前臺來編譯,那該怎麼實現呢?
(這種需求確實比較少見,可是是存在的。。。)vue

可能的解決方案

官方根據狀況推薦了三種替代 partial 的方式,分別爲:webpack

  1. normal component 常規組件,沒有處於性能關鍵區域(簡單來講就是對性能有較大影響)時使用git

  2. dynamic component 動態組件,根據組件名稱動態綁定時使用github

  3. functional component 函數組件,處於性能關鍵區域時使用web

另外根據文檔中的的狀況來看:api

  1. 異步組件和高級異步組件異步組件) 也可能解決樓主的問題數組

下面來分別分析一下:緩存

常規組件

用法服務器

// 註冊
Vue.component('my-component', {
  template: '<div>A custom component!</div>'
})
// 建立根實例
new Vue({
  el: '#example'
})

這是常規組件的註冊方法
先來看下 Vue 實例的聲明週期圖,摘自官網:

lifecircle

圖中黃色的部分,能夠看出 vue 實例的模板是來自哪裏的?

  1. el 選項提供的選擇器

  2. template 選項提供的字符串 (固然沒有的話會編譯組件的 outerHTML)

還有其餘可能嗎?沒有了。。。並且兩個選項的提供都是在 beforeMount 以前,這說明啥?這說明這兩個選項一旦提供就不能改了!因此在聲明組件的時候就必須提供這兩個選項來做爲編譯的模板,並且是不能更改的,那我若是想要異步拿到模板去編譯顯然不可能。
so,常規組件,卒。

動態組件

動態組件簡單來講是使用 <component :is="currentView"></component> 讓多個組件使用一個掛載點,currentView 能夠直接是註冊進來的組件,如:

var vm = new Vue({
  el: '#example',
  data: {
    currentView: 'home'
  },
  components: {
    home: { /* ... */ },
    posts: { /* ... */ },
    archive: { /* ... */ }
  }
})
<component v-bind:is="currentView">
  <!-- 組件在 vm.currentview 變化時改變! -->
</component>

也能夠是選項對象!!

var Home = {
  template: '<p>Welcome home!</p>'
}
var vm = new Vue({
  el: '#example',
  data: {
    currentView: Home
  }
})

從目前瞭解的狀況來看,import 進來的組件通常也都是準備好的,若是想要異步加載可能須要 webpack 的一些功能,暫且先跳過。
那動態組件能夠是選項對象?那選項對象若是是異步的呢?好吧有但願~ 糖糖先記着~

函數組件

這個,看了第一遍文檔,不是太懂,不要緊!找關鍵的地方: API:render 函數

Vue 選項中的 render 函數若存在,則 Vue 構造函數不會從 template 選項或經過 el 選項指定的掛載元素中提取出的 HTML 模板編譯 render 函數。

這裏所說的很清楚,template 或 el 選項最終都會被編譯成 render 函數,那若是有 render 函數的話,就會忽略那兩個選項。仍是要看上面那張圖,render 函數是在 beforeMount 的時候就已經編譯完成的,因此也是不能改變的。
so,函數組件,卒。

異步組件

這裏直接摘官網說明:

在大型應用中,咱們可能須要將應用拆分爲多個小模塊,按需從服務器下載。爲了讓事情更簡單,Vue.js 容許將組件定義爲一個工廠函數,動態地解析組件的定義。Vue.js 只在組件須要渲染時觸發工廠函數,而且把結果緩存起來,用於後面的再次渲染。例如:

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    // Pass the component definition to the resolve callback
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})

能夠看出來,異步組件基本能夠實現樓主的需求,可是上面是 ES5 的寫法,能夠猜一下 ES6 的寫法可能以下:

// 某個vue文件
export default function (resolve, reject) {
  // 遠程加載你的模板
  apiService.then(data => {
    resolve({
      template: data
    })
  }
}

可是這個樓主沒有實際用過,官方寫的 ES5 能夠把選項對象定義成一個工廠方函數,ES6 應該能夠直接返回工廠函數。感興趣的同窗能夠試一下而後告訴樓主。

樓主的方法

樓主沒有用上面的異步組件(其實由於當時看不懂 囧),我在工做中使用的實際上是 動態組件結合 Vue 的 computed 屬性。靈感來源與 vue 論壇:innerhtml-compilation-vue
LinuxBorg 大神在論壇上回答另外一個同窗的提問時提到的,能夠看到下面還有樓主的留言~

computed: {
  dynComponent() {
    const template = this.content ? this.content : '<div>nothing here yet</div>'
    return { 
      template, // use content as template for this component
      props: this.$options.props // re-use current props definitions
    }
}
<component is:="dynComponent" v-bind="$props"/>

即 computed 屬性直接返回一個組件的選項對象,這個選項對象的模板能夠異步獲取,而後配合 動態組件 <component></component> 會幫你編譯來自遠程的模板,又因爲 computed 屬性的響應式特性,遠程模板若是改變的話,就會自動從新編譯咯~

固然樓主用的比這個複雜一些,會涉及到 v-model 等雙向綁定,須要將這個組件再封裝一層而且轉換一下模板,這裏就不細說了。

小結

總之呢,上面分析了在 VueJS 2.x 中編譯遠程模板的可能性,最後得出了兩種方法:

  1. 異步組件,應該是官方的推薦方法

  2. 動態組件 + computed,變通之法,論壇上發現的思路

固然若是有其餘方法歡迎交流,本文若是有不嚴謹不正確的地方也歡迎指出~

本文發自個人blog,原文連接個人blog

相關文章
相關標籤/搜索