成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

深入理解 Webpack 打包分塊(下)

pingan8787 / 3103人閱讀

摘要:例如允許我們在打包時(shí)將腳本分塊利用瀏覽器緩存我們能夠有的放矢的加載資源。文章的內(nèi)容大體分為兩個(gè)方面,一方面在思路制定模塊分離的策略,另一方面從技術(shù)上對方案進(jìn)行落地。我之前提到測試之下是什么樣具體的場景并不重要。

前言

隨著前端代碼需要處理的業(yè)務(wù)越來越繁重,我們不得不面臨的一個(gè)問題是前端的代碼體積也變得越來越龐大。這造成無論是在調(diào)式還是在上線時(shí)都需要花長時(shí)間等待編譯完成,并且用戶也不得不花額外的時(shí)間和帶寬下載更大體積的腳本文件。

然而仔細(xì)想想這完全是可以避免的:在開發(fā)時(shí)難道一行代碼的修改也要重新打包整個(gè)腳本?用戶只是粗略瀏覽頁面也需要將整個(gè)站點(diǎn)的腳本全部下載下來?所以趨勢必然是按需的、有策略性的將代碼拆分和提供給用戶。最近流行的微前端某種意義上來說也是遵循了這樣的原則(但也并不是完全基于這樣的原因)

幸運(yùn)的是,我們目前已有的工具已經(jīng)完全賦予我們實(shí)現(xiàn)以上需求的能力。例如 Webpack 允許我們在打包時(shí)將腳本分塊;利用瀏覽器緩存我們能夠有的放矢的加載資源。

在探尋最佳實(shí)踐的過程中,最讓我疑惑的不是我們能不能做,而是我們應(yīng)該如何做:我們因該采取什么樣的特征拆分腳本?我們應(yīng)該使用什么樣的緩存策略?使用懶加載和分塊是否有異曲同工之妙?拆分之后究竟能帶來多大的性能提升?最重要的是,在面多諸多的方案和工具以及不確定的因素時(shí),我們應(yīng)該如何開始?這篇文章就是對以上問題的梳理和回答。文章的內(nèi)容大體分為兩個(gè)方面,一方面在思路制定模塊分離的策略,另一方面從技術(shù)上對方案進(jìn)行落地。

本文的主要內(nèi)容翻譯自 The 100% correct way to split your chunks with Webpack。 這篇文章循序漸進(jìn)的引導(dǎo)開發(fā)者步步為營的對代碼進(jìn)行拆分優(yōu)化,所以它是作為本文的線索存在。同時(shí)在它的基礎(chǔ)上,我會對 Webpack 及其他的知識點(diǎn)做縱向擴(kuò)展,對方案進(jìn)行落地。

以下開始正文


把應(yīng)用代碼進(jìn)行分離

現(xiàn)在讓我們把目光轉(zhuǎn)向 Alice 一遍又一遍下載的 main.js 文件

我之前提到過我們的站點(diǎn)里又兩個(gè)完全不同的部分:一個(gè)產(chǎn)品列表頁面和一個(gè)詳情頁面。每個(gè)頁面獨(dú)立的代碼提及大概是 25KB(共享 150KB 的代碼)

我們的“產(chǎn)品詳情”頁面目前不會進(jìn)行更改,因?yàn)樗浅5耐昝?。所以如果我們把它劃分為?dú)立文件,大部分時(shí)候它都能夠從緩存中進(jìn)行加載

你知道我們還有一個(gè)用于渲染 icon 用的 25KB 的幾乎不發(fā)生修改的 SVG 文件嗎?我們應(yīng)該對它做些什么

我們手動(dòng)的增加一些 entry 入口,告訴 Webpack 給它們都創(chuàng)建獨(dú)立的文件:

module.exports = {
  entry: {
    main: path.resolve(__dirname, "src/index.js"),
    ProductList: path.resolve(__dirname, "src/ProductList/ProductList.js"),
    ProductPage: path.resolve(__dirname, "src/ProductPage/ProductPage.js"),
    Icon: path.resolve(__dirname, "src/Icon/Icon.js"),
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name].[contenthash:8].js",
  },
  plugins: [
    new webpack.HashedModuleIdsPlugin(), // so that file hashes don"t change unexpectedly
  ],
  optimization: {
    runtimeChunk: "single",
    splitChunks: {
      chunks: "all",
      maxInitialRequests: Infinity,
      minSize: 0,
      cacheGroups: {
        vendor: {
          test: /[/]node_modules[/]/,
          name(module) {
            // get the name. E.g. node_modules/packageName/not/this/part.js
            // or node_modules/packageName
            const packageName = module.context.match(/[/]node_modules[/](.*");)[1];

            // npm package names are URL-safe, but some servers don"t like @ symbols
            return `npm.${packageName.replace("@", "")}`;
          },
        },
      },
    },
  },
};

并且 Webpack 自動(dòng)為它們之間的共享代碼也創(chuàng)建了獨(dú)立的文件,也就是說ProductListProductPage不會擁有重復(fù)的代碼

這回 Alice 在大多數(shù)周里都會節(jié)省下 50KB 的下載量

只有 1.815MB 了

我們已經(jīng)為 Alice 節(jié)省了 56% 的下載量,并且會持續(xù)下去(在我們的理論場景中)

所有這些都是通過修改 Webapck 配置實(shí)現(xiàn)的——我們還沒有修改任何一行應(yīng)用程序的代碼。

我之前提到測試之下是什么樣具體的場景并不重要。因?yàn)闊o論你遇見的是什么場景,結(jié)論始終是一致的:把你的代碼劃分為更多更有意義的小文件,用戶需要下載的代碼也就越少


很快我們就將談到“代碼分離”——另一種分割文件的方式——但是首先我想首先解決你現(xiàn)在正在考慮的問題

網(wǎng)絡(luò)請求變多的時(shí)候是不是會變得更慢?

答案非常明確是否定的

在 HTTP/1.1 的情況下確實(shí)會如此,但是在 HTTP/2 中不會

盡管如此,這篇來自 2016 年的文章和來自于Khan Academy 2015 年的文章都得出結(jié)論說即使有 HTTP/2 下載太多文件的話仍然會導(dǎo)致變慢。但是在這兩篇文章里“太多”意味著上百個(gè)文件。所以只要記住如果你有上百個(gè)文件,你或許達(dá)到了并行的上限

如果你在好奇如何在 Windows 10 的 IE11 上支持 HTTP/2。我對那些還在使用古董機(jī)器的人做了調(diào)查,他們出奇一致的讓我放心他們根本不關(guān)心網(wǎng)站的加載速度

每一個(gè) webpack 打包后的文件里會不會有多余的模板代碼?

有的

但什么是“模板代碼”?

想象一下如果整個(gè)項(xiàng)目只有文件app.js,那么最終的輸出的打包文件也只是app.js的文件內(nèi)容而已。

但是如果app.js文件內(nèi)容是空的話(一行代碼都沒有),那么最終的打包文件也是空的嗎?

不是,Webpack 為了實(shí)現(xiàn)編譯之后的模塊化,它會將你的代碼進(jìn)行一次封裝,這些用于封裝的代碼會占用一部分體積,是每個(gè)模塊都必須存在的,所以成為模板代碼

如果我有多個(gè)小文件的話是不是壓縮的效果就減弱了

是的

事實(shí)確實(shí)是:

多文件 = 多 Webpack 模板代碼

多文件 = 壓縮減小

讓我們把其中的損耗的都明確下來

我剛剛做了一個(gè)測試,一個(gè) 190 KB 的站點(diǎn)文件被劃分為了19個(gè)文件,發(fā)送給瀏覽器的字節(jié)數(shù)大概多了 2%

所以……首次訪問的文件提及增加了 2% 但是直到世界末日其他的每次訪問文件體積都減小了 60%

所以損耗的正確數(shù)字是:一點(diǎn)都不。

當(dāng)我在測試 1 個(gè)文件對比 19 個(gè)文件情況時(shí),我想我應(yīng)該賦予測試一些不同的網(wǎng)絡(luò)環(huán)境,包括 HTTP/1.1

下面這張表格給予了“文件越多越好”的有力支持

在 3G 和 4G 的情況下當(dāng)有19個(gè)文件時(shí)加載時(shí)間減少了 30%

但真的是這樣嗎?

這份數(shù)據(jù)看上去“噪點(diǎn)”很多,舉個(gè)例子,在 4G 場景下第二次運(yùn)行時(shí),網(wǎng)站加載花費(fèi)了 646ms,但是之后的第二輪運(yùn)行則花費(fèi)了 1116ms——時(shí)間增加了73% 。所以宣稱 HTTP/2 快了 30% 有一些心虛

我創(chuàng)建這張表格是為了試圖量化 HTTP/2 究竟能帶來多大的差異,但是我唯一能說的是“并沒有太大的區(qū)別”

真正令人驚喜的是最后兩行,舊版本的 Windows 和 HTTP/1.1 我本以為會慢非常多。我猜我需要更慢的網(wǎng)絡(luò)環(huán)境用于進(jìn)行驗(yàn)證


故事時(shí)間!我從微軟網(wǎng)站下載了一個(gè) Windows 7 的虛擬機(jī)來測試這些東西

我想把默認(rèn)的 IE8 升級至 IE9

所以我前往微軟下載 IE9 的頁面然后發(fā)現(xiàn):

最后提一句 HTTP/2,你知道它已經(jīng)集成進(jìn) Node 中了嗎?如果你想嘗試,我用100行寫了一段 HTTP/2 服務(wù),能夠?yàn)槟愕臏y試帶來緩存上的幫助


以上就是我想說的關(guān)于打包分離的一切。我想這個(gè)實(shí)踐唯一的壞處是需要說服人們加載如此多的小文件是沒有問題的

代碼分離(不必加載你不需要的代碼)

這個(gè)特殊的實(shí)踐只對某些站點(diǎn)有效

我樂意重申一下我發(fā)明的 20/20 理論:如果站點(diǎn)的某些部分只有 20% 用戶會訪問,并且這部分的腳本量大于你整個(gè)站點(diǎn)的 20% 的話,你就應(yīng)該考慮按需加載代碼了

你可以對數(shù)值進(jìn)行調(diào)整來適配更復(fù)雜的場景。重點(diǎn)是保持平衡,需要決策將對站點(diǎn)無意義的代碼分離出來

如何決策

假設(shè)你有擁有一個(gè)購物網(wǎng)站,你在糾結(jié)是否應(yīng)該把“結(jié)賬”功能的代碼分離出來,因?yàn)橹挥?30% 的用戶會走到那一步

首先是要讓賣的更好

其次計(jì)算出“結(jié)賬”功能的獨(dú)立代碼有多少。因?yàn)樵谧觥按a分離”之前你常常做“打包文件分離”,你或許已經(jīng)知道了這部分代碼量有多少

(它可能比你想象的還要小,所以計(jì)算之后你可能獲得驚喜。如果你有一個(gè) React 站點(diǎn),你的 store,reducer,routing,actions 可能會被整個(gè)網(wǎng)站共享,獨(dú)立的部分可能大部分是組件和幫助類庫)

假設(shè)你注意到結(jié)算頁面獨(dú)立代碼一共只有 7KB,其他部分的代碼 300KB??吹竭@種情況我會建議不把這些代碼分開,有以下幾個(gè)原因

它并不會讓加載變得更慢。記得你之前并行的加載這些文件,你可以試著記錄加載 300KB 和 307KB 的文件是否有變化

如果你延遲加載這部分代碼,用戶在點(diǎn)擊“付款”之后仍然需要等待文件的加載——你并不希望在這關(guān)鍵時(shí)刻給用戶帶來任何的阻力

代碼分離會導(dǎo)致程序代碼的更改,這需要將之前同步邏輯的地方改為異步邏輯。這并不復(fù)雜,但是對于改善用戶體驗(yàn)這件事的性價(jià)比來說還是過于復(fù)雜了

這些就是我說的“這項(xiàng)令人振奮的技術(shù)或許不適合你”

讓我們看看兩個(gè)代碼分離的例子

回滾方案(Polyfills)

我們從這個(gè)例子開始是因?yàn)樗m用于大多數(shù)站點(diǎn),并且是一個(gè)非常好的入門

我給我的站點(diǎn)使用了一堆酷炫的功,所以我使用了一個(gè)文件導(dǎo)入了我需要的所有回滾方案。它只需要八行代碼:

require("whatwg-fetch");
require("intl");
require("url-polyfill");
require("core-js/web/dom-collections");
require("core-js/es6/map");
require("core-js/es6/string");
require("core-js/es6/array");
require("core-js/es6/object");

我在我的入口文件index.js頂部引入了這個(gè)文件

import "./polyfills";
import React from "react";
import ReactDOM from "react-dom";
import App from "./App/App";
import "./index.css";

const render = () => {
  ReactDOM.render(<App />, document.getElementById("root"));
}

render(); // yes I am pointless, for now

在 Webpack 配置關(guān)于打包分離的小節(jié)配置中,我的回滾代碼會自動(dòng)被分為四個(gè)不同的文件因?yàn)橛兴膫€(gè) npm 包。它們一共大小 25KB 左右,并且 90% 的瀏覽器都不需要它們,所以它們值得動(dòng)態(tài)的進(jìn)行加載。

在 Webpack 4 以及 import() 語法(不要和import語法混淆了)的支持下,有條件的加載回滾代碼變得非常簡單了

import React from "react";
import ReactDOM from "react-dom";
import App from "./App/App";
import "./index.css";

const render = () => {
  ReactDOM.render(<App />, document.getElementById("root"));
}

if (
  "fetch" in window &&
  "Intl" in window &&
  "URL" in window &&
  "Map" in window &&
  "forEach" in NodeList.prototype &&
  "startsWith" in String.prototype &&
  "endsWith" in String.prototype &&
  "includes" in String.prototype &&
  "includes" in Array.prototype &&
  "assign" in Object &&
  "entries" in Object &&
  "keys" in Object
) {
  render();
} else {
  import("./polyfills").then(render);
}

現(xiàn)在是不是更有意義了?如果瀏覽器支持所有的新特性,那么渲染頁面。否則加載回滾代碼渲染頁面。當(dāng)代碼在運(yùn)行在瀏覽器中時(shí),Webpack 的運(yùn)行時(shí)會負(fù)責(zé)這四個(gè)包的加載,并且當(dāng)它們被下載并且解析完畢時(shí),render()函數(shù)才會被調(diào)用,并且其它工作繼續(xù)運(yùn)行

(順便說一聲,如果需要使用import()的話,你需要 Babel 的 dynamic-import 插件 。并且如 Webpack 文檔解釋的,import()使用 Promises,所以你需要把這部分的回滾代碼獨(dú)立出來)

非常簡單不是嗎?

有一些更棘手的場景

基于路由的動(dòng)態(tài)加載(針對 React)

回到 Alice 的例子,假設(shè)網(wǎng)站現(xiàn)在多了一個(gè)“管理”頁面,產(chǎn)品的賣家可以登陸并且管理他們售賣的產(chǎn)品

這個(gè)頁面有很多有用的功能,很多的圖表,需要安裝一個(gè)來自 npm 的表單類庫。因?yàn)槲乙呀?jīng)實(shí)現(xiàn)了打包代碼分離,目測至少已經(jīng)節(jié)省了100KB 的大小文件

現(xiàn)在我設(shè)置了一份當(dāng)用戶訪問呢/admin時(shí)渲染的路由。當(dāng) Webpack 把一切都打包完畢之后,它會去查找import AdminPage from "./AdminPage.js",并且說“嘿,我需要把它包含到初始化的加載文件中”

但是我們不想這么做,我們希望在動(dòng)態(tài)加載中加載管理頁面,比如import("./AdminPage.js"),這樣 Webpack 就知道需要?jiǎng)討B(tài)加載它。

非???,不需要任何的配置

與直接引用AdminPage不同,當(dāng)用戶訪問/admin時(shí)我使用另外一個(gè)組件用于實(shí)現(xiàn)如下功能:

核心思想非常簡單,當(dāng)組件加載時(shí)(也就意味著用戶訪問/admin時(shí)),我們動(dòng)態(tài)的加載./AdminPage.js然后在組件 state 中保存對它的引用

在渲染函數(shù)中,在等待>加載的過程中我們簡單的渲染出

Loading...
,一旦加載成功則渲染出

為了好玩我想自己實(shí)現(xiàn)它,但是在真實(shí)的世界里你只需要像React 關(guān)于代碼分離的文檔描述的那樣使用 react-loadable即可


以上就是所有內(nèi)容了。以上我說的每一個(gè)觀點(diǎn),還能說的更精簡嗎?

如果人們會不止一次的訪問你的站點(diǎn),把你的代碼劃分為不同的小文件

如果你的站點(diǎn)有很大一部分用戶不會訪問到,動(dòng)態(tài)的加載它們

謝謝閱讀,祝你有愉快的一天

完蛋了我忘記提 CSS 了

關(guān)于開發(fā)體驗(yàn)

以上我們都是在針對 production 對代碼進(jìn)行分割。但事實(shí)上我們在開發(fā)過程中也會面臨同樣的問題:當(dāng)代碼量增多時(shí),打包的時(shí)間也在不斷增長。但是例如 node_modules 里的代碼千年不變,完全不需要被重新編譯。這部分我們也可以通過代碼分離的思想對代碼進(jìn)行分離。比如 DLL 技術(shù)

通常我們說的 DLL 指的是 Windows 系統(tǒng)的下的動(dòng)態(tài)鏈接庫文件,它的本意是將公共函數(shù)庫提取出來給大家公用以減少程序體積。我們的 DLL 也是借助了這種思想,將公共代碼分離出來。

使用 DLL 簡單來說分為兩步:

輸出 DLL 文件

我們將我們需要分離的文件到打包為 DLL 文件,以分離 node_modules 類庫為例,關(guān)鍵配置如下。注意這段配置僅僅是用于分離 dll 文件,并非打包應(yīng)用腳本

module.exports = {
   entry: {
      library: [
         "react",
         "redux",
         "jquery",
         "d3",
         "highcharts",
         "bootstrap",
         "angular"
      ]
   },
   output: {
      filename: "[name].dll.js",
      path: path.resolve(__dirname, "./build/library"),
      library: "[name]"
   },
   plugins: [
    new webpack.DllPlugin({
        name: "[name]",
        path: "./build/library/[name].json"
    })
  ]
};

關(guān)鍵在于使用 DLLPlugin 輸出的 json 文件,用于告訴 webpack 從哪找到預(yù)編譯的類庫代碼

引入 DLL 文件

在正式打包應(yīng)用腳本的 Webpack 配置中引入 DLL 即可:

plugins: [
  new webpack.DllReferencePlugin({
    context: __dirname,
    manifest: require("./build/library/library.json")
  })
]

不過美中不足的是,你仍然需要在你最終的頁面里引入 dll 文件

如果你的覺得手動(dòng)配置 dll 仍然覺得繁瑣,那么可以嘗試使用 AutoDllPlugin

本文同時(shí)也發(fā)布在我的知乎專欄,歡迎大家關(guān)注

參考資料 Bundle VS Chunk

What are module, chunk and bundle in webpack");

Concepts - Bundle vs Chunk

SurviveJS: Glossary

Hash

What is the purpose of webpack hash and chunkhash");

Hash vs chunkhash vs ContentHash

Adding Hashes to Filenames

SplitChunksPlugin

Webpack 4?—?Mysterious SplitChunks Plugin

Webpack (v4) Code Splitting using SplitChunksPlugin

Reduce JavaScript Payloads with Code Splitting

Webpack v4 chunk splitting deep dive

what reuseExistingChunk: true means, can give a sample"); DLL

How To Use The Dll Plugin to Speed Up Your Webpack Build

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/6875.html

相關(guān)文章

  • 深入理解 Webpack 打包分塊(上)

    摘要:而一個(gè)哈希字符串就是根據(jù)文件內(nèi)容產(chǎn)生的簽名,每當(dāng)文件內(nèi)容發(fā)生更改時(shí),哈希串也就發(fā)生了更改,文件名也就隨之更改。很顯然這不是我們需要的,如果文件內(nèi)容發(fā)生了更改,的打包文件的哈希應(yīng)該發(fā)生變化,但是不應(yīng)該。前言 隨著前端代碼需要處理的業(yè)務(wù)越來越繁重,我們不得不面臨的一個(gè)問題是前端的代碼體積也變得越來越龐大。這造成無論是在調(diào)式還是在上線時(shí)都需要花長時(shí)間等待編譯完成,并且用戶也不得不花額外的時(shí)間和帶寬...

    Rocko 評論0 收藏0
  • 關(guān)于vue的懶加載實(shí)踐

    摘要:最近在研究的按需加載,好奇怪,之前好像并沒有看到的官文里面有這一部分,是我看差了嗎尬笑其實(shí)只需要看官文就可以了,里面有懶加載的講解,并且附帶了詳細(xì)內(nèi)容的連接。所以很大程度上優(yōu)化了頁面的初始加載速度。只是為了測試按需加載隨便寫的而已。 最近在研究vue的按需加載,好奇怪,之前好像并沒有看到vue的官文里面有這一部分,是我看差了嗎hahaha~尬笑~ 其實(shí)只需要看vue-router官文就...

    wangzy2019 評論0 收藏0
  • 談?wù)勄岸斯こ袒?js加載

    摘要:當(dāng)年的加載在沒有前端工程化之前,基本上是我們是代碼一把梭,把所需要的庫和自己的代碼堆砌在一起,然后自上往下的引用就可以了。而且對于前后端的技術(shù)要求較高,所以對于項(xiàng)目未必是最有效的方案。 當(dāng)年的 js 加載 在沒有 前端工程化之前,基本上是我們是代碼一把梭,把所需要的庫和自己的代碼堆砌在一起,然后自上往下的引用就可以了。 那個(gè)時(shí)代我們沒有公用的cdn,也沒有什么特別好的方法來優(yōu)化加載j...

    paulli3 評論0 收藏0
  • webpack再看一遍

    摘要:在終端中使用可以自動(dòng)創(chuàng)建這個(gè)文件輸入這個(gè)命令后,終端會問你一系列問題。百度后發(fā)現(xiàn)引入了模式,有三個(gè)狀態(tài),開發(fā)模式生產(chǎn)模式無。 什么是webpack,為什么要使用webapck * 導(dǎo)語 之前一直忙著項(xiàng)目,沒時(shí)間整理自己的東西,最近剛好發(fā)現(xiàn)自己對webpack又如此陌生了,于是整理了一篇關(guān)于webpack初探的干貨,這里是一點(diǎn)簡單的webpack配置 為什么使用webpck 現(xiàn)今很多網(wǎng)頁...

    whinc 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<