在 angular
中 能夠經過 ComponentFactoryResolver
來動態建立 component
, 在平時使用 vue
的過程當中也沒有了解到這方面的信息。因而就花時間研究了一下。javascript
Vue
的組件能夠經過兩種方式來聲明,一種是經過 Vue.component
,另一種則是 Single File Components(SFC)
。 html
如下除非特別說明,組件都是全局註冊的vue
首先來看 Vue.component
方式的。java
Vue.component('button-counter',{ data: function () { return { count: 0 } }, template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' });
在上面的代碼中咱們聲明瞭一個叫作 button-counter
的組件。若是在常規狀況下使用的話,只須要在頁面上寫對應的 <button-counter></button-counter>
標籤就夠了。web
那麼經過編程方式怎麼處理呢?vue-cli
在官方文檔中咱們能夠看到,咱們能夠經過 Vue.component('component-name')
的方式來獲取到 組件
。而組件實例又有 $mount
這樣一個方法,官方對於 $mount
的解釋以下:編程
$mount 方法有兩個參數api
{Element | string} [elementOrSelector]
{boolean} [hydrating]
If a Vue instance didn’t receive the
el
option at instantiation, it will be in 「unmounted」 state, without an associated DOM element.vm.$mount()
can be used to manually start the mounting of an unmounted Vue instance.appIf
elementOrSelector
argument is not provided, the template will be rendered as an off-document element, and you will have to use native DOM API to insert it into the document yourself.ideThe method returns the instance itself so you can chain other instance methods after it.
那咱們是否能夠經過這種方式來達到咱們的需求呢?
還不夠!
爲何?
由於 Vue.component
返回的結果是一個 function
!它返回的並非 組件實例,而是一個構造函數。
那到這裏其實咱們就清楚了。 對於 Vue.component
聲明的組件,咱們先經過 Vue.component
獲取它的構造函數,再 new
出一個組件實例,最後 經過$mount
掛載到 html
上。
下面是完整的代碼:
Vue.component("button-counter", { data: function() { return { count: 0 }; }, template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' }); Vue.component("app", { data: function() { return { count: 0 }; }, template: '<div> <h1>App Component</h1><button @click="insert">click to insert new Component</button> <div id="appId"> </div></div>', methods: { insert() { const component = Vue.component("button-counter"); const instance = new component(); instance.$mount("#appId"); } } }); new Vue({ el: "#app" });
https://codepen.io/YoRolling/...
在實際工做中,大部分都是用官方的腳手架 vue-cli
生成項目,用的也是 SFC
這種方式。
咱們的 button-counter
若是用 SFC 方式實現的話應該是這樣子的:
<template> <button v-on:click="count++">You clicked me {{ count }} times.</button> </template> <script> export default { name: "ButtonCounter", data() { return { count: 0 }; } }; </script>
那麼是否能夠經過 export
出的對象來實現咱們的需求呢?
首先咱們來看,在 SFC
中, 咱們在 script 中 export
了一個對象出去,那麼經過這個對象應該是能夠達到要求的。
先來看看 import
以後這個對象是什麼樣子的。
能夠發現 import
獲得的對象要比咱們在 組件中聲明的多了一些屬性和方法。
在 Vue.component
模式中,咱們先獲取到組件的構造函數,而後構造實例,經過實例的一些方法來處理數據和掛載節點。
很顯然,現有數據不能知足咱們。若是咱們能將這個對象轉化成一個組件的構造函數,那咱們就能夠利用上面的方案來實現咱們的需求了。
那麼,究竟須要怎麼轉換呢?
沒錯! 就是 Vue.extend
這個大兄 dei!咱們看下官方的說明。
Create a 「subclass」 of the base Vue constructor. The argument should be an object containing component options.The special case to note here is the
data
option - it must be a function when used withVue.extend()
.
經過傳入一個包含 Component options
的對象, Vue.extend
幫助咱們建立一個 繼承了 Vue constructor
的子類,也就是咱們以前須要的構造函數。
好了,獲得了構造函數,接下來的工做就簡單了 。實例化,而後掛載。
下面就是完整的代碼:
<template> <div id="app"> <div> <img width="25%" src="./assets/logo.png"> </div> <div> <button @click="insert">click me to insert ButtonCounter</button> </div> <div id="container"></div> </div> </template> <script> import ButtonCounter from './components/ButtonCounter'; import Vue from 'vue'; export default { name: 'App', components: { ButtonCounter, }, methods: { insert() { const bcConstructor = Vue.extend(ButtonCounter); const instance = new bcConstructor(); instance.$mount('#container'); }, }, }; </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
在線查看 https://codesandbox.io/s/m59r3547zy
https://codesandbox.io/s/m59r...
Happy Ending。
到這裏,經過編程的方式動態建立組件掛載到HTML 的兩種方式就完成了。
再次感到看文檔的重要性。 🤨
👻 若有錯誤,歡迎你們斧正!