摘要:當然,上面的定時器完全可以換成其他諸如按鈕點擊之類的事件來觸發。
Webpack Bundle Split 和 Code Split
話說之前也是對 chunk 這個概念有些模糊,并且很多時候網上的文章大部分在將代碼分離動態加載之類的。寫這篇文章的目的也是想讓其他那些跟我一樣曾經對這個概念不是很清楚的童鞋有個清晰的認識。廢話不多說,擼起袖子直接干!
Let"s Dive in!
Webpack 文件分離包括兩個部分,一個是 Bundle 的分離,一個是 Code 代碼的分離:
Bundle splitting: 實際上就是創建多個更小的文件,并行加載,以獲得更好的緩存效果;主要的作用就是使瀏覽器并行下載,提高下載速度。并且運用瀏覽器緩存,只有代碼被修改,文件名中的哈希值改變了才會去再次加載。
Code splitting: 只加載用戶最需要的部分,其余的代碼都遵從懶加載的策略;主要的作用就是加快頁面加載速度,不加載不必要加載的東西。
準備工作在進行文件分離之前的準備工作,我們先寫一些代碼:
入口文件 src/index.js:
const { getData } = require("./main") const { findMaxIndex } = require("./math") let arr = [1,2,123,21,3,21,321,1] findMaxIndex(arr) getData("./index.html")
兩個依賴模塊:
src/main.js:
const axios = require("axios") const getData = url => { axios.get(url).then(d => { console.log(d.status) console.log(d.data.length) }) } module.exports = { getData }
src/math.js:
const _ = require("lodash") const findMaxIndex = arr => { let x = _.max(arr) let r = Array.prototype.indexOf.call(arr, x) console.log(r); } module.exports = { findMaxIndex }
增加一個 webpack 配置文件 webpack.config.js:
const path = require("path") module.exports = { mode: "development", entry: path.resolve(__dirname, "src/index.js"), output: { path: path.resolve(__dirname, "dist"), filename: "[name].[contenthash].js" }, }文件分離之前打包效果
在 bundle split 和 code split 操作之前,我們先看一下當前默認打包的效果:
全部依賴都被打包到 main.xxx.js 中去,大小是 609k開始分離操作 Bundle Split
Bundle Split 的主要任務是將多個引用的包和模塊進行分離,避免全部依賴打包到一個文件下
基本用法Webpack 4 中需要使用到 optimization.splitChunks 的配置:
const path = require("path") module.exports = { mode: "development", entry: path.resolve(__dirname, "src/index.js"), output: { path: path.resolve(__dirname, "dist"), filename: "[name].[contenthash].js" }, optimization: { splitChunks: { chunks: "all" } } }
optimization.splitChunks 的意思是將所有來自 node_modules 中的依賴全部打包分離出來,這個時候我們再看打包的文件是個什么樣子:
增加了 splitChunks 的配置,我們第三方模塊都被打包到了 vendors~main.xxx.js 中去了,這個文件大小有 604k,而入口文件 main.xxx.js 則只有 7k
雖然說這樣將第三方模塊多帶帶打包出去能夠減小入口文件的大小,但這樣仍然是個不小的文件;這個小的測試項目中我們使用到了 axios 和 lodash 這兩個第三方模塊,因此我們希望的應該是將這兩個模塊多帶帶分離出來兩個文件,而不是全部放到一個 vendors 中去,那么我們繼續配置 webpack.config.js:
將每個 npm 包多帶帶分離出來這里我們需要使用到 webpack.HashedModuleIdsPlugin 這個插件
參考官方文檔
直接上代碼:
const path = require("path") const webpack = require("webpack") module.exports = { mode: "development", entry: path.resolve(__dirname, "src/index.js"), plugins: [ new webpack.HashedModuleIdsPlugin() // 根據模塊的相對路徑生成 HASH 作為模塊 ID ], output: { path: path.resolve(__dirname, "dist"), filename: "[name].[contenthash].js" }, optimization: { runtimeChunk: "single", splitChunks: { chunks: "all", // 默認 async 可選值 all 和 initial maxInitialRequests: Infinity, // 一個入口最大的并行請求數 minSize: 0, // 避免模塊體積過小而被忽略 minChunks: 1, // 默認也是一表示最小引用次數 cacheGroups: { vendor: { test: /[/]node_modules[/]/, // 如果需要的依賴特別小,可以直接設置成需要打包的依賴名稱 name(module, chunks, chcheGroupKey) { // 可提供布爾值、字符串和函數,如果是函數,可編寫自定義返回值 const packageName = module.context.match(/[/]node_modules[/](.*?)([/]|$)/)[1] // 獲取模塊名稱 return `npm.${packageName.replace("@", "")}` // 可選,一般情況下不需要將模塊名稱 @ 符號去除 } } } } } }
這里我們主要做了幾件事:
為了避免每次打包的文件哈希變化,我們可以使用 webpack 內置的 HashedModuleIdsPlugin,這樣可以避免每次打包的文件哈希值變化首先增加 maxInitialRequests 并設置成 Infinity,指定這個入口文件最大并行請求數
然后將 minSize 和 minChunks 分別設置成 0 和 1,即使模塊非常小也將其提取出來,并且這個模塊的引用次數只有 1 也要提取
最后配置匹配的依賴以及分離出的文件名格式
另外,我們還將運行時代碼分離出來,這塊代碼還可以配合 InlineManifestWebpackPlugin 直接插入到 HTML 文件中。這里我們將這個配置設置成 single,即將所有chunk的運行代碼打包到一個文件中
這樣 Bundle Split 的操作基本就完成了,讓我們看看效果如何:
所依賴的幾個模塊都被分離出去了
使用 HtmlWebpackPlugin 這個插件將 js 代碼注入 html 文件中
npm i -D html-webpack-plugin
修改 webpack.config.js 文件:
// 配置文件引入這個插件 var HtmlWebpackPlugin = require("html-webpack-plugin"); // ... module.exports = { // ... plugins: [ new HtmlWebpackPlugin(), new webpack.HashedModuleIdsPlugin() // 根據模塊的相對路徑生成 HASH 作為模塊 ID ], // ... }
安裝 http-server 或使用 vscode 的插件 Live Server 將代碼放入一個本地服務器中,打開瀏覽器的調試窗口進入到 Network 面板:
可以看到我們將模塊多帶帶分離出來并行加載,這樣比多帶帶加載一個龐大的包要快不少,接下來我們還要進行代碼分離,將不必要加載的模塊延遲加載Code Split
代碼分離實際上就是只加載用戶需要使用到的部分代碼,不是必須的就暫時不加載。
這里我們要用到 require.ensure 這個方法去獲取依賴,這樣 webpack 打包之后將會增加運行時代碼,在設定好的條件下才會觸發獲取這個依賴。
在 ES6 中我們可以用 Dynamic Imports 來替代上述方案,如果使用 ES6 語法那么需要使用到 babel 以及 babel 的插件 plugin-syntax-dynamic-import,在瀏覽器中為了保證兼容性,還需要安裝 promise 的 polyfill,用法大同小異,可直接觀摩 webpack 的官方文檔
修改我們的代碼:
const { getData } = require("./main") let arr = [1,2,123,21,3,21,321,1] getData("./index.html") setTimeout(() => { require.ensure(["./math"], function(require) { const { findMaxIndex } = require("./math") console.log(findMaxIndex(arr)) }) }, 3000)
我們設定了一個定時器,只有在 3000 毫秒以后,瀏覽器才會去請求這個 math 模塊
編譯之后,打開調試面板刷新瀏覽器:
在頁面剛加載完畢后,瀏覽器會嘗試獲取上述這么幾個模塊,因為模塊都很小很快就加載完成了
在 3500ms 左右,瀏覽器才會去獲取 requie.ensure 方法定義的 math 模塊,因 math 模塊又包含依賴 lodash,因此這個 lodash 第三方模塊也會被按需加載。
這樣我們就完成了代碼分離的操作,這樣做的優勢就是不需要第一時間加載的模塊,可以推遲加載,以頁面的加載速度。當然,上面的 timeout 定時器完全可以換成其他諸如按鈕點擊之類的事件來觸發。
END
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/101027.html
摘要:的和的是同步加載的,通過這些方式引入的依賴會被打包在一起,文件因而變大。而或的是按需加載異步的,對于一些可以延遲加載的模塊依賴,應該用這種方式,從而避免文件太大。 上一篇回顧使用 Webpack 的動機,比較理論,本篇側重實用。 這幾篇文章的前提是 Webpack 已經入門。若無,請自行到 Webpack 官方網站的 getting start 按指引一步步實操。 啃先生(MrKenn...
摘要:而一個哈希字符串就是根據文件內容產生的簽名,每當文件內容發生更改時,哈希串也就發生了更改,文件名也就隨之更改。很顯然這不是我們需要的,如果文件內容發生了更改,的打包文件的哈希應該發生變化,但是不應該。前言 隨著前端代碼需要處理的業務越來越繁重,我們不得不面臨的一個問題是前端的代碼體積也變得越來越龐大。這造成無論是在調式還是在上線時都需要花長時間等待編譯完成,并且用戶也不得不花額外的時間和帶寬...
摘要:支持定義分割點,通過進行按需加載。若按照中做,則會造成通用模塊重復打包。下文將詳細說明。同樣是利用和來處理的。如下在中添加入口其中模塊為通用功能模塊在中對應和這樣則會打包出和兩個文件。為通用功能模塊。希望有更好方案的同學能夠不吝賜教。 什么是code splitting 首先說,code splitting指什么。我們打包時通常會生成一個大的bundle.js(或者index,看你如...
摘要:關于模板的有好幾種。一次安裝所有的大家可以了解一些的用法把編譯成。安裝參考文檔功能將源文件遷移到指定的目錄,返回新的文件路徑。安裝用法它會將所有的入口中引用的移動到和頁面對應的獨立分離的文件。 webpack是需要自己編寫自己需要的一個配置對象,取決你如何使用webpack,下面指定了所有的可用的配置選項。參考文檔:https://doc.webpack-china.org... we...
摘要:不知大家是不是跟大雄一樣之前從未看過編譯產出的代碼。前文大雄給了一個粗陋的動態加載的方法說白了就是動態創建標簽。大雄看完至少大概知道了原來編出來的代碼是那樣執行的原來可以那么靈活的使用。 Code Splitting是webpack的一個重要特性,他允許你將代碼打包生成多個bundle。對多頁應用來說,它是必須的,因為必須要配置多個入口生成多個bundle;對于單頁應用來說,如果只打包...
閱讀 689·2023-04-25 19:53
閱讀 4262·2021-09-22 15:13
閱讀 2565·2019-08-30 10:56
閱讀 1320·2019-08-29 16:27
閱讀 2932·2019-08-29 14:00
閱讀 2407·2019-08-26 13:56
閱讀 426·2019-08-26 13:29
閱讀 1611·2019-08-26 11:31