/ VueJS

[VueJS] 關於動態載入這件事

因為有一些需求的緣故,所以需要做到動態載入元件。想當然爾,事情不是想像中的那麼簡單。雖然說 VueJS 已經很方便了,不過做起來還是有一些地方需要留意


動態元件

VueJS 有一個 :is 可以用,詳細的使用辦法可以參考官方說明

<component :is="myComponent"></component>
import myComponent from 'components/myComponent.vue'

export default {
    components: {
        myComponent: myComponent
    },
    data () {
        return {
            myComponent: 'myComponent'
        }
    }
}

一般來說項上述的用法,其實就能動態的改變樣版中的元件。問題來了,如果每次都修改都要重新產生一次檔案(也就是每次都要 npm run build,然後更新整包檔案)。這樣用起來也太麻煩了點。

所以說,是不是能有更好的方式來做這件事情?

把 Vue 放到外面去

假設我們有一個 App.js,用來初始化整個 App 的地方,我們將 Vue 給拋出去。

import Vue from 'vue'

export default {
    name: 'app',
    created () {
        // 從這裡把 Vue 丟到外面去
        if (typeof window.Vue === 'undefined') {
            window.Vue = Vue
        }
    }
}

或許你會問,丟出去要幹嘛?我們現在再回來看上面提到的例子。

export default {
    mounted () {
        let dynamicComponent = document.getElementById('dynamicComponent')
        if (!dynamicComponent) {
            dynamicComponent.src = '/static/my_component.js'
            dynamicComponent.async = true
            dynamicComponent.onload = c => {
                this.component = 'myComponent'
            }
        } else {
            const script = document.querySelector('script')
            dynamicComponent = document.createElement('script')
            dynamicComponent.src = '/static/my_component.js'
            dynamicComponent.async = true
            dynamicComponent.onload = c => {
                this.component = 'myComponent'
            }

            script.parentNode.insertBefore(dynamicComponent, script)
        }
    },
    data () {
        return {
            component: ''
        }
    }
}

看起來很麻煩,但是上面做的事情就是將 /static/my_component.js 給載入到我們的 App 裡面。那 /static/my_component.js 會長什麼樣子呢?

Vue.component('myComponent', {
    template: '<div>這是一個簡單的範例</div>',
    mounted: function () {
        // ...
    }
});

他其實就是一個包裝好的 Vue Component 而已,沒有什麼太神奇的地方。

附加條件

這種方法有一個弊病,看到剛剛的 window.Vue = Vue 了嗎?意思就是,你的專案不能使用 Runtime-only 的方式,一定得用 Runtime + Compiler,不然你包裝好的 Vue Component 有可能會無法使用。

為何不用 import

雖然說 import 也可以直接使用在 components 裡面,但是這個方式在 runtime 的時候會造成錯誤,畢竟原本的 App 結構當中可能不存在你的檔案。或者說,你的檔案是後期才要製作或是修改的,那麼即便 import 好用,在此也是無用武之地。

外部檔案也可以打包

既然 App 都可以了,我們自己外部的檔案當然也是可以。不過,這麼做的話專案可能會變得比較複雜。也就是說,你做個專案可能得跑好幾次 npm run build 就是了。

但如果是頻繁修改某一個元件,或是元件結構相對複雜,需要多重動態載入的話,這種方法也不會麻煩。畢竟不用每次都打包一整個 App,而且就算 Component 壞掉了,也只會是某個區塊無法顯示,不會讓整個 App 掛掉。

LIVE DEMO

建議開 1080P,因為我螢幕錄影解析度開太大了 XD

webpack 設定

我所使用的只是 Vue-CLI 內建的 webpack-simple 這個樣版而已,其中你有幾個地方需要修改,

    output: {
        // path: path.resolve(__dirname, './dist'),
        // 把輸出的資料夾指定到你要的地方
        path: path.resolve(__dirname, './components'),
        // publicPath: '/dist/',
        // 把輸出的 publicPath 根據你的主要 App 作對應的設定
        publicPath: '/static/components/',
        // 最後可以改一下輸出檔案名稱
        // filename: 'build.js'
        filename: 'components.js'
    },

單一元件 main.js 修改

上述有提到把 Vue 丟出來給 window.Vue 這件事情,所以當我單一元件要使用的時候,其實相對容易,把原本的 main.js 稍微改一下就好了,

import App from './App.vue'
import Hello from './Hello.vue'

// 注意這邊,這個 Component 叫做 'single-component'
window.Vue.component('single-component', {
  render: h => h(App)
})

// 你可以載入其他的,記得要先 import 進來
window.Vue.component('hello-component', {
  render: h => h(Hello)
})

小結

我目前已經有一兩個正式專案使用者種方式載入,對於頻繁修改元件來說,挺愜意的。