前端基礎:Vue 的構造器extend 與手動掛載$mount,不常用但有用

使用場景

我們在寫 Vue.js 時,不論是用 CDN 的方式還是在 Webpack 裡用 npm 引入的 Vue.js,都會有一個根節點,並且創建一個根實例,比如:





Webpack 也類似,一般在入口文件 main.js 裡,最後會創建一個實例:


import Vue from 'vue';
import App from './app.vue';
new Vue({
el: '#app',
render: h => h(App)
});

因為用 Webpack 基本都是前端路由的,它的 html 裡一般都只有一個根節點

,其餘都是通過 JavaScript 完成,也就是許多的 Vue.js 組件(每個頁面也是一個組件)。

有了初始化的實例,之後所有的頁面,都由 vue-router 幫我們管理,組件也都是用 import 導入後局部註冊(也有在 main.js 全局註冊的),不管哪種方式,組件(或頁面)的創建過程我們是無需關心的,只是寫好 .vue 文件並導入即可。這樣的組件使用方式,有幾個特點:

  1. 所有的內容,都是在 #app 節點內渲染的;
  2. 組件的模板,是事先定義好的;
  3. 由於組件的特性,註冊的組件只能在當前位置渲染。

比如你要使用一個組件 <i-date-picker>,渲染時,這個自定義標籤就會被替換為組件的內容,而且在哪寫的自定義標籤,就在哪裡被替換。換句話說,常規的組件使用方式,只能在規定的地方渲染組件,這在一些特殊場景下就比較侷限了,例如:/<i-date-picker>

  1. 組件的模板是通過調用接口從服務端獲取的,需要動態渲染組件;
  2. 實現類似原生 window.alert() 的提示框組件,它的位置是在 下,而非
    ,並且不會通過常規的組件自定義標籤的形式使用,而是像 JS 調用函數一樣使用。

    一般來說,在我們訪問頁面時,組件就已經渲染就位了,對於場景 1,組件的渲染是異步的,甚至預先不知道模板是什麼。對於場景 2,其實並不陌生,在 jQuery 時代,通過操作 DOM,很容易就能實現,你可以沿用這種思路,只是這種做法不那麼 Vue,既然使用 Vue.js 了,就應該用 Vue 的思路來解決問題。對於這兩種場景,Vue.extend 和 vm.$mount 語法就派上用場了。

    用法

    上文我們說到,創建一個 Vue 實例時,都會有一個選項 el,來指定實例的根節點,如果不寫 el 選項,那組件就處於未掛載狀態。Vue.extend 的作用,就是基於 Vue 構造器,創建一個“子類”,它的參數跟 new Vue 的基本一樣,但 data 要跟組件一樣,是個函數,再配合 $mount ,就可以讓組件渲染,並且掛載到任意指定的節點上,比如 body。

    比如上文的場景,就可以這樣寫:

    import Vue from 'vue';
    const AlertComponent = Vue.extend({
    template: '
    {{ message }}
    ',
    data () {
    return {
    message: 'Hello, World!'
    };
    },
    });

    這一步,我們創建了一個構造器,這個過程就可以解決異步獲取 template 模板的問題,下面要手動渲染組件,並把它掛載到 body 下:

    const component = new AlertComponent().$mount();

    這一步,我們調用了 $mount 方法對組件進行了手動渲染,但它僅僅是被渲染好了,並沒有掛載到節點上,也就顯示不了組件。此時的 component 已經是一個標準的 Vue 組件實例,因此它的 $el 屬性也可以被訪問:

    document.body.appendChild(component.$el);

    當然,除了 body,你還可以掛載到其它節點上。

    $mount 也有一些快捷的掛載方式,以下兩種都是可以的:

    // 在 $mount 裡寫參數來指定掛載的節點
    new AlertComponent().$mount('#app');
    // 不用 $mount,直接在創建實例時指定 el 選項
    new AlertComponent({ el: '#app' });

    實現同樣的效果,除了用 extend 外,也可以直接創建 Vue 實例,並且用一個 Render 函數來渲染一個 .vue 文件:

    import Vue from 'vue';
    import Notification from './notification.vue';
    const props = {}; // 這裡可以傳入一些組件的 props 選項
    const Instance = new Vue({
    render (h) {
    return h(Notification, {
    props: props
    });
    }
    });
    const component = Instance.$mount();
    document.body.appendChild(component.$el);

    這樣既可以使用 .vue 來寫複雜的組件(畢竟在 template 裡堆字符串很痛苦),還可以根據需要傳入適當的 props。渲染後,如果想操作 Render 的 Notification 實例,也是很簡單的:

    const notification = Instance.$children[0];

    因為 Instance 下只 Render 了 Notification 一個子組件,所以可以用 $children[0] 訪問到。

    如果你還不理解這樣做的目的,沒有關係,後面小節的兩個實戰你會感受到它的用武之地。

    需要注意的是,我們是用 $mount 手動渲染的組件,如果要銷燬,也要用 $destroy 來手動銷燬實例,必要時,也可以用 removeChild 把節點從 DOM 中移除。

    結語

    這兩個 API 並不難理解,只是不常使用罷了,因為多數情況下,我們只關注在業務層,並使用現成的組件庫。


    歡迎關注


分享到:


相關文章: