webpack4搭建現代Hybird-h5工程

1、前言

這篇文章會分享一下我是如何針對混合開發(Hybird h5端)搭建構建環境css

內容涉及到以下幾點:html

(1)混合開發h5端頁面特色以及需求

(2)如何利用webpack4進行多入口的代碼分割以達到較優的緩存利用率

(3)如何針對首屏渲染進行優化

(4)使用webpack4如何同時輸出ES next(現代)與es5(向後兼容)的包

(5)客戶端打包h5資源到包內策略

代碼倉庫webpack-esnext-cli,你們也能夠邊看代碼邊理解,固然能本身動手搭建一次是最好的前端

2、混合開發h5端頁面特色以及需求

混合開發的h5端頁面有什麼特色?vue

入口繁雜、部分頁面僅文案、須要較快的首屏渲染速度、部分頁面的使用率不高,依賴網絡的速度快慢node

(1)入口繁雜

入口繁雜其實意味着你的前端工程搭建必須是以多入口爲起點搭建的,如webpack你能夠配置entry,自行寫一個腳本在構建時獲取每個頁面的js入口,而多入口意味着你必須考慮頁面之間共享的模塊應該如何抽取以達到一個較優的模塊利用率,這點咱們在文章下一節詳細講。webpack

(2)部分頁面僅文案,且利用率不高

其實有作過混合開發你天然會發現有一些頁面是隻有文案,沒有任何js交互的,這類的頁面,相似用戶協議、常見問題之類的文案類頁面,使用率不高,對首屏又有要求的咱們徹底能夠將html寫在html文件中,不須要用vue這類的框架去寫虛擬DOM依賴框架去渲染,這樣能夠節省掉請求腳本以及框架運行渲染的時間,保證文字能夠快速出如今頁面當中。git

(3)較快的首屏渲染以及網絡的快慢

由於咱們的h5頁面是比較依賴手機網絡的,它不像客戶端資源都是大包到本地的,如今雖然4g普及了,可是用戶不少時候網絡其實並很差,那在弱網的狀況下對頁面的打開速度就有了必定的挑戰了,從工程角度上考慮,咱們可不能夠像客戶端那樣把h5須要的資源也打包進本地呢?答案固然是能夠的,後面我會陸續講到。es6

3、如何利用webpack4進行代碼分割

根據上面第二點咱們提到的頁面特色和需求,咱們的多頁面共享的模塊應該是下面這樣的:github

一、每一個入口基本都須要用到的包應該長期緩存(hash值不變)

二、共享的chunk可自由分割

三、共享的chunk可在頁面配置引用

那咱們先來看看在webpack4中咱們能夠經過什麼手段保證如上三點web

例如在vue的工程當中,咱們的每一個入口基本都依賴於vue.js,而在咱們打出的包中vue所佔的資源大小比重也是比較大的,而這部分就是咱們須要長期緩存的。關於這個點我在個人另外一篇文章中講過。這裏咱們在webpack4中會單獨將vue打包成vendor做爲項目的基礎包供頁面引入(固然你也能夠將其他的模塊打包進來)

...

optimization: {
    splitChunks: {
        ...
        
        'vendor': {
            test: /node_modules\/vue/,
            name: 'vendor',
            chunks: 'all',
            enforce: true,
            priority: 2
          },
          
        ...
    }
},
plugins: [
    // 穩定moduleId,避免引入了一個新模塊後,致使模塊ID變動使得vender和common的hash變化後緩存失效
    new webpack.HashedModuleIdsPlugin(),
]

...
複製代碼

單獨將vue打包進vendor後,咱們就保證了一個基礎模塊是穩定的,但咱們還須要一些靈活性,好比咱們有一些複雜的頁面可能會作成單頁面,這時候咱們就須要引入vue-router、vuex這類的工具。

你可能會說爲何不把這些包打包進vendor中?由於混合開發中頁面的入口是很是繁雜的,若是用戶打開一個普通的頁面,僅依賴vue就能夠了,可是由於你抽取的時候把vue-router也打包進去了,致使用戶下載了一個它可能用不到的又比較大的文件,同時這樣也會影響到其餘頁面的渲染速度,由於js包太大了,致使下載時間過長,而分開打包能起到一個增量下載的做用。

這時候咱們在splitChunks中增長一項spa-vendor配置:

optimization: {
    splitChunks: {
        ...
        
        // 項目基礎包
        'vendor': {
            test: /node_modules\/vue/,
            name: 'vendor',
            chunks: 'all',
            enforce: true,
            priority: 2
        },
        // 單頁面須要引入vue-router, vuex,這裏單獨分割出來
        'spa-vendor': {
            test: /node_modules\/vue-router/g,
            name: 'spa-vendor',
            chunks: 'all',
            enforce: true, 
            priority: 10
        },
          
        ...
    }
},
複製代碼

好了,到這裏,咱們已經把項目一些比較大的,不常變動的包獨立分割出來而且作到持久緩存了,那剩餘的大小不那麼大的包咱們就可讓webpack根據大小和引用率去自動打包了,這裏咱們加一個commons包的配置

optimization: {
    splitChunks: {
        ...
        
        // 項目基礎包
        'vendor': {
            test: /node_modules\/vue/,
            name: 'vendor',
            chunks: 'all',
            enforce: true,
            priority: 2
        },
        // 單頁面須要引入vue-router, vuex,這裏單獨分割出來
        'spa-vendor': {
            test: /node_modules\/vue-router/g,
            name: 'spa-vendor',
            chunks: 'all',
            enforce: true, 
            priority: 10
        },
        // 剩餘chunk自動分割
        'commons': {
            name: 'commons',
            minChunks: 5, // 引用次數大於5則打包進commons
            minSize: 3000, // chunk大小大於這個值才容許打包進commons
            chunks: 'all',
            enforce: true,
            priority: 1
        }
        ...
    }
},
複製代碼

你們看到這裏會看到splitChunk中每一個chunk的priority(優先級)是不同的,commons的優先級是最低的,由於要等到spa-vendor和vendor抽取完成後纔會到commons抽取

完成後,打出的包是下面這樣的

vendor(60k):

spa-vendor(23k,仍是比較大的):

commons:

自由分割和長期緩存咱們已經作到了,那剩下的就是chunk在頁面中自由引入了。在我寫的webpack-esnext-cli中,我是用了nunjucks模板引擎去作頁面的資源引入的,利用webpack4和webpack-manifest-plugin插件在打包後輸出的資源表,對資源進行頁面的自由配置

好比,須要引入spa-vendor的單頁,咱們會引入manifest、vendor、spa-vendor、commons包、頁面入口js(業務文件)默認引入

<!DOCTYPE html>
<html lang="en" bgc-f7f7f7>
<head>
  ...
</head>
<body>
  <div id="app"></div>
  <!-- 以註釋的方式添加模板語法,addAssets方法能夠注入對應模塊組(按順序) -->
  <!-- {{ 'js' | addAssets(['manifest', 'vendor', 'spa-vendor', 'commons']) }} -->
</body>
</html>
複製代碼

若是僅僅是普通的只須要基於vue的,咱們會引入manifest、vendor、commons(這裏的addAssets方法傳入空數組默認引入這幾個chunk),這樣咱們在頁面打開時,就不須要加載spa-vendor了,達到一個模塊冗餘的做用

<!DOCTYPE html>
<html lang="en" bgc-f7f7f7>
<head>
  ...
</head>
<body>
  <div id="app"></div>
  <!-- 以註釋的方式添加模板語法,addAssets方法能夠注入對應模塊組(按順序) -->
  <!-- {{ 'js' | addAssets([]) }} -->
</body>
</html>
複製代碼

固然你能夠更細緻的區分你的chunk應該如何去分割,這裏只是演示一下。

4、使用webpack4輸出ES next語法的包

PHILIP WALTON這篇文章講解了輸出es next的原理,那時候看得我是激情澎湃,因此就本身花了時間去實現了一套

這裏就講一下思路吧,代碼實現你們能夠自行去看倉庫代碼

在打包的時候,咱們須要輸出兩套包

原理其實就是經過改變broswerList讓babel編譯出不一樣語法的包

modern(es6):

legacy(es5):

構建es6語法的包後咱們須要輸出es5的入口文件,爲了不輸出的資源表重疊的狀況須要給es5的入口從新命名

其中a.js爲咱們的es6構建入口,腳本建立的a-legacy.js爲咱們的es5包構建入口,內容以下:

// a-legacy.js

import './a.js'
複製代碼

打包時,咱們根據js入口生成對應的html文件後,根據每次打包生成的資源表選擇資源進行插入

資源表:

如上圖,資源表對應了本來的路徑與輸出後的資源路徑,在輸出html時根據路徑去作資源匹配就能夠了

輸出後的html以下(modern包的manifest文件內聯了):

其中支持type=module語法的瀏覽器就會自動加載es6語法的包,不支持則加載es5的向後兼容包

這麼作其實效益是很是大的,下面引用兩張PHILIP WALTON文章的圖

可見輸出的es6的包無論在size仍是解析速度都是優於es5語法的包的,對於移動端加速效果仍是很是大的

5、加速首屏與將h5資源打包進客戶端

加速首屏要作什麼?就是讓內容儘快的出現啊。

在以前咱們已經經過模塊分割和輸出es next包讓咱們的資源利用率和大小,還有代碼解析的速度都獲得了提高了,接下來咱們應該要考慮一下如何利用客戶端的能力進行優化了。

加速首屏,無非就是加快webview的啓動速度,和減小包的下載時間嘛,由於首屏的速度很大一部分緣由是由於資源下載致使頁面阻塞。

設想一下,若是webview能夠攔截咱們的資源請求,那咱們是否是就能夠把咱們頁面的js與css等靜態資源一塊兒打包到客戶端中,在客戶端開啓webview後,經過攔截url,對本地資源進行url的匹配,命中則讀取本地文件,文件過時則再次從服務器上拉取,甚至可讓服務端作一個推送服務更新資源文件,能作到這樣h5頁面在客戶端基本能達到秒開了,也能減輕服務器的壓力,在弱網的狀況下優化很是明顯。

固然首屏你還能夠經過構建預渲染一部分html到html文件中,個人另外一篇文章中有講--如何在webpack中作預渲染下降首屏空白時間,這裏就再也不說了

6、總結

Hybird h5的工程搭建,其實更可能是根據需求去作的,使用webpack只是一種方式,更重要的我以爲是對資源加載、緩存的理解和運用。不說了,不說了,該時候搬磚了。有什麼疑問或者建議歡迎提出。

相關文章
相關標籤/搜索