Hello ~ 我是小斑,一個富文本編輯器。今天,咱來聊聊體重,對,沒錯!就是那使人頭疼的體重!任何事物都煩這個體重,固然也包括我,當個人創造者阿飛發佈小斑的第一個版本時,小斑足足有 4M
之重!看着控制檯茫茫多的流量消耗,他愣住了:你。。。特麼的這麼怎麼胖,足足 4M
(包括 CSS
)!但小斑我也很無奈呀!代碼是你寫的,怪我咯!javascript
軟件發展總歸避免不了兩個過程:野蠻生長 & 精心優化,小斑長算是長出來了,但這一身肥膘得好好減減!css
噥!上圖,就是我剛誕生時,所包含的 JS
模塊,足足 3M
之多!但小斑的核心代碼僅僅存在於右下角藍色的方塊內,其餘都屬於加強體驗的代碼,包括 UI
框架,代碼高亮,表情的便捷輸入等等。html
接下來,小斑瘦身記,正式開始。java
提到數據壓縮,就不得不提一個鼎鼎大名的數據壓縮算法:Gzip
。react
Gzip
全名: GNU zip
開源的數據壓縮算法,普遍應用於網絡傳輸。你們可能會有疑惑,數據壓縮算法這麼多,憑啥 Gzip
如此出名?由於壓縮比高?webpack
no!no!no!僅僅由於它是 GNU
開頭,互聯網誕生於一個專利橫飛的年代,各類好用的壓縮算法受限於專利,並不能使用在開放的互聯網上,所以你們須要一個高效且開源的壓縮算法,Gzip
所以誕生!git
使用 Gzip
很簡單,過程大概也就 3 步:github
Gzip
將須要發送的數據進行壓縮;Gzip
解壓;所以,Gzip
是服務端對輸出內容的優化,且須要客戶端支持(客戶端須要有解壓的能力)。web
等等!須要客戶端支持?會不會很麻煩?算法
固然不!別忘了 Gzip
的全名是 GNU zip
,它但是開源的!只要客戶端內置 Gzip
模塊,就能夠徹底使用 Gzip
壓縮後的數據!所以能夠簡單的認爲:只要服務端進行了 Gzip
壓縮,文件的體積能瞬間減小一半!
至於如何開啓 Gzip
,各個服務平臺都有與之相對應的方法。小斑由 Nginx
提供服務,開啓 Gzip
模塊後,小斑體積驟減至 1.5M
,看起來像個瘦子了!但也僅僅是看起來!我依然是個胖子,只不過神奇的壓縮算法把我給變瘦了,濾鏡下的胖子,再怎麼好看依舊是個胖子!
ps:並非全部的瀏覽器都內置了 Gzip
模塊,IE
系列就沒有(你們趕忙拋棄 IE
吧),所以真實環境下客戶端須要一些特殊的請求頭,來控制服務器返回的具體內容。
冗餘代碼:就是那些打包到項目中,卻沒被使用的代碼。看到這個標題,有些小夥伴可能會心生不屑,哼!這人連 Webpack
支持 TreeShaking
了都不知道,也不知道是用那個版本的 Webpack
,還在說這些老掉牙的問題,真沒意思!
稍等!小斑誕生環境中的 Webpack
早已 4.42.0
,徹底支持 TreeShaking
,在這裏,小斑只想問一個簡單的問題:TreeShaking
能解決樣式冗餘嗎?
不能,直到如今爲止,依然沒有一種較好的辦法,在代碼打包時,解決樣式冗餘的問題。但在代碼打包前,也就是編寫代碼的時候,卻能夠!
相信使用過 Antd
組件庫的小夥伴,使用 Antd
時第一行代碼都長這樣:
import "antd/dist/antd.css";
但我悄悄告訴你,這個文件,整整 700K
!請問:有何感想?
小斑爲此深刻查看了 Antd
代碼,瞭解到其實每一個組件下都有單獨的樣式文件,單獨引入樣式,就能能夠移除多餘 CSS
代碼。示例以下:
// antd 的公共樣式,必須引入 import "antd/lib/style/core/index.less"; // 組件樣式,單獨引入便可 import "antd/lib/button/style/index.less"; import "antd/lib/xxx/style/index.less";
考慮到,編輯器內,用到了一些高級特性,須要較高版本瀏覽器的支持,故只考慮了最近幾個版本的 Chrome(包括使用 Chromium 核心的其餘瀏覽器)
、FireFox
、Safari
,一些已經成爲標準的 CSS3
樣式前綴就不必兼容了,所以小斑的兼容性以下:
"browserslist": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ]
不要問小斑爲何不兼容 IE
,連它親爸都不要它了,兼容它幹嗎!
一番下來,小斑的樣式獲得了最大程度的精簡(由於 Antd
組件內的樣式包含了組件的全部樣式,會有部分冗餘),樣式體積驟減,由本來 900K
減至不到 200K
!通過 Gzip
發生到客戶端的體積,不到 100K
!
在小斑誕生之初,便已肯定,編輯器必須支持代碼高亮,那天然就須要引入 highlight.js
,但這句代碼比 Antd
的樣式代碼更加誇張,好嘛!整整 1M
的 JS
!看到上圖中那個大大的 highlight.js
代碼塊了嗎?臃腫不堪,經仔細研究後發現,核心的高亮代碼其實不多,絕大部分被 languages
佔用!
怎麼辦?去官網瞧瞧唄,得嘞,不看不知道,一看嚇一跳,經過如下形式引入的 highlight.js
包含了整整 189
種的語言解析器!
import hljs from 'highlight.js';
什麼?爲啥 TreeShaking
沒起做用?這和 TreeShaking
的關係可真心不大,高亮是在打包後纔用到的模塊!那該如何精簡?其實官方已經給了答案:按需加載,僅加載須要引入的語言解析器,甚至官方已經爲咱們推薦好了經常使用的 38
種語言,一一注入便可!
import hljs from 'highlight.js/lib/core'; import javascript from 'highlight.js/lib/languages/javascript'; hljs.registerLanguage('javascript', javascript);
雖然總體的代碼量因爲須要註冊 38
種語言增長了一點點,但換來的倒是 1M
到 300K
打包體積的變化,值嗎?很是的值!
Δ 移除很是用語言後的代碼高亮後的總體打包體積。highlight.js
僅佔了其中的一小塊。
有用過小斑編輯器應該知道,小斑支持選擇代碼的高亮風格以及文章的主題。一開始,小斑包含這部份內容,但後來發現,每次發版,這部份內容永遠不變!高亮風格和文章主題不過是一段樣式信息而已,爲何不靜態化?把這部份內容獨立出去!這樣不只小斑可使用,你們也均可以使用了!
最後,阿飛又折騰出一個新項目:zebra-editor-theme
專一於文章樣式的管理,但願你們能多多關注!
固然,開源意味着共享,站點內全部的代碼高亮風格,以及文章主題,你們均可以獲取,使用,一下是文章主題以及代碼風格的樣式文件獲取方式,固然來目前的文章主題還不多,歡迎你們多多 PR
~
冗餘,靜態代碼已優化完畢,看着這依然有 2.5M
的小斑,阿飛嘆了口氣:哎,你個胖子啊,看來不能一口氣把全部的代碼都給你。異步加載模塊得用上了!
異步加載是什麼?
一種延遲加載的技術,可讓資源在須要加載的時候,才進行加載。
如何實現?
很簡單!一句話的事:
const loadMdAst = () => import( /* webpackChunkName: "mdast", webpackPrefetch: true */ "@textlint/markdown-to-ast" );
如上,就動態的引入了 Markdown
語法解析器。
如何使用?
import
函數返回 Promise
,該 Promise
會在資源加載結束後進入 resolve
狀態,就能得到模塊的默認導出了!
const mdAstParse = await loadMdAst();
如上就能在 async
函數中獲取 Markdwon
的解析器。但該種方式有個弊端,因爲異步函數是有傳染性的!會致使一層層的函數都變成異步函數。
import {lazy, Suspense} from "react"; const Panel = lazy(() => import(/* webpackChunkName: "color", webpackPrefetch: true */ "./color-panel"), ); const ColorBtn = () => { return ( <Suspense fallback={<Loading size={80} />}> <Panel /> </Suspense> ) }
一個簡單例子:顏色選擇框經過異步組件的形式,加載到項目中,相關內容可翻閱文檔:React-Suspense
。
異步的模塊或是組件能夠延緩加載時間,但卻會致使兩個問題:
對於第一點,若是說在 async
函數出來以前,大部分小夥伴心裏估計是抗拒的,本來流程明確的代碼,被套用在一個 Promise
鏈裏,想一想都不舒服,不過如今 async/await
函數規範以及制定,異步的獲取結果也就一個 await
的事,一點也不麻煩!因此目前,第一點能夠忽略不計!
關於第二點,這麼說,若是項目中的模塊首屏就會被用到,雖然使用了異步的形式,但卻依然要等到模塊加載完畢才能展現首屏,這類異步的模塊其實沒有必要。
但若是說項目中有個功能,使用者始終不會用到,不加載應該是最好的選擇。還有,若是該功能,不會出如今首屏,但卻須要長達 1s
的加載時間,那麼它以一種後臺加載的形式,默默的在用戶使用的時候加載好,對用戶來講,也是很是棒的體驗了!
所以,對於異步模塊,只要知足這兩點中的一點,就可使用了:
異步模塊,雖然不能減小小斑的代碼總量,但卻能讓小斑早一點與大家相見。你說值嗎?固然很是的值呀!
熟悉 Webpack
的小夥伴都應該知道,Webpack
擁有代碼分類打包的能力,具體到 Webpack4
其實就是 optimization.splitChunks
這一塊你們討論的不少了,複製粘貼官網的文檔也沒有意義,就很少說了。在這小斑推薦你們結合官網和網上總結一塊兒看,由於網絡上文章的並不必定是最新的,記錄下爲何要代碼分塊(知其因此然比知其然更加劇要!):
UI
庫)實際上是能夠緩存的;bundle
不添加版本號,就能被瀏覽器緩存了!分包,說到底,是讓瀏覽器緩存生效的一種策略,若是說全部的代碼都在一個 bundle
中,爲了不緩存致使的影響,不得已須要添加版本號,但分包以後就不一樣了,即便版本須要更新,一些包的內容其實沒有發生變化,打包出來的文件名也就不會變化,這極大的利用了緩存!
Δ 經異步加載,內容分包後的最終成果
首屏須要加載的內容塊以下:
willChange
: 一些可能會發生變化的項目依賴,如 antd
,由於可能會使用新的組件;library
: 固定不會的項目依賴,如 react
;main
: 小斑的核心代碼;未經 Gzip
前,一共:554K + 344K + 338K ≈ 1M
,Gzip
後僅僅 340K
小斑瘦身大獲成功!可是,一切並無結束!
Service Work
能夠簡單理解爲一個介於客戶端和服務器之間的一個代理,客戶端的請求會經過 Service Work
對外發送,並獲取內容,所以 Service Work
具備了控制返回內容的權利,它能夠緩存返回的內容,並在第二次請求的時候直接返回數據,這點和緩存很像,但區別就在於,它是可控的,甚至能夠提早獲取資源,並緩存!
想像一個具體場景,你的網頁發版了,用戶焦急的等待着瀏覽器下載新版網頁的資源,但一旁使用 Service Work
的網站依舊瞬間打開了老版頁面,開始瀏覽了起來,看起來好像你隔壁的網頁沒更新。但次日,你卻發現隔壁的網頁,不只更新到了最新版,並且依舊瞬間打開!做爲一個積極向上的開發者,我想,你應該理解到了這在用戶體驗上的差距!
Service Work
能夠檢查更新,輸出原先內容的同時,下載最新內容,並在合適的時候更新緩存內容,對於用戶來講,除了第一次訪問,以後的每一次都是瞬間打開,徹底不須要下載新內容!
這時候有些小夥伴就會問了:這麼牛逼的技術,必定很難咯!不!它不只不難,還很簡單,簡單到若是你使用腳手架,都不須要考慮這個問題!
因此,你要作的僅僅是打開腳手架裏的 Service Work
選項 ~
囉囉嗦嗦寫了將近 6000
字,固然,還有一些好比 JS
的代碼壓縮插件 TerserPlugin
,CSS
的壓縮插件 MiniCssExtractPlugin
之類的不提也罷,都是形式化的東西,這些腳手架已經幫開發者弄好了,不須要考慮,固然瞭解仍是須要了解的,那就先把 Webpack
文檔看一遍吧 ~
雖然文章僅用了幾天寫成,但具體到小斑的優化過程倒是是一個較長的過程,總結不易,給個 Star
唄。