日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
webpack的異步加載原理及分包策略

 webpack 異步加載原理

創(chuàng)新互聯(lián)專業(yè)IDC數(shù)據(jù)服務(wù)器托管提供商,專業(yè)提供成都服務(wù)器托管,服務(wù)器租用,雙線服務(wù)器托管,雙線服務(wù)器托管,成都多線服務(wù)器托管等服務(wù)器托管服務(wù)。

webpack ensure 有人稱它為異步加載,也有人稱為代碼切割,他其實就是將 js 模塊給獨(dú)立導(dǎo)出一個.js 文件,然后使用這個模塊的時候,再創(chuàng)建一個 script 對象,加入到 document.head 對象中,瀏覽器會自動幫我們發(fā)起請求,去請求這個 js 文件,然后寫個回調(diào)函數(shù),讓請求到的 js 文件做一些業(yè)務(wù)操作。

舉個例子

需求:main.js 依賴兩個 js 文件:A.js 是點擊 aBtn 按鈕后,才執(zhí)行的邏輯,B.js 是點擊 bBtn 按鈕后,才執(zhí)行的邏輯。

webpack.config.js,我們先來寫一下 webpack 打包的配置的代碼 

 
 
 
 
  1. const path = require('path') // 路徑處理模塊  
  2. const HtmlWebpackPlugin = require('html-webpack-plugin')  
  3. const { CleanWebpackPlugin } = require('clean-webpack-plugin') // 引入CleanWebpackPlugin插件  
  4. module.exports = {  
  5.   entry: { 
  6.      index: path.join(__dirname, '/src/main.js'),  
  7.   },  
  8.   output: { 
  9.      path: path.join(__dirname, '/dist'),  
  10.     filename: 'index.js',  
  11.   },  
  12.   plugins: [  
  13.     new HtmlWebpackPlugin({  
  14.       template: path.join(__dirname, '/index.html'),  
  15.     }),  
  16.     new CleanWebpackPlugin(), // 所要清理的文件夾名稱  
  17.   ],  

index.html 代碼如下 

 
 
 
 
  1.   
  2.   
  3.     
  4.       
  5.       
  6.     webpack  
  7.     
  8.     
  9.       
  10.       按鈕A  
  11.       按鈕B  
  12.     
  
  •     
  •  
  • 入口文件 main.js 如下 

     
     
     
     
    1. import A from './A'  
    2. import B from './B'  
    3. document.getElementById('aBtn').onclick = function () {  
    4.   alert(A)  
    5. }  
    6. document.getElementById('bBtn').onclick = function () {  
    7.   alert(B)  

    A.js 和 B.js 的代碼分別如下 

     
     
     
     
    1. // A.js  
    2. const A = 'hello A'  
    3. module.exports = A  
    4. // B.js  
    5. const B = 'hello B'  
    6. module.exports = B 

    此時,我們對項目進(jìn)行 npm run build, 打包出來的只有兩個文件

    •  index.html
    •  index.js

    由此可見,此時 webpack 把 main.js 依賴的兩個文件都同時打包到同一個 js 文件,并在 index.html 中引入。但是 A.js 和 B.js 都是點擊相應(yīng)按鈕才會執(zhí)行的邏輯,如果用戶并沒有點擊相應(yīng)按鈕,而且這兩個文件又是比較大的話,這樣是不是就導(dǎo)致首頁默認(rèn)加載的 js 文件太大,從而導(dǎo)致首頁渲染較慢呢?那么有能否實現(xiàn)當(dāng)用戶點擊按鈕的時候再加載相應(yīng)的依賴文件呢?

    webpack.ensure 就解決了這個問題。

    require.ensure 異步加載

    下面我們將 main.js 改成異步加載的方式 

     
     
     
     
    1. document.getElementById('aBtn').onclick = function () {  
    2.   //異步加載A  
    3.   require.ensure([], function () {  
    4.     let A = require('./A.js')  
    5.     alert(A)  
    6.   })  
    7. }   
    8. document.getElementById('bBtn').onclick = function () {  
    9.   //異步加載b  
    10.   require.ensure([], function () {  
    11.     let B = require('./B.js')  
    12.     alert(B)  
    13.   })  

    此時,我們再進(jìn)行一下打包,發(fā)現(xiàn)多了 1.index.js 和 2.index.js 兩個文件。而我們打開頁面時只引入了 index.js 一個文件,當(dāng)點擊按鈕 A 的時候才引入 1.index.js 文件,點擊按鈕 B 的時候才引入 2.index.js 文件。這樣就滿足了我們按需加載的需求。

    require.ensure 這個函數(shù)是一個代碼分離的分割線,表示回調(diào)里面的 require 是我們想要進(jìn)行分割出去的,即 require('./A.js'),把 A.js 分割出去,形成一個 webpack 打包的單獨(dú) js 文件。它的語法如下 

     
     
     
     
    1. require.ensure(dependencies: String[], callback: function(require), chunkName: String) 

    我們打開 1.index.js 文件,發(fā)現(xiàn)它的代碼如下 

     
     
     
     
    1. (windowwindow.webpackJsonp = window.webpackJsonp || []).push([  
    2.   [1],  
    3.   [  
    4.     ,  
    5.     function (o, n) {  
    6.       o.exports = 'hello A'  
    7.     },  
    8.   ],  
    9. ]) 

    由上面的代碼可以看出:

    1.  異步加載的代碼,會保存在一個全局的 webpackJsonp 中。
    2.  webpackJsonp.push 的的值,兩個參數(shù)分別為異步加載的文件中存放的需要安裝的模塊對應(yīng)的 id 和異步加載的文件中存放的需要安裝的模塊列表。
    3.  在滿足某種情況下,會執(zhí)行具體模塊中的代碼。

    import() 按需加載

    webpack4 官方文檔提供了模塊按需切割加載,配合 es6 的按需加載 import() 方法,可以做到減少首頁包體積,加快首頁的請求速度,只有其他模塊,只有當(dāng)需要的時候才會加載對應(yīng) js。

    import()的語法十分簡單。該函數(shù)只接受一個參數(shù),就是引用包的地址,并且使用了 promise 式的回調(diào),獲取加載的包。在代碼中所有被 import()的模塊,都將打成一個單獨(dú)的包,放在 chunk 存儲的目錄下。在瀏覽器運(yùn)行到這一行代碼時,就會自動請求這個資源,實現(xiàn)異步加載。

    下面我們將上述代碼改成 import()方式。 

     
     
     
     
    1. document.getElementById('aBtn').onclick = function () {  
    2.   //異步加載A  
    3.   import('./A').then((data) => {  
    4.     alert(data.A)  
    5.   })  
    6. }  
    7. document.getElementById('bBtn').onclick = function () {  
    8.   //異步加載b  
    9.   import('./B').then((data) => {  
    10.     alert(data.B)  
    11.   })  

    此時打包出來的文件和 webpack.ensure 方法是一樣的。

    路由懶加載

    為什么需要懶加載?

    像 vue 這種單頁面應(yīng)用,如果沒有路由懶加載,運(yùn)用 webpack 打包后的文件將會很大,造成進(jìn)入首頁時,需要加載的內(nèi)容過多,出現(xiàn)較長時間的白屏,運(yùn)用路由懶加載則可以將頁面進(jìn)行劃分,需要的時候才加載頁面,可以有效的分擔(dān)首頁所承擔(dān)的加載壓力,減少首頁加載用時。

    vue 路由懶加載有以下三種方式

    •  vue 異步組件
    •  ES6 的 import()
    •  webpack 的 require.ensure()

    vue 異步組件

    這種方法主要是使用了 resolve 的異步機(jī)制,用 require 代替了 import 實現(xiàn)按需加載 

     
     
     
     
    1. export default new Router({  
    2.   routes: [  
    3.     {  
    4.       path: '/home',',  
    5.       component: (resolve) => require(['@/components/home'], resolve),  
    6.     },  
    7.     {  
    8.       path: '/about',',  
    9.       component: (resolve) => require(['@/components/about'], resolve),  
    10.     },  
    11.   ],  
    12. }) 

    require.ensure

    這種模式可以通過參數(shù)中的 webpackChunkName 將 js 分開打包。 

     
     
     
     
    1. export default new Router({  
    2.   routes: [ 
    3.      {  
    4.       path: '/home',  
    5.       component: (resolve) => require.ensure([], () => resolve(require('@/components/home')), 'home'), 
    6.     },  
    7.     {  
    8.       path: '/about',  
    9.       component: (resolve) => require.ensure([], () => resolve(require('@/components/about')), 'about'),  
    10.     },  
    11.   ],  
    12. }) 

    ES6 的 import()

    vue-router 在官網(wǎng)提供了一種方法,可以理解也是為通過 Promise 的 resolve 機(jī)制。因為 Promise 函數(shù)返回的 Promise 為 resolve 組件本身,而我們又可以使用 import 來導(dǎo)入組件。 

     
     
     
     
    1. export default new Router({  
    2.   routes: [  
    3.     {  
    4.       path: '/home',  
    5.       component: () => import('@/components/home'),  
    6.     },  
    7.     {  
    8.       path: '/about',  
    9.       component: () => import('@/components/home'),  
    10.     },  
    11.   ], 
    12. }) 

    webpack 分包策略

    在 webpack 打包過程中,經(jīng)常出現(xiàn) vendor.js, app.js 單個文件較大的情況,這偏偏又是網(wǎng)頁最先加載的文件,這就會使得加載時間過長,從而使得白屏?xí)r間過長,影響用戶體驗。所以我們需要有合理的分包策略。

    CommonsChunkPlugin

    在 Webapck4.x 版本之前,我們都是使用 CommonsChunkPlugin 去做分離 

     
     
     
     
    1. plugins: [  
    2.   new webpack.optimize.CommonsChunkPlugin({  
    3.     name: 'vendor',  
    4.     minChunks: function (module, count) {  
    5.       return (  
    6.         module.resource &&  
    7.         /.js$/.test(module.resource) &&  
    8.         module.resource.indexOf(path.join(__dirname, './node_modules')) === 0  
    9.       )  
    10.     },  
    11.   }),  
    12.   new webpack.optimize.CommonsChunkPlugin({  
    13.     name: 'common',  
    14.     chunks: 'initial',  
    15.     minChunks: 2,  
    16.   }),  

    我們把以下文件單獨(dú)抽離出來打包

    •  node_modules 文件夾下的,模塊
    •  被 3 個 入口 chunk 共享的模塊

    optimization.splitChunks

    webpack 4 最大的改動就是廢除了 CommonsChunkPlugin 引入了 optimization.splitChunks。如果你的 mode 是 production,那么 webpack4 就會自動開啟 Code Splitting。

    它內(nèi)置的代碼分割策略是這樣的:

    •  新的 chunk 是否被共享或者是來自 node_modules 的模塊
    •  新的 chunk 體積在壓縮之前是否大于 30kb
    •  按需加載 chunk 的并發(fā)請求數(shù)量小于等于 5 個
    •  頁面初始加載時的并發(fā)請求數(shù)量小于等于 3 個

    雖然在 webpack4 會自動開啟 Code Splitting,但是隨著項目工程的最大,這往往不能滿足我們的需求,我們需要再進(jìn)行個性化的優(yōu)化。

    應(yīng)用實例

    我們先找到一個優(yōu)化空間較大的項目來進(jìn)行操作。這是一個后臺管理系統(tǒng)項目,大部分內(nèi)容由 3-4 個前端開發(fā),平時開發(fā)周期較短,且大部分人沒有優(yōu)化意識,只是寫好業(yè)務(wù)代碼完成需求,日子一長,造成打包出來的文件較大,大大影響性能。

    我們先用 webpack-bundle-analyzer 分析打包后的模塊依賴及文件大小,確定優(yōu)化的方向在哪。

    然后我們再看下打包出來的 js 文件

    看到這兩張圖的時候,我內(nèi)心是崩潰的,槽點如下

    •  打包后生成多個將近 1M 的 js 文件,其中不乏 vendor.js 首頁必須加載的大文件
    •  xlsx.js 這樣的插件沒必要使用,導(dǎo)出 excel 更好的方法應(yīng)該是后端返回文件流格式給前端處理
    •  echart 和 iview 文件太大,應(yīng)該使用 cdn 引入的方法

    吐槽完之后我們就要開始做正事了。正是因為有這么多槽點,我們才更好用來驗證我們優(yōu)化方法的可行性。

    抽離 echart 和 iview

    由上面分析可知,echart 和 iview 文件太大,此時我們就用到 webpack4 的 optimization.splitChunks 進(jìn)行代碼分割了,把他們單獨(dú)抽離打包成文件。(為了更好地呈現(xiàn)優(yōu)化效果,我們先把 xlsx.js 去掉)

    vue.config.js 修改如下: 

     
     
     
     
    1. chainWebpack: config => {  
    2.     config.optimization.splitChunks({  
    3.       chunks: 'all',  
    4.       cacheGroups: {  
    5.         vendors: {  
    6.           name: 'chunk-vendors',  
    7.           test: /[/]node_modules[/]/,  
    8.           priority: 10,  
    9.           chunks: 'initial'  
    10.         },  
    11.         iview: {  
    12.           name: 'chunk-iview',  
    13.           priority: 20,  
    14.           test: /[/]node_modules[/]_?iview(.*)/  
    15.         },  
    16.         echarts: {  
    17.           name: 'chunk-echarts',  
    18.           priority: 20,  
    19.           test: /[/]node_modules[/]_?echarts(.*)/  
    20.         },  
    21.         commons: {  
    22.           name: 'chunk-commons',  
    23.           minChunks: 2,  
    24.           priority: 5,  
    25.           chunks: 'initial',  
    26.           reuseExistingChunk: true  
    27.         }  
    28.       }  
    29.     })  
    30.   }, 

    此時我們再用 webpack-bundle-analyzer 分析一下

    打包出來的 js 文件

    從這里可以看出我們已經(jīng)成功把 echart 和 iview 單獨(dú)抽離出來了,同時 vendor.js 也相應(yīng)地減小了體積。此外,我們還可以繼續(xù)抽離其他更多的第三方模塊。

    CDN 方式

    雖然第三方模塊是單獨(dú)抽離出來了,但是在首頁或者相應(yīng)路由加載時還是要加載這樣一個幾百 kb 的文件,還是不利于性能優(yōu)化的。這時,我們可以用 CDN 的方式引入這樣插件或者 UI 組件庫。

        1.  在 index.html 引入相應(yīng) cdn 鏈接 

     
     
     
     
    1.   
    2.    
    3.   
    4.   
    5.   
      
  •     
  •     
  •     
  •     
  •  
  •     1.  vue.config.js 配置 externals 

     
     
     
     
    1. configureWebpack: (config) => {  
    2.   config.externals = {  
    3.     vue: 'Vue',  
    4.     xlsx: 'XLSX',  
    5.     iview: 'iView',  
    6.     iView: 'ViewUI',  
    7.   }  

        1.  刪除之前的引入方式并卸載相應(yīng) npm 依賴包 

     
     
     
     
    1. npm uninstall vue iview echarts xlsx --save 

    此時我們在來看一下打包后的情況

    打包出來的 js 文件

    well done ! 這時基本沒有打包出大文件了,首頁加載需要的 vendor.js 也只有幾十 kb,而且我們還可以進(jìn)一步優(yōu)化,就是把 vue 全家桶的一些模塊再通過 cdn 的方法引入,比如 vue-router,vuex,axios 等。這時頁面特別是首頁加載的性能就得到大大地優(yōu)化了。


    網(wǎng)站欄目:webpack的異步加載原理及分包策略
    文章地址:http://www.dlmjj.cn/article/dpcocsh.html