webpack-lazy loading和prefetch

一、lazy loading

咱們經過一個示例來講明什麼是懶加載。html

// a,js
import './common'
console.log('A')
export default 'A'

// b.js
import './common'
console.log('B')
export default 'B'

// common.js
console.log('公共模塊')
export default 'common'

// 異步代碼
import(/* webpackChunkName: 'a'*/ './a').then(function(a) {
  console.log(a)
})

import(/* webpackChunkName: 'b'*/ './b').then(function(b) {
  console.log(b)
})

document.addEventListener('click', () => {
  // 異步加載lodash模塊
  import('lodash').then(({default: _}) => {
    console.log(_.join(['3', '4']))
  })
})

目錄結構:
1.png
配置webpack.config.js前端

const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

module.exports = {
  entry: {
    main: './lazyloading/index.js'
  },
  output: {
    path: path.resolve(__dirname, 'build'), // 打包文件的輸出目錄
    filename: '[name].bundle.js', // 代碼打包後的文件名
    publicPath: __dirname + '/build/', // 引用的路徑或者 CDN 地址
    chunkFilename: '[name].js' // 代碼拆分後的文件名
  },
  // 拆分代碼配置項
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        lodash: {
          name: 'lodash',
          test: /[\\/]node_modules[\\/]/,
          priority: 10
        },
        common: {
          name: 'common',
          minSize: 0, //表示在壓縮前的最小模塊大小,默認值是 30kb,若是沒設置爲0,common模塊就不會抽離爲公共模塊,由於原始大小小於30kb
          minChunks: 2, // 最小公用次數
          priority: 5, // 優先級
          reuseExistingChunk: true // 公共模塊必開啓
        },
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  },
  plugins: [
    new CleanWebpackPlugin(), // 會刪除上次構建的文件,而後從新構建
    new BundleAnalyzerPlugin()
  ]
}

咱們打包執行index.html,當點擊頁面的時候,異步加載lodash並輸出內容,演示以下:
lazyloading.gifnode

第一次進入頁面時並無加載lodash模塊,當我點擊頁面時,瀏覽器再去加載lodash,並執行輸出,這就是所謂的懶加載。webpack

其實懶加載就是經過import去異步加載一個模塊,至於何時去加載,這個要根據業務邏輯來寫,好比彈窗組件、模態框組件等等,都是點擊按鈕以後纔出現。web

懶加載可以加快頁面的加載速度,若是你把詳情頁、彈窗等頁面所有打包放在一個js中,當用戶只是訪問首頁,只須要首頁的代碼,不須要其餘頁面的代碼,加入多餘的代碼只會使得加載時間變長。chrome

import 後面返回的是一個 then,說明這是一個 promise 類型,一些低端的瀏覽器不支持 promise,好比 IE ,若是要使用這種異步的代碼,就要使用 babel 以及 babel-polyfill 來作轉換,由於我使用的是高版本的 chrome 瀏覽器,對 ES6 語法是支持的,因此我並無安裝 babel 也能使用。promise

如今咱們再列舉一個示例證明懶加載在性能方面的優化瀏覽器

更改index.js緩存

// 異步代碼
import(/* webpackChunkName: 'a'*/ './a').then(function(a) {
  console.log(a)
})

import(/* webpackChunkName: 'b'*/ './b').then(function(b) {
  console.log(b)
})

document.addEventListener('click', () => {
  var element = document.createElement('div')
  element.innerHTML = "hello world"
  document.body.appendChild(element)
})

從新打包,並打開 index.html ,打開瀏覽器控制檯,按 ctrl + shift + p ,輸入 coverage
就能看到當前頁面加載的 js 代碼未使用率,紅色區域表示未被使用的代碼段

2.png
這裏一開始紅色區域的代碼未被使用,當我點擊了頁面後,變成綠色,頁面出現了 Hello World,說明代碼被使用了性能優化

頁面剛加載的時候,異步的代碼根本就不會執行,可是咱們卻把它下載下來,實際上就會浪費頁面執行性能,webpack 就但願像這樣交互的功能,應該把它放到一個異步加載的模塊去寫

新建一個 click.js 文件

function handleClick() {
  var element = document.createElement('div')
  element.innerHTML = 'hello world'
  document.body.appendChild(element)
}

export default handleClick

而且將 index.js 文件改成異步的加載模塊:

document.addEventListener('click', () => {
  import('./click.js').then(({default: fn}) => {
    fn()
  })
})

從新打包,使用 coverage 分析
3.png
當加載頁面的時候,沒有加載業務邏輯,當點擊網頁的時候,纔會加載 0.js 模塊,也就是咱們在 index.js 中異步引入的 click.js

click.js文件越大時,懶加載對性能的提高越加明顯

因此想寫出高性能的代碼,不只僅考慮緩存,更要考慮到代碼的使用率

因此 webpack 在打包過程當中,是但願咱們多寫這種異步的代碼,才能提高網站的性能,這也是爲何 webpacksplitChunks 中的 chunks 默認是 async

二、prefetch

異步能提升你網頁打開的性能,而同步代碼是增長一個緩存,對性能的提高是很是有限的,由於緩存通常是第二次打開網頁或者刷新頁面的時候,緩存頗有用,可是網頁的性能通常是用戶第一次打開網頁,看首屏的時候。

固然,這也會出現另外一個問題,就是當用戶點擊的時候,纔去加載業務模塊,若是業務模塊比較大的時候,用戶點擊後並無立馬看到效果,而是要等待幾秒,這樣體驗上也很差,怎麼去解決這種問題

一:若是訪問首頁的時候,不須要加載詳情頁的邏輯,等用戶首頁加載完了之後,頁面展現出來了,頁面的帶寬被釋放出來了,網絡空閒了,再「偷偷」的去加載詳情頁的內容,而不是等用戶去點擊的時候再去加載

這個解決方案就是依賴 webpackPrefetching/Preloading 特性

修改index.js

document.addEventListener('click', () => {
  import(/* webpackPrefetch: true */ './click.js').then(({default: fn}) => {
    fn()
  })
})

webpackPrefetch: true 會等你主要的 JS 都加載完了以後,網絡帶寬空閒的時候,它就會預先幫你加載好

從新打包後刷新頁面,注意看 Network
prefetch.gif
能夠看到尚未點擊頁面時,當主要的js都加載完後,0.js(也就是click.js)在網絡空閒時就加載了。點擊頁面時,第二次加載就利用緩存中的0.js,發現加載時間由29ms縮減到4ms,可想而知預加載對頁面性能提高的重要性。

這裏咱們使用的是 webpackPrefetch,還有一種是 webpackPreload

區別:

與 prefetch 相比,Preload 指令有不少不一樣之處

Prefetch會等待覈心代碼加載完以後,有空閒以後再去加載。Preload 會和核心的代碼並行加載,仍是不推薦

總結

針對優化,不只僅是侷限於緩存,緩存能帶來的代碼性能提高是很是有限的,而是如何讓代碼的使用率最高,有一些交互後才用的代碼,能夠寫到異步組件裏面去,經過懶加載的形式,去把代碼邏輯加載進來,這樣會使得頁面訪問速度變的更快,若是你以爲懶加載會影響用戶體驗,可使用 Prefetch 這種方式來預加載,不過在某些遊覽器不兼容,會有兼容性的問題,重點不是在 Prefetch 怎麼去用,而是在作前端代碼性能優化的時候,緩存不是最重要的點,最重要的是代碼使用的覆蓋率上(coverage)

相關文章
相關標籤/搜索