【前端性能優(yōu)化指南】5.1 - 優(yōu)化你的 JavaScript

?? 靜態(tài)資源優(yōu)化的總體思路

image.png

隨著 Web 的發(fā)展,JavaScript 從以前只承擔(dān)簡(jiǎn)單的腳本功能,到現(xiàn)在被用于構(gòu)建大型、復(fù)雜的前端應(yīng)用,經(jīng)歷了很大的發(fā)展。這也讓它在當(dāng)下的前端應(yīng)用中扮演了一個(gè)非常重要的角色,因此在這一節(jié)首先來(lái)看看的我們熟悉的 JavaScript。

1. 減少不必要的請(qǐng)求

在進(jìn)行 JavaScript 優(yōu)化時(shí),我們還是秉承總體思路,首先就是減少不必要的請(qǐng)求。

1.1. 代碼拆分(code split)與按需加載

相信熟練使用 webpack 的同學(xué)對(duì)這一特性都不陌生。

雖然整體應(yīng)用的代碼非常多,但是很多時(shí)候,我們?cè)谠L(fǎng)問(wèn)一個(gè)頁(yè)面時(shí),并不需要把其他頁(yè)面的組件也全部加載過(guò)來(lái),完全可以等到訪(fǎng)問(wèn)其他頁(yè)面時(shí),再按需去動(dòng)態(tài)加載。核心思路如下所示:

document.getElementById('btn').addEventListener('click', e => {
    // 在這里加載 chat 組件相關(guān)資源 chat.js
    const script = document.createElement('script');
    script.src = '/static/js/chat.js';
    document.getElementsByTagName('head')[0].appendChild(script);
});

在按鈕點(diǎn)擊的監(jiān)聽(tīng)函數(shù)中,我動(dòng)態(tài)添加了 <script> 元素。這樣就可以實(shí)現(xiàn)在點(diǎn)擊按鈕時(shí),才加載對(duì)應(yīng)的 JavaScript 腳本。

代碼拆分一般會(huì)配合構(gòu)建工具一起使用。以 webpack 為例,在日常使用時(shí),最常見(jiàn)的方式就是通過(guò) dynamic import[1] 來(lái)告訴 webpack 去做代碼拆分。webpack 編譯時(shí)會(huì)進(jìn)行語(yǔ)法分析,之后遇到 dynamic import 就會(huì)認(rèn)為這個(gè)模塊是需要?jiǎng)討B(tài)加載的。相應(yīng)的,其子資源也會(huì)被如此處理(除非被其他非動(dòng)態(tài)模塊也引用了)。

在 webpack 中使用代碼拆分最常見(jiàn)的一個(gè)場(chǎng)景是基于路由的代碼拆分。目前很多前端應(yīng)用都在使用 SPA(單頁(yè)面應(yīng)用)形式,或者 SPA 與 MPA(多頁(yè)面應(yīng)用)的結(jié)合體,這就會(huì)涉及到前端路由。而頁(yè)面間的業(yè)務(wù)差異也讓基于路由的代碼拆分成為一個(gè)最佳實(shí)踐。想了解如何在 react-router v4 中實(shí)現(xiàn)路由級(jí)別的代碼拆分,可以看這篇文章[2]。

當(dāng)然,如果你不使用 webpack 之類(lèi)的構(gòu)建工具,你也可以選擇一個(gè) AMD 模塊加載器(例如 RequireJS)來(lái)實(shí)現(xiàn)前端運(yùn)行時(shí)上的異步依賴(lài)加載。

1.2. 代碼合并

我們?cè)诳傮w思路里有提到,減少請(qǐng)求的一個(gè)方法就是合并資源。試想一個(gè)極端情況:我們現(xiàn)在不對(duì) node_modules 中的代碼進(jìn)行打包合并,那么當(dāng)我們請(qǐng)求一個(gè)腳本之前將可能會(huì)并發(fā)請(qǐng)求數(shù)十甚至上百個(gè)依賴(lài)的腳本庫(kù)。同域名下的并發(fā)請(qǐng)求數(shù)過(guò)高會(huì)導(dǎo)致請(qǐng)求排隊(duì),同時(shí)還可能受到 TCP/IP 慢啟動(dòng)的影響。

當(dāng)然,在很多流行的構(gòu)建工具中(webpack/Rollup/Parcel),是默認(rèn)會(huì)幫你把依賴(lài)打包到一起的。不過(guò)當(dāng)你使用其他一些工具時(shí),就要注意了。例如使用 FIS3 時(shí),就需要通過(guò)配置聲明,將一些 common 庫(kù)或 npm 依賴(lài)進(jìn)行打包合并。又或者使用 Gulp 這樣的工具,也需要注意進(jìn)行打包。

總之,千萬(wàn)不要讓你的碎文件散落一地。

2. 減少包體大小

2.1. 代碼壓縮

JavaScript 代碼壓縮比較常見(jiàn)的做法就是使用 UglifyJS 做源碼級(jí)別的壓縮。它會(huì)通過(guò)將變量替換為短命名、去掉多余的換行符等方式,在盡量不改變?cè)创a邏輯的情況下,做到代碼體積的壓縮?;疽呀?jīng)成為了前端開(kāi)發(fā)的標(biāo)配。在 webpack 的 production 模式下是默認(rèn)開(kāi)啟的;而在 Gulp 這樣的任務(wù)流管理工具上也有 gulp-uglify 這樣的功能插件。

另一個(gè)代碼壓縮的常用手段是使用一些文本壓縮算法,gzip 就是常用的一種方式。

響應(yīng)頭

上圖中響應(yīng)頭的 Content-Encoding 表示其使用了 gzip。

壓縮效果

深色的數(shù)字表示壓縮后的大小為 22.0KB,淺色部分表示壓縮前的大小為 91.9KB,壓縮比還是挺大的,很有效果。一般服務(wù)器都會(huì)內(nèi)置相應(yīng)模塊來(lái)進(jìn)行 gzip 處理,不需要我們單獨(dú)編寫(xiě)壓縮算法模塊。例如在 Nginx 中就包含了 ngx_http_gzip_module[3] 模塊,通過(guò)簡(jiǎn)單的配置就可以開(kāi)啟。

gzip            on;
gzip_min_length 1000;
gzip_comp_level 6;
gzip_types      application/javascript application/x-javascript text/javascript;

2.2. Tree Shaking

Tree Shaking 最早進(jìn)入到前端的視線(xiàn)主要是因?yàn)?Rollup。后來(lái)在 webpack 中也被實(shí)現(xiàn)了。其本質(zhì)是通過(guò)檢測(cè)源碼中不會(huì)被使用到的部分,將其刪除,從而減小代碼的體積。例如:

// 模塊 A
export function add(a, b) {
    return a + b;
}

export function minus(a, b) {
    return a - b;
}
// 模塊 B
import {add} from 'module.A.js';
console.log(add(1, 2));

可以看到,模塊 B 引用了模塊 A,但是只使用了 add 方法。因此 minus 方法相當(dāng)于成為了 Dead Code,將它打包進(jìn)去沒(méi)有意義,該方法是永遠(yuǎn)不會(huì)被使用到的。

注意,我在上面的代碼中使用了 ESM 規(guī)范的模塊語(yǔ)法,而沒(méi)有使用 CommonJS。這主要是由于 Tree Shaking 算是一種靜態(tài)分析,而 ESM 本身是一種的靜態(tài)的模塊化規(guī)范,所有依賴(lài)可以在編譯期確定。如果想要更好得在 webpack 中使用,可以在查看其官網(wǎng)上的這部分內(nèi)容[4]。關(guān)于 Tree Shaking 的介紹也可以從這里了解下[5]。

注意,剛才說(shuō)了 Tree Shaking 非常依賴(lài)于 ESM。像是前端流行的工具庫(kù) lodash 一般直接安裝的版本是非 ESM 的,為了支持 Tree Shaking,我們需要去安裝它的 ESM 版本 —— lodash-es 來(lái)實(shí)現(xiàn) Tree Shaking[6]

此外,Chrome DevTools 也可以幫助你查看加載的 JavaScript 代碼的使用覆蓋率[7]。

2.3. 優(yōu)化 polyfill 的使用

前端技術(shù)的一大特點(diǎn)就是需要考慮兼容性。為了讓大家能順暢地使用瀏覽器的新特性,一些程序員們開(kāi)發(fā)了新特性對(duì)應(yīng)的 polyfill,用于在非兼容瀏覽器上也能使用新特性的 API。后續(xù)升級(jí)不用改動(dòng)業(yè)務(wù)代碼,只需要?jiǎng)h除相應(yīng)的 polyfill 即可。

這種舒適的開(kāi)發(fā)體驗(yàn)也讓 polyfill 成為了很多項(xiàng)目中不可或缺的一份子。然而 polyfill 也是有代價(jià)的,它增加了代碼的體積。畢竟 polyfill 也是 JavaScript 寫(xiě)的,不是內(nèi)置在瀏覽器中,引入的越多,代碼體積也越大。所以,只加載真正所需的 polyfill 將會(huì)幫助你減小代碼體積。

首先,不是每個(gè)業(yè)務(wù)的兼容性要求都一樣。因此,按你業(yè)務(wù)的場(chǎng)景來(lái)確定引入哪些 polyfill 是最合適的。然而,特性千千萬(wàn),手動(dòng) import 或者添加 Babel Transformer 顯然是一件成本極高的事。針對(duì)這點(diǎn),我們可以通過(guò) browserslist 來(lái)幫忙,許多前端工具(babel-preset-env/autoprefixer/eslint-plugin-compat)都依賴(lài)于它。使用方式可以看這里

其次,在 Chrome Dev Summit 2018 上還介紹了一種 Differential Serving[8] 的技術(shù),通過(guò)瀏覽器原生模塊化 API 來(lái)盡量避免加載無(wú)用 polyfill。

<script type="module" src="main.mjs"></script>
<script nomodule src="legacy.js"></script>

這樣,在能夠處理 module 屬性的瀏覽器(具有很多新特性)上就只需加載 main.mjs(不包含 polyfill),而在老式瀏覽器下,則會(huì)加載 legacy.js(包含 polyfill)。

最后,其實(shí)在理想上,polyfill 最優(yōu)的使用方式應(yīng)該是根據(jù)瀏覽器特性來(lái)分發(fā),同一個(gè)項(xiàng)目在不同的瀏覽器,會(huì)加載不同的 polyfill 文件。例如 Polyfill.io 就會(huì)根據(jù)請(qǐng)求頭中的客戶(hù)端特性與所需的 API 特性來(lái)按實(shí)際情況返回必須的 polyfill 集合。

2.4. webpack

webpack 現(xiàn)在已經(jīng)成為很多前端應(yīng)用的構(gòu)建工具,因此這里單獨(dú)將其列了出來(lái)。我們可以通過(guò) webpack-bundle-analyzer 這個(gè)工具來(lái)查看打包代碼里面各個(gè)模塊的占用大小。

webpack-bundle-analyzer

很多時(shí)候,打包體積過(guò)大主要是因?yàn)橐肓瞬缓线m的包,對(duì)于如何優(yōu)化依賴(lài)包的引入,這里有一些建議可以幫助你減小 bundle 的體積[9]。

3. 解析與執(zhí)行

除了 JavaScript 下載需要耗時(shí)外,腳本的解析與執(zhí)行也是會(huì)消耗時(shí)間的。

3.1. JavaScript 的解析耗時(shí)

很多情況下,我們會(huì)忽略 JavaScript 文件的解析。一個(gè) JavaScript 文件,即使內(nèi)部沒(méi)有所謂的“立即執(zhí)行函數(shù)”,JavaScript 引擎也是需要對(duì)其進(jìn)行解析和編譯的。

js 處理

上圖可以看出,解析與編譯消耗了好幾百毫秒。所以換一個(gè)角度來(lái)說(shuō),刪除不必要的代碼,對(duì)于降低 Parse 與 Compile 的負(fù)載也是很有幫助的。

同時(shí),我們從前一節(jié)已經(jīng)知道,JavaScript 的解析、編譯和執(zhí)行會(huì)阻塞頁(yè)面解析,延遲用戶(hù)交互。所以有時(shí)候,加載同樣字節(jié)數(shù)的 JavaScript 對(duì)性能的影響可能會(huì)高于圖片,因?yàn)閳D片的處理可以放在其他線(xiàn)程中并行執(zhí)行。

3.2. 避免 Long Task

對(duì)于一些單頁(yè)應(yīng)用,在加載完核心的 JavaScript 資源后,可能會(huì)需要執(zhí)行大量的邏輯。如果處理不好,可能會(huì)出現(xiàn) JavaScript 線(xiàn)程長(zhǎng)時(shí)間執(zhí)行而阻塞主線(xiàn)程的情況。

long task

例如在上圖中,幀率下降明顯的地方出現(xiàn)了 Long Task,伴隨著的是有一段超過(guò) 700 ms 的腳本執(zhí)行時(shí)間。而性能指標(biāo) FCP 與 DCL 處于其后,一定程度上可以認(rèn)為,這個(gè) Long Task 阻塞了主線(xiàn)程并拖慢了頁(yè)面的加載時(shí)間,嚴(yán)重影響了前端性能與體驗(yàn)。

想要了解更多關(guān)于 Long Task 的內(nèi)容,可以看看 Long Task 相關(guān)的標(biāo)準(zhǔn)[10]。

3.3. 是否真的需要框架

相信如果現(xiàn)在問(wèn)大家,我們是否需要 React、Vue、Angular 或其他前端框架(庫(kù)),大概率是肯定的。

但是我們可以換個(gè)角度來(lái)思考這個(gè)問(wèn)題。類(lèi)庫(kù)/框架幫我們解決的問(wèn)題之一是快速開(kāi)發(fā)與后續(xù)維護(hù)代碼,很多時(shí)候,類(lèi)庫(kù)/框架的開(kāi)發(fā)者是需要在可維護(hù)性、易用性和性能上做取舍的。對(duì)于一個(gè)復(fù)雜的整站應(yīng)用,使用框架給你的既定編程范式將會(huì)在各個(gè)層面提升你工作的質(zhì)量。但是,對(duì)于某些頁(yè)面,我們是否可以反其道行之呢?

例如產(chǎn)品經(jīng)理反饋,咱們的落地頁(yè)加載太慢了,用戶(hù)容易流失。這時(shí)候你會(huì)開(kāi)始優(yōu)化性能,用上這次「性能之旅」里的各種措施。但你有沒(méi)有考慮過(guò),對(duì)于像落地頁(yè)這樣的、類(lèi)似靜態(tài)頁(yè)的頁(yè)面,是不是可以“返璞歸真”?

也許你使用了 React 技術(shù)棧 —— 你加載了 React、Redux、React-Redux、一堆 Reducers…… 好吧,整個(gè) JavaScript 可能快 1MB 了。更重要的是,這個(gè)頁(yè)面如果是用于拉新的,這也代表著訪(fǎng)問(wèn)者并沒(méi)有緩存可以用。好吧,為了一個(gè)靜態(tài)頁(yè)(或者還有一些非常簡(jiǎn)單的表單交互),用戶(hù)付出了高額的成本,而原本這只需要 50 行不到的代碼。所以有時(shí)候考慮使用原生 JavaScript 來(lái)實(shí)現(xiàn)它也是一種策略。Netflix 有一篇文章介紹了他們是如何通過(guò)這種方式大幅縮減加載與操作響應(yīng)時(shí)間的[11]。

當(dāng)然,還是強(qiáng)調(diào)一下,并不是說(shuō)不要使用框架/類(lèi)庫(kù),只是希望大家不要拘泥于某個(gè)思維定式。做工具的主人,而不是工具的“奴隸”。

3.4. 針對(duì)代碼的優(yōu)化

<font style="color:#d65">請(qǐng)注意,截止目前(2019.08)以下內(nèi)容不建議在生產(chǎn)環(huán)境中使用。</font>

還有一種優(yōu)化思路是把代碼變?yōu)樽顑?yōu)狀態(tài)。它其實(shí)算是一種編譯優(yōu)化。在一些編譯型的靜態(tài)語(yǔ)言上(例如 C++),通過(guò)編譯器進(jìn)行一些優(yōu)化非常常見(jiàn)。

這里要提到的就是 facebook 推出的 Prepack。例如下面一段代碼:

(function () {
    function hello() {return 'hello';}
    function world() {return 'world';}
    global.s = hello() + ' ' + world();
})();

可以?xún)?yōu)化為:

s = 'hello world';

不過(guò)很多時(shí)候,代碼體積和運(yùn)行性能是會(huì)有矛盾的。同時(shí) Prepack 也還不夠成熟,所以不建議在生產(chǎn)環(huán)境中使用。

4. 緩存

JavaScript 部分的緩存與我們?cè)诘谝徊糠掷锾岬降木彺婊疽恢拢绻阌洸惶辶耍?a target="_blank">可以回到咱們的第一站。

4.1. 發(fā)布與部署

這里簡(jiǎn)單提一下:大多數(shù)情況下,我們對(duì)于 JavaScript 與 CSS 這樣的靜態(tài)資源,都會(huì)啟動(dòng) HTTP 緩存。當(dāng)然,可能使用強(qiáng)緩存,也可能使用協(xié)商緩存。當(dāng)我們?cè)趶?qiáng)緩存機(jī)制上發(fā)布了更新的時(shí)候,如何讓瀏覽器棄用緩存,請(qǐng)求新的資源呢?

一般會(huì)有一套配合的方式:首先在文件名中包含文件內(nèi)容的 Hash,內(nèi)容修改后,文件名就會(huì)變化;同時(shí),設(shè)置不對(duì)頁(yè)面進(jìn)行強(qiáng)緩存,這樣對(duì)于內(nèi)容更新的靜態(tài)資源,由于 uri 變了,肯定不會(huì)再走緩存,而沒(méi)有變動(dòng)的資源則仍然可以使用緩存。

上面說(shuō)的主要涉及前端資源的發(fā)布和部署,詳細(xì)可以看這篇內(nèi)容[12],這里就不展開(kāi)了。

4.2. 將基礎(chǔ)庫(kù)代碼打包合并

為了更好利用緩存,我們一般會(huì)把不容易變化的部分單獨(dú)抽取出來(lái)。例如一個(gè) React 技術(shù)棧的項(xiàng)目,可能會(huì)將 React、Redux、React-Router 這類(lèi)基礎(chǔ)庫(kù)單獨(dú)打包出一個(gè)文件。

這樣做的優(yōu)點(diǎn)在于,由于基礎(chǔ)庫(kù)被單獨(dú)打包在一起了,即使業(yè)務(wù)代碼經(jīng)常變動(dòng),也不會(huì)導(dǎo)致整個(gè)緩存失效。基礎(chǔ)框架/庫(kù)、項(xiàng)目中的 common、util 仍然可以利用緩存,不會(huì)每次發(fā)布新版都會(huì)讓用戶(hù)花費(fèi)不必要的帶寬重新下載基礎(chǔ)庫(kù)。

所以一種常見(jiàn)的策略就是將基礎(chǔ)庫(kù)這種 Cache 周期較長(zhǎng)的內(nèi)容單獨(dú)打包在一起,利用緩存減少新版本發(fā)布后用戶(hù)的訪(fǎng)問(wèn)速度。這種方法本質(zhì)上是將緩存周期不同的內(nèi)容分離了,隔離了變化。

webpack 在 v3.x 以及之前,可以通過(guò) CommonChunkPlugin 來(lái)分離一些公共庫(kù)。而升級(jí)到 v4.x 之后有了一個(gè)新的配置項(xiàng) optimization.splitChunks:

// webpack.config.js
module.exports = {
    //...
    optimization: {
        splitChunks: {
            chunks: 'all',
            minChunks: 1,
            cacheGroups: {
                commons: {
                    minChunks: 1,
                    automaticNamePrefix: 'commons',
                    test: /[\\/]node_modules[\\/]react|redux|react-redux/,
                    chunks: 'all'
                }
            }
        }
    }
}

4.3. 減少 webpack 編譯不當(dāng)帶來(lái)的緩存失效

由于 webpack 已經(jīng)成為前端主流的構(gòu)建工具,因此這里再特別提一下使用 webpack 時(shí)的一些注意點(diǎn),減少一些不必要的緩存失效。

我們知道,對(duì)于每個(gè)模塊 webpack 都會(huì)分配一個(gè)唯一的模塊 ID,一般情況下 webpack 會(huì)使用自增 ID。這就可能導(dǎo)致一個(gè)問(wèn)題:一些模塊雖然它們的代碼沒(méi)有變化,但由于增/刪了新的其他模塊,導(dǎo)致后續(xù)所有的模塊 ID 都變更了,文件 MD5 也就變化了。另一個(gè)問(wèn)題在于,webpack 的入口文件除了包含它的 runtime、業(yè)務(wù)模塊代碼,同時(shí)還有一個(gè)用于異步加載的小型 manifest,任何一個(gè)模塊的變化,最后必然會(huì)傳導(dǎo)到入口文件。這些都會(huì)使得網(wǎng)站發(fā)布后,沒(méi)有改動(dòng)源碼的資源也會(huì)緩存失效。

規(guī)避這些問(wèn)題有一些常用的方式。

4.3.1. 使用 Hash 來(lái)替代自增 ID

你可以使用 HashedModuleIdsPlugin 插件,它會(huì)根據(jù)模塊的相對(duì)路徑來(lái)計(jì)算 Hash 值。當(dāng)然,你也可以使用 webpack 提供的 optimization.moduleIds,將其設(shè)置為 hash,或者選擇其他合適的方式。

4.3.2. 將 runtime chunk 單獨(dú)拆分出來(lái)

通過(guò) optimization.runtimeChunk 配置可以讓 webpack 把包含 manifest 的 runtime 部分單獨(dú)分離出來(lái),這樣就可以盡可能限制變動(dòng)影響的文件范圍。

// webpack.config.js
module.exports = {
    //...
    optimization: {
        runtimeChunk: {
            name: 'runtime'
        }
    },
}

如果你對(duì) webpack 模塊化 runtime 運(yùn)行的原理不太了解,可以看看這篇文章[13]。

4.3.3. 使用 records

你可以通過(guò) recordsPath 配置來(lái)讓 webpack 產(chǎn)出一個(gè)包含模塊信息記錄的 JSON 文件,其中包含了一些模塊標(biāo)識(shí)的信息,可以用于之后的編譯。這樣在后續(xù)的打包編譯時(shí),對(duì)于被拆分出來(lái)的 Bundle,webpack 就可以根據(jù) records 中的信息來(lái)盡量避免破壞緩存。

// webpack.config.js
module.exports = {
  //...
  recordsPath: path.join(__dirname, 'records.json')
};

如果對(duì)上述避免或減少緩存失效的方法感興趣,也可以再讀一讀這篇文章14。在 webpack v5.x 的計(jì)劃中,也有針對(duì) module 和 chunk ID 的一些工作計(jì)劃來(lái)提高長(zhǎng)期緩存。


「性能優(yōu)化」系列內(nèi)容

  1. 帶你全面掌握前端性能優(yōu)化 ??

  2. 如何利用緩存減少遠(yuǎn)程請(qǐng)求?

  3. 如何加快請(qǐng)求速度?

  4. 如何加速頁(yè)面解析與處理?

  5. 靜態(tài)資源優(yōu)化的總體思路是什么?

    5.1. 如何針對(duì) JavaScript 進(jìn)行性能優(yōu)化?(本文)

    5.2. ?? 如何針對(duì) CSS 進(jìn)行性能優(yōu)化?

    5.3. 圖片雖好,但也會(huì)帶來(lái)性能問(wèn)題

    5.4. 字體也需要性能優(yōu)化么?

    5.5. 如何針對(duì)視頻進(jìn)行性能優(yōu)化?

  6. 如何避免運(yùn)行時(shí)的性能問(wèn)題?

  7. 如何通過(guò)預(yù)加載來(lái)提升性能?

  8. 尾聲

目前內(nèi)容已全部更新至 ? fe-performance-journey ? 倉(cāng)庫(kù)中,陸續(xù)會(huì)將內(nèi)容同步到掘金上。如果希望盡快閱讀相關(guān)內(nèi)容,也可以直接去該倉(cāng)庫(kù)中瀏覽。


參考資料

  1. Proposal Dynamic Import
  2. 在 react-router4 中進(jìn)行代碼拆分
  3. Module ngx_http_gzip_module
  4. Tree Shaking - webpack
  5. Tree Shaking 性能優(yōu)化實(shí)踐 - 原理篇
  6. Tree Shaking for Lodash
  7. CSS and JS code coverage - Chrome DevTools
  8. Chrome Dev Summit 2018
  9. Optimize your libraries with webpack
  10. Long Tasks API 1
  11. A Netflix Web Performance Case Study
  12. 大公司里怎樣開(kāi)發(fā)和部署前端代碼?
  13. webpack進(jìn)階:前端運(yùn)行時(shí)的模塊化設(shè)計(jì)與實(shí)現(xiàn)
  14. Separating a Manifest
  15. The cost of JavaScript in 2019
  16. [譯] 2019 年的 JavaScript 性能
  17. webpack 4: Code Splitting, chunk graph and the splitChunks optimization
  18. 文本壓縮算法的對(duì)比和選擇
  19. 簡(jiǎn)單聊聊 GZIP 的壓縮原理與日常應(yīng)用
  20. Text Compression
  21. Better tree shaking with deep scope analysis
  22. How we reduced our initial JS/CSS size by 67%
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 慢慢的干貨~ 有點(diǎn)長(zhǎng)~ 關(guān)于 React 應(yīng)用加載的優(yōu)化,其實(shí)網(wǎng)上類(lèi)似的文章已經(jīng)有太多太多了,隨便一搜就是一堆,已...
    BluesCurry閱讀 3,996評(píng)論 1 19
  • 京東PLUS會(huì)員項(xiàng)目是國(guó)內(nèi)第一個(gè)電商付費(fèi)會(huì)員項(xiàng)目,正式開(kāi)通的會(huì)員數(shù)量已破千萬(wàn)。我團(tuán)隊(duì)從2016年接手這個(gè)項(xiàng)目的前端...
    沫之閱讀 1,332評(píng)論 0 6
  • 簡(jiǎn)介 幾個(gè)月前,我的任務(wù)是將我們組的 React 構(gòu)建配置升級(jí)到 Webpack 4。我們的主要目標(biāo)之一是利用 t...
    1024譯站閱讀 1,361評(píng)論 0 2
  • webpack的打包和性能優(yōu)化 tree shaking tree shaking 是一個(gè)術(shù)語(yǔ),通常用于描述移除 ...
    mongofeng閱讀 1,848評(píng)論 0 0
  • ??!我專(zhuān)程來(lái)見(jiàn)你 美麗的蘆葦姑娘 你那柔白嬌軟的手臂 在向我頻頻招喚 我來(lái)了 來(lái)到了你的圣潔之地 請(qǐng)你莫怪罪我 我...
    為愛(ài)而生820閱讀 588評(píng)論 8 11

友情鏈接更多精彩內(nèi)容