天天看點

h5首頁加載慢_前端性能優化之預加載和懶加載

h5首頁加載慢_前端性能優化之預加載和懶加載

老項目優化第二步——資源的懶加載和預加載

之前做前端項目性能優化的時候,為了使頁面加載更快,用盡了手段,包括檔案的合并、壓縮,檔案緩存和開啟伺服器端的 gzip 壓縮等等。

但發現壓縮總有個極限,并且遇到弱網環境時,使用者在感覺上依舊會有“慢”的概念,尤其是在首屏展示的時候,于是就選了兩種方案(骨架屏和資源預加載,骨架屏下次再總結)。

預加載

預加載相信很多人都有聽說過,引用Patrick Hamann的解釋就是,預加載是浏覽器對将來可能被使用資源的一種暗示,一些資源可以在目前頁面使用到,一些可能在将來的某些頁面中被使用。

簡單了解就是,将頁面加載時的資源提前加載到本地,等頁面真正加載時直接從緩存擷取資源。

h5首頁加載慢_前端性能優化之預加載和懶加載

我主要是使用在首屏加載時需要的資源,以及一個資源過大的檔案上,避免頁面長時間空白,減少等待時間,優化體驗。

預加載可以細分為以下幾個點:DNS-prefetch、subresource 、 prefetch、preconnect、prerender。

可根據自己項目需要去使用。

(1)DNS 預解析 DNS-Prefetch

通過 DNS 預解析來告訴浏覽器未來我們可能從某個特定的 URL 擷取資源,當浏覽器真正使用到該域中的某個資源時就可以盡快地完成 DNS 解析。比如說,我們可能從 http://example.com 擷取資源時(圖檔、音頻等),就可以在<head> 标簽中加入以下内容:

<link rel="dns-prefetch" href="//example.com" target="_blank" rel="external nofollow" >
           

當我們從該 URL 請求一個資源時,就不再需要等待 DNS 的解析過程。

(2)預連接配接 Preconnect

與 DNS 預解析類似,preconnect 不僅完成 DNS 預解析,同時還将進行 TCP 握手和建立傳輸層協定。可以這樣使用:

<link rel="preconnect" href="http://example.com" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
           

(3)預擷取 Prefetching

如果我們确定某個資源将來一定會被使用到,我們可以讓浏覽器預先請求該資源并放入浏覽器緩存中,也就是說如果我們猜測使用者接下來将要通路哪個具體的資源,那就可以用prefetching來預加載确定的資源了。

<link rel="prefetch" href="image.png" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
           

(4)Subresources

這是另一個預擷取方式,這種方式指定的預擷取資源具有最高的優先級,在所有 prefetch 項之前進行:

<link rel="subresource" href="styles.css" target="_blank" rel="external nofollow" >
           

prefetch 為将來的頁面提供了一種低優先級的資源預加載方式,而subresource 為目前頁面提供了一種高優先級的資源預加載。

如果資源是目前頁面必須的,或者資源需要盡快可用,那麼最好使用 subresource 而不是 prefetch。

(5)預渲染 Prerender

prerender 可以預先加載文檔的所有資源,類似于在隐藏的tab 頁中打開了某個連結 – 将下載下傳所有資源、建立 DOM 結構、完成頁面布局、應用 CSS 樣式和執行 JavaScript 腳本等。

當使用者真正通路該連結時,隐藏的頁面就切換為可見,使頁面看起來就是瞬間加載完成一樣。

<link rel="prerender" href="http://example.com" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
           

需要注意的是不要濫用該特性,當你知道使用者一定會點選某個連結時才可以進行預渲染,否則浏覽器将無條件地下載下傳所有預渲染需要的資源。

(6)新特性:Preloading

和prefetching不同,preloading會讓浏覽器無論如何都下載下傳指定的資源,也就是說浏覽器一定會預加載該資源。

<link rel="preload" href="image.png" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >
           

所有預加載技術都存在一個潛在的風險:對資源預測錯誤。

而預加載的開銷是高昂的,比如搶占 CPU 資源,消耗電池,浪費帶寬等,是以必須謹慎行事。

雖然很難确定使用者下一步将通路哪些資源,但高可信的場景确實存在:

  • 如果使用者完成一個帶有明顯結果的搜尋,那麼結果頁面很可能會被加載。
  • 如果使用者進入到登陸頁面,那麼登陸成功的頁面很可能會被加載。
  • 如果使用者閱讀一個多頁的文章或通路一個分頁的結果集,那麼下一頁很可能會被加載。

(7)H5音樂預加載 preload=”auto”

<audio src="audio.mp3" autoplay="autoplay" loop preload="auto" id="sendid2"></audio> 
           

(8)使用html标簽

<img src="image.png" style="display:none"/>
           

(9)使用Image對象

<script src="./imagePreload.js"></script>

// imagePreload.js檔案
var image= new Image()
image.src="https://xxx.xx.com/image.jpg"
           

優化網頁性能的另一種方式, 懶加載

圖檔懶加載

圖檔懶加載一般用于網頁中延遲加載圖像,使用者滾動到它們之前,可視區域外的圖像不會加載。這與圖像預加載相反,在長網頁上使用延遲加載将使網頁加載更快。

使用懶加載是為了應對那些頁面過長,圖檔資源過多的網頁。如果要等整個頁面一次性加載完成,那體驗就太差了。

實作原理
  • 首先,不要将圖檔位址放到src屬性中,而是放到其它屬性(data-original)中。
  • 頁面加載完成後,根據scrollTop判斷圖檔是否在使用者的視野内,如果在,則将data-original屬性中的值取出存放到src屬性中。
  • 在滾動事件中重複判斷圖檔是否進入視野,如果進入,則将data-original屬性中的值取出存放到src屬性中。
// 圖檔懶加載
<html >

<head>
    <meta charset="UTF-8">
    <title>Lazyload</title>
    <style>
        .image-item {
            display: block;
            margin-bottom: 50px;
            height: 200px; // 一定記得設定圖檔高度
        }
    </style>
</head>

<body>
    <img src="" class="image-item" lazyload="true" data-original="images/1.png" />
    <img src="" class="image-item" lazyload="true" data-original="images/2.png" />
    <img src="" class="image-item" lazyload="true" data-original="images/3.png" />
    <img src="" class="image-item" lazyload="true" data-original="images/4.png" />
    <img src="" class="image-item" lazyload="true" data-original="images/5.png" />
    <img src="" class="image-item" lazyload="true" data-original="images/6.png" />
    <img src="" class="image-item" lazyload="true" data-original="images/7.png" />
    <img src="" class="image-item" lazyload="true" data-original="images/8.png" />
    <img src="" class="image-item" lazyload="true" data-original="images/9.png" />
    <img src="" class="image-item" lazyload="true" data-original="images/10.png" />
    <img src="" class="image-item" lazyload="true" data-original="images/11.png" />
    <img src="" class="image-item" lazyload="true" data-original="images/12.png" />
    <script>
        var viewHeight = document.documentElement.clientHeight // 擷取可視區高度
        function lazyload() {
            var eles = document.querySelectorAll('img[data-original][lazyload]')
            Array.prototype.forEach.call(eles, function (item, index) {
                var rect
                if (item.dataset.original === "")
                    return
                rect = item.getBoundingClientRect() // 用于獲得頁面中某個元素的左,上,右和下分别相對浏覽器視窗的位置
                if (rect.bottom >= 0 && rect.top < viewHeight) {
                    !function () {
                        var img = new Image()
                        img.src = item.dataset.original
                        img.onload = function () {
                            item.src = img.src
                        }
                        item.removeAttribute("data-original")// 移除屬性,下次不再周遊
                        item.removeAttribute("lazyload")
                    }()
                }
            })
        }
        lazyload() // 剛開始還沒滾動螢幕時,要先觸發一次函數,初始化首頁的頁面圖檔
        document.addEventListener("scroll",lazyload)
    </script>
</body>

</html>
           

懶加載庫

Github上也有很多懶加載庫可以直接引用:

  • lazysizes 是一個功能十分強大的懶加載庫,主要用于加載圖檔和iframes。你隻需要指定src/srcset屬性,lazysizes會幫你自動懶加載内容。值得注意的是,lazysizes基于intersection observer,是以你需要一個polyfill。你還可以通過一些插件擴充庫的功能以用于懶加載視訊。
  • lozad.js是一個輕量級、高性能的懶加載庫,基于intersection observer,你同樣需要提供一個相關的polyfill。
  • blazy是一個輕量級的懶加載庫,大小僅為1.4KB。相對于lazysizes,它不需要任何的外部依賴,并且相容IE7+。你可能猜測到了,blazy不支援intersection observer,性能相對較差。
  • react-lazyload基于react的懶加載元件。

Vue路由懶加載(元件按需加載)

在單頁應用中,運用webpack打包後的檔案随着項目的進行會變得很大,元件越來越多,首次啟動項目時,需要加載的内容過多,延時過長,不利于使用者體驗。

運用懶加載可以将頁面進行劃分,需要的時候加載頁面,可以有效的分擔首頁所承擔的加載壓力,減少首頁加載時間。

與webpack配合實作元件懶加載

(1)在webpack配置檔案中的output路徑配置chunkFilename屬性

output: {
        path: resolve(__dirname, 'dist'),
        filename: options.dev ? '[name].js': '[name].js?[chunkhash]',
        chunkFilename: 'chunk[id].js?[chunkhash]',
        publicPath: options.dev ? '/assets/': publicPath
},
// chunkFilename路徑将會作為元件懶加載的路徑
           

(2)配合webpack支援的異步加載方法

// resolve => require([URL], resolve), 支援性好
// () => system.import(URL) , webpack2官網上已經聲明将逐漸廢除, 不推薦使用
// () => import(URL), webpack2官網推薦使用, 屬于es7範疇, 需要配合babel的syntax-dynamic-import插件使用, 具體使用方法如下

npm install --save-dev babel-core babel-loader babel-plugin-syntax-dynamic-importbabel-preset-es2015
use: [{
        loader: 'babel-loader',
        options: {
          presets: [['es2015', {modules: false}]],
          plugins: ['syntax-dynamic-import']
        }
      }]
           
具體執行個體中實作懶加載

(1)路由中配置異步元件

export default new Router({
    routes: [
        {
            mode: 'history',
            path: '/home,
            name: home,
            component:  resolve => require(['../views/home.vue'], resolve) // 懶加載
        },
    ]
})
           

(2)執行個體中配置異步元件

components: {
        historyTab: resolve => {require(['../../component/page.vue'], resolve)} // 懶加載   
}
           

(3)全局注冊異步元件

Vue.component(‘header’, () => {
    System.import('./component/header.vue')
})
           

以上是這次項目優化在加載這方面整理的材料,後續會持續更新歡迎各位朋友指正