• 當前位置:聯(lián)升科技 > 技術(shù)資訊 > 開(kāi)發(fā)技術(shù) >

    為 Vue 的惰性加載加一個(gè)進(jìn)度條

    2020-10-26    作者:前端小混混    來(lái)源:前端先鋒    閱讀: 次
    通常用 Vue.js 編寫(xiě)單頁(yè)應用(SPA)時(shí),當加載頁(yè)面時(shí),所有必需的資源(如 JavaScript 和 CSS 文件)都會(huì )被一起加載。在處理大文件時(shí),這可能會(huì )導致用戶(hù)體驗不佳。
    借助 Webpack,可以用 import() 函數而不是 import 關(guān)鍵字在 Vue.js 中按需加載頁(yè)面。

    為什么要按需加載?
    Vue.js 中 SPA 的典型工作方式是將所有功能和資源打包一并交付,這樣可以使用戶(hù)無(wú)需刷新頁(yè)面即可使用你的應用。如果你沒(méi)有為了按需加載頁(yè)面針對自己的應用進(jìn)行明確的設計,那么所有的頁(yè)面會(huì )被立即加載,或者提前使用大量?jì)却孢M(jìn)行不必要的預加載。
    這對有許多頁(yè)面的大型 SPA 非常不利,會(huì )導致使用低端手機和低網(wǎng)速的用戶(hù)體驗會(huì )很差。如果通過(guò)按需加載,用戶(hù)將不需要下載他們當前不需要的資源。
    Vue.js 沒(méi)有為動(dòng)態(tài)模塊提供任何加載指示器相關(guān)的控件。即使進(jìn)行了預取和預加載,也沒(méi)有對應的空間讓用戶(hù)知道加載的過(guò)程,所以還需要通過(guò)添加進(jìn)度條來(lái)改善用戶(hù)體驗。
    準備項目
    首先需要一種讓進(jìn)度條與 Vue Router 通信的方法。事件總線(xiàn)模式比較合適。
    事件總線(xiàn)是一個(gè) Vue 實(shí)例的單例。由于所有 Vue 實(shí)例都有一個(gè)使用 $on和 $emit的事件系統,因此可以用它在應用中的任何地方傳遞事件。
    首先在 components 目錄中創(chuàng )建一個(gè)新文件 eventHub.js:
    import Vue from 'vue' 
    export default new Vue() 
    然后把 Webpack 配置為禁用預取和預加載,這樣就可以針對每個(gè)函數單獨執行此類(lèi)操作,當然你也可以全局禁用它。在根文件夾中創(chuàng )建一個(gè) vue.config.js 文件并添加禁用預取和預加載的相關(guān)配置:
    module.exports = { 
        chainWebpack: (config) => { 
            // 禁用預取和預加載 
            config.plugins.delete('prefetch') 
            config.plugins.delete('preload') 
        }, 
    添加路由和頁(yè)面
    用 npx 安裝 Vue router 并使用:
    $ npx vue add router 
    編輯位于 router/index.js 下的 router 文件并更新路由,以便可以用 import() 函數代替 import 語(yǔ)句:
    以下默認配置:
    import About from '../views/About.vue' 
        path: '/about', 
        name: 'About', 
        component: About 
    }, 
    將其改為:
        path: '/about', 
        name: 'About', 
        component: () => import('../views/About.vue') 
    }, 
    如果希望可以選擇按需加載某些頁(yè)面,而不是全局禁用預取和預加載,可以用特殊的 Webpack 注釋?zhuān)灰?vue.config.js 中配置 Webpack:
    import( 
        /* webpackPrefetch: true */ 
        /* webpackPreload: true */ 
        '../views/About.vue' 
    import() 和 import 之間的主要區別是在運行時(shí)加載由 import() 加載的 ES 模塊,在編譯時(shí)加載由 import 加載的 ES 模塊。這就意味著(zhù)可以用 import() 延遲模塊的加載,并僅在必要時(shí)加載。
    實(shí)現進(jìn)度條
    由于無(wú)法準確估算頁(yè)面的加載時(shí)間(或完全加載),因此我們無(wú)法真正的去創(chuàng )建進(jìn)度條。也沒(méi)有辦法檢查頁(yè)面已經(jīng)加載了多少。不過(guò)可以創(chuàng )建一個(gè)進(jìn)度條,并使它在頁(yè)面加載時(shí)完成。
    由于不能真正反映進(jìn)度,所以描繪的進(jìn)度只是進(jìn)行了隨機跳躍。
    先安裝 lodash.random,因為在生成進(jìn)度條的過(guò)程中將會(huì )用這個(gè)包產(chǎn)生一些隨機數:
    $ npm i lodash.random 
    然后,創(chuàng )建一個(gè) Vue 組件 components/ProgressBar.vue:
    <template> 
        <div :class="{'loading-container': true, loading: isLoading, visible: isVisible}"> 
            <div class="loader" :style="{ width: progress + '%' }"> 
                <div class="light"></div> 
            </div> 
            <div class="glow"></div> 
        </div> 
    </template> 
    接下來(lái)向該組件添加腳本。在腳本中先導入 random 和 $eventHub,后面會(huì )用到:
    <script> 
    import random from 'lodash.random' 
    import $eventHub from '../components/eventHub' 
    </script> 
    導入之后,在腳本中定義一些后面要用到的變量:
    // 假設加載將在此時(shí)間內完成。 
    const defaultDuration = 8000  
    // 更新頻率 
    const defaultInterval = 1000  
    // 取值范圍 0 - 1. 每個(gè)時(shí)間間隔進(jìn)度增長(cháng)多少 
    const variation = 0.5  
    // 0 - 100. 進(jìn)度條應該從多少開(kāi)始。 
    const startingPoint = 0  
    // 限制進(jìn)度條到達加載完成之前的距離 
    const endingPoint = 90  
    然后編碼實(shí)現異步加載組件的邏輯:
    export default { 
        name: 'ProgressBar', 
         
        data: () => ({ 
            isLoading: true, // 加載完成后,開(kāi)始逐漸消失 
            isVisible: false, // 完成動(dòng)畫(huà)后,設置 display: none 
            progress: startingPoint, 
            timeoutId: undefined, 
        }), 
     
        mounted() { 
            $eventHub.$on('asyncComponentLoading', this.start) 
            $eventHub.$on('asyncComponentLoaded', this.stop) 
        }, 
     
        methods: { 
            start() { 
                this.isLoading = true 
                this.isVisible = true 
                this.progress = startingPoint 
                this.loop() 
            }, 
     
            loop() { 
                if (this.timeoutId) { 
                    clearTimeout(this.timeoutId) 
                } 
                if (this.progress >= endingPoint) { 
                    return 
                } 
                const size = (endingPoint - startingPoint) / (defaultDuration / defaultInterval) 
                const p = Math.round(this.progress + random(size * (1 - variation), size * (1 + variation))) 
                this.progress = Math.min(p, endingPoint) 
                this.timeoutId = setTimeout( 
                    this.loop, 
                    random(defaultInterval * (1 - variation), defaultInterval * (1 + variation)) 
                ) 
            }, 
     
            stop() { 
                this.isLoading = false 
                this.progress = 100 
                clearTimeout(this.timeoutId) 
                const self = this 
                setTimeout(() => { 
                    if (!self.isLoading) { 
                        self.isVisible = false 
                    } 
                }, 200) 
            }, 
        }, 
    在 mounted() 函數中,用事件總線(xiàn)來(lái)偵聽(tīng)異步組件的加載。一旦路由告訴我們已經(jīng)導航到尚未加載的頁(yè)面,它將會(huì )開(kāi)始加載動(dòng)畫(huà)。
    最后其添加一些樣式:
    <style scoped> 
    .loading-container { 
        font-size: 0; 
        position: fixed; 
        top: 0; 
        left: 0; 
        height: 5px; 
        width: 100%; 
        opacity: 0; 
        display: none; 
        z-index: 100; 
        transition: opacity 200; 
     
    .loading-container.visible { 
        display: block; 
    .loading-container.loading { 
        opacity: 1; 
     
    .loader { 
        background: #23d6d6; 
        display: inline-block; 
        height: 100%; 
        width: 50%; 
        overflow: hidden; 
        border-radius: 0 0 5px 0; 
        transition: 200 width ease-out; 
     
    .loader > .light { 
        float: right; 
        height: 100%; 
        width: 20%; 
        background-image: linear-gradient(to right, #23d6d6, #29ffff, #23d6d6); 
        animation: loading-animation 2s ease-in infinite; 
     
    .glow { 
        display: inline-block; 
        height: 100%; 
        width: 30px; 
        margin-left: -30px; 
        border-radius: 0 0 5px 0; 
        box-shadow: 0 0 10px #23d6d6; 
     
    @keyframes loading-animation { 
        0% { 
            margin-right: 100%; 
        } 
        50% { 
            margin-right: 100%; 
        } 
        100% { 
            margin-right: -10%; 
        } 
    </style> 
    最后將 ProgressBar 添加到 App.vue 或布局組件中,只要它與路由視圖位于同一組件中即可,它在應用的整個(gè)生命周期中都可用:
    <template> 
        <div> 
            <progress-bar></progress-bar> 
            <router-view></router-view> 
            <!--- 你的其它組件 --> 
        </div> 
    </template> 
     
    <script> 
    import ProgressBar from './components/ProgressBar.vue' 
    export default { 
           components: { ProgressBar }, 
    </script> 
    然后你就可以在頁(yè)面頂端看到一個(gè)流暢的進(jìn)度條:

    頁(yè)面頂端的進(jìn)度條
    為延遲加載觸發(fā)進(jìn)度條現在 ProgressBar 正在事件總線(xiàn)上偵聽(tīng)異步組件加載事件。當某些資源以這種方式加載時(shí)應該觸發(fā)動(dòng)畫(huà)?,F在向路由添加一個(gè)路由守護來(lái)接收以下事件:
    import $eventHub from '../components/eventHub' 
    router.beforeEach((to, from, next) => { 
        if (typeof to.matched[0]?.components.default === 'function') { 
            $eventHub.$emit('asyncComponentLoading', to) // 啟動(dòng)進(jìn)度條 
        } 
        next() 
    }) 
     
    router.beforeResolve((to, from, next) => { 
        $eventHub.$emit('asyncComponentLoaded') // 停止進(jìn)度條 
        next() 
    }) 
    為了檢測頁(yè)面是否被延遲加載了,需要檢查組件是不是被定義為動(dòng)態(tài)導入的,也就是應該為 component:() => import('...') 而不是component:MyComponent。
    這是通過(guò) typeof to.matched[0]?.components.default === 'function'完成的。帶有 import 語(yǔ)句的組件不會(huì )被歸為函數。
    總結
    在本文中,我們禁用了在 Vue 應用中的預取和預加載功能,并創(chuàng )建了一個(gè)進(jìn)度條組件,該組件可顯示以模擬加載頁(yè)面時(shí)的實(shí)際進(jìn)度。


    相關(guān)文章

    我們很樂(lè )意傾聽(tīng)您的聲音!
    即刻與我們取得聯(lián)絡(luò )
    成為日后肩并肩合作的伙伴。

    行業(yè)資訊

    聯(lián)系我們

    13387904606

    地址:新余市仙女湖區仙女湖大道萬(wàn)商紅A2棟

    手機:13755589003
    QQ:122322500
    微信號:13755589003

    江西新余網(wǎng)站設計_小程序制作_OA系統開(kāi)發(fā)_企業(yè)ERP管理系統_app開(kāi)發(fā)-新余聯(lián)升網(wǎng)絡(luò )科技有限公司 贛ICP備19013599號-1   贛公網(wǎng)安備 36050202000267號   

    微信二維碼
    色噜噜狠狠一区二区三区果冻|欧美亚洲日本国产一区|国产精品无码在线观看|午夜视频在线观看一区|日韩少妇一区二区无码|伊人亚洲日韩欧美一区二区|国产在线码观看清码视频