前言:在vue 官方資料中,咱們能夠能夠很學會如何經過vue構建「動態組件」以及「異步組件」,然而,在官方資料中,並無涉及到真正的「動態異步」組件,通過大量的時間研究和技術分析,咱們給出目前比較合理的技術實現方式,並分析一下vue動態異步組件的實現思路。
官網中介紹,動態組件是經過tagcomponent
來構建,在當中綁定組件的引用便可,大體代碼以下:html
<template> <component :is="currentComp"></component> </template> <script> import compA from './CompA'; import compB from './CompB'; import compC from './CompC'; export default { data () { return { currentComp: compA } }, methods: { changeComp (name) { switch (name) { case 'compA' : { this.currentComp = compA; break; case 'compB' : this.currentComp = compB; break; case 'compC' : this.currentComp = compC; break; default : this.currentComp = compA; break; } } } } </script>
簡單說明一下,經過對字符串的判斷,來切換組件的實例,實例發生變化後,component
組件會自動切換。這就是vue中最基本的動態組件。可是,這樣的代碼有個很顯著的問題:前端
對於第二點,咱們能夠很容易的使用require或者import語法來進行異步加載。然而這並無解決問題1所帶來的隱患。按照實際的要求,咱們須要一個能夠在客戶端隨意配置的組件,即經過組件的地址或配置在進行動態加載的方式。這就是爲何要進行動態異步組件的構建。vue
這種構建方式是剛需的,一方面他能夠構築相似portal
中的porlet
組件,另外一方面,他的實現方式將給組件加載帶來巨大的提高。緩存
首先,咱們看一下預期的結果,咱們但願構建以下的代碼模式:異步
<template> <async-component path="/views/moduleA/compA"></async-component> </template>
所以,咱們建立一個AsyncComponent.vue
文件,並書寫代碼:async
<template> <component v-bind:is="componentFile"></component> </template> <script> export default { props: { path: { type: String, required: true, default: () => null } }, data () { const componentFile = this.render; return { componentFile: componentFile } }, methods: { render () { this.componentFile = (resolve) => ({ component: import(`@/${this.path}`), loading: { template: '<div style="height: 100%; width: 100%; display: table;"><div style="display: table-cell; vertical-align: middle; text-align: center;"><div>加載中</div></div></div>' }, error: { template: '<div style="height: 100%; width: 100%; display: table;"><div style="display: table-cell; vertical-align: middle; text-align: center;"><div>加載錯誤</div></div></div>' }, delay: 200, timeout: 10000 }); } }, watch: { file () { this.render(); } } } </script>
這個代碼很好解釋:分佈式
path
屬性,構建時候經過render
函數建立異步組件;render
函數爲官網的異步組件構建方式;path
屬性,當發生變化時,從新經過render函數構建;爲了可以讓組件可被從新激活,而且重用性更高,咱們對組件進行更多的參數化,最終結果以下:ide
<template> <keep-alive v-if="keepAlive"> <component :is="AsyncComponent" v-bind="$attrs" v-on="$listeners"/> </keep-alive> <component v-else :is="AsyncComponent" v-bind="$attrs" v-on="$listeners"/> </template> <script> import factory from './factory.js'; /** * 動態文件加載器 */ export default { inheritAttrs: false, // 外部傳入屬性 props: { // 文件的路徑 path: { type: String, default: null }, // 是否保持緩存 keepAlive: { type: Boolean, required: false, default: true }, // 延遲加載時間 delay: { type: Number, default: 20 }, // 超時警告時間 timeout: { type: Number, default: 2000 } }, data () { return { // 構建異步組件 - 懶加載實現 AsyncComponent: factory(this.path, this.delay, this.timeout) } }, watch: { path () { this.AsyncComponent = factory(this.path, this.delay, this.timeout); } }, methods: { load (path = this.path) { this.AsyncComponent = factory(path, this.delay, this.timeout); } } } </script>
具體改動以下:函數
keep-alive
配置,可以讓組件持久化;async-component
tag)傳參;async-component
tag)進行事件監聽;factory
函數增長加載組件、錯誤組件、未定義組件的封裝;delay
、timeout
配置;load
函數,方便外部JavaScript
調用;至此,咱們能夠經過<async-component path="/views/moduleA/compA.vue"></async-component>
來構建組件,若是path
是一個傳入屬性,經過改變該屬性觸發watch
來從新加載新組件。或者給該組件添加ref
屬性來獲取異步組件容器,經過調用load
方法來從新裝載。優化
看上去,如今的封裝已經很是好,可是距離完美還差很大一截。雖然已經解決了開發人員重複造輪子的問題,並優化了最佳的代碼模式,然而咱們仍然能發現一個問題:ref
以後,拿到的是AsyncComponent
組件,並不是能像<component>
tag同樣能夠直接對內部組件進行獲取。若是按照這樣的理想去思考,有利也是有弊的。利是咱們構建出了<component>
tag的動態異步版,弊是AsyncComponent
做爲容器的屬性很容易被內部的裝載物所替換,好比load
方法。
且不考慮這樣實現以後的問題,咱們能夠在開發過程當中經過約束來避免,代碼中也能夠增長屬性檢測機制。但這樣的實現方式很是困難,我嘗試過以下方式,均不能實現:
函數式組件: 經過添加functional: true
能夠把一個組件函數化,這樣使得該組件沒法獲取到this
即組件實例,所以他所掛載的就是內部的裝載組件。然而在構建過程當中,出現了無限遞歸render
函數的問題,沒法解決,並且createElement
函數沒法建立空節點,致使組件總會出現一個沒必要要的標籤。抽象化組件: 經過添加
abstract: true
能夠把一個組件抽象化,這樣組件是沒法渲染其自身,而只會掛在內部的裝載組件。但這樣作以後,會致使外部容器沒法經過ref
屬性查找到它,更沒法獲取到裝載組件,所以也以失敗了結。繼承式組件: 經過添加
extends
能夠在構建AsyncComponent
的時候實現繼承,這樣裝載組件的屬性都會傳入到AsyncComponent
中,然而,這樣的方式不支持動態繼承,因此仍是宣告失敗。
雖然都是失敗的結果,但這並不能中止咱們的想象,相信將來會有方法來解決這個問題。目前,咱們還只能老老實實的把AsyncComponent
當作容器來使用。
你要問我動態異步組件的將來是什麼,我會告訴你咱們的夢想有多大。咱們但願實現分佈式前端UIServer,讓AsyncComponent
能夠加載遠程組件。遠程組件就能夠實現配置化,就能夠脫離代碼自己而進行前端的組件構建。