在 Web 應用開發過程當中,咱們常常談及到的就是優化,而優化每每又是既簡單而又複雜的過程,優化這個命題很廣,最終體現出來的都是用戶體驗問題,咱們一切優化都是爲了用戶體驗。javascript
爲何說簡單?在現代 Web 開發生態中,有很是優秀的工具鏈幫助咱們作一些很實際的優化工做,例如 webpack
。這些工具能夠很好的幫助咱們解決包之間的依賴、減少包大小、提取公共模塊等等問題。css
爲何說複雜?優化這個話題咱們談了不少年,只要有用戶羣,優化問題就會一直存在。而優化工做涉及的領域特別廣,包含的因素又特別多,有時候須要針對特定的場景作特殊的優化工做,因此說又很複雜。html
不論是簡單仍是複雜,做爲程序員,咱們應當作一些咱們力所能及的優化工做,本文屬於探討性話題,但願廣大網友可以在留言區留下您的一些思考。前端
這裏不探討如何書寫高性能的代碼,而是探討下咱們書寫的代碼該如何被構建。這裏以 webpack 爲構建工具(版本爲4.19),來闡述下在 webpack 咱們該作的優化工做。java
webpack 從 v4
版本開始,作了不少的優化工做,詳情請看這裏 。咱們就拿 Code Splitting
提及,Code Splitting
是 webpack 一項重要的編譯特性,可以幫助咱們將代碼進行拆包,抽取出公共代碼。利用這項特性咱們能夠作更多的優化工做,減小加載時間,例如能夠作按需加載。而在 webpack 中開啓 Code Splitting
也很簡單,它是一項開箱即用的插件,示例代碼以下:react
module.export = {
// ...
optimization: {
splitChunks: {
chunks: 'all'
}
}
}
複製代碼
上面的 chunks
配置建議你們配置爲 all
,詳細配置請參考:splitChunks.chunkswebpack
這裏給出個參考結果:分別爲配置前和配置後git
這裏明顯多出了幾個包含 vendors~
字樣的文件,並且你會發現 app
這個文件被提取出了 vendors~app~react_vendor
和 vendors_app_redux_vendor
這兩個文件。至於最大的文件爲何還有1.02M,咱們可使用 analyze
來分析下包的結構,這裏是因爲包含了 antd
的文件。程序員
在實際開發過程當中,路由也是須要進行 Code Splitting
,在過去咱們常用 bundle-loader ,來幫助咱們進行代碼分割,它是基於 require.ensure 接口進行實現。既然咱們能夠對路由進行代碼分割,那麼路由頁面中的組件咱們是否能夠按需加載,實現代碼分割呢?答案是顯然的。github
這種業務場景也是很是的多,這裏我舉一個例子,就是一個登陸頁面,登陸有多種方式,其中最多見的就是帳號登陸和掃碼登陸,默認爲掃碼登陸。當用戶沒有選擇帳號登陸,那麼按道理這部分代碼咱們能夠不進行加載,從而減小加載時間,優化用戶體驗。咱們建議能進行組件級分割就分割,最大化減少頁面大小。
在 React
中雖然也可使用 bundle-loader 來實現組件級代碼分割,可是也會有一些問題。在後來,React Router
官方也推薦使用 react-loadable 來進行代碼分割。強烈建議 React
使用者使用此庫,該庫的功能很強大,是基於 import() 實現。它能夠實現預加載、從新加載等等強大功能。
若是你對本身編寫的代碼很瞭解,你能夠經過在 package.json
中添加 sideEffects
來啓用 Tree Shaking
,即搖樹優化,幫助咱們刪掉一些不用的代碼。這裏再也不贅述,詳情能夠點擊Tree Shaking。
在談到 Code Spliting
時,咱們不得不想到 dynamic import
,在以前版本的 webpack 中,咱們想實現動態加載使用的是 require.ensure ,而在新版本中,取而代之的 import() ,這是TC39關於使用 import()的提案,而目前 import()兼容性以下:
import() 返回一個 Promise
,若是你想使用它請確保支持 Promise
或者使用 Polyfill
,在想使用 import() 前,咱們還得使用預處理器,咱們可使用 @babel/plugin-syntax-dynamic-import 插件來幫助webpack解析。webpack 官方給了咱們一個 dynamic import
的示例 ,這裏我就不作舉例。使用 import() 咱們能夠很方便的實現 preload
預加載、懶加載以及上面談到的 Code Splitting
。
Polyfill
如今對於你們來講應該並不陌生,他能夠幫助咱們使用一些瀏覽器目前並不支持的特性,例如 Promise
。在Babel中,官方建議使用 babel-preset-env 配合 .browserslistrc ,開發人員能夠無需關心目標環境,提高開發體驗。尤爲在 Polyfill
方面,只要咱們配置好 .browserslistrc ,Babel 就能夠智能的根據咱們配置的瀏覽器列表來幫助咱們自注入 Polyfill
,好比:
.babelrc
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "entry"
}
]
]
}
複製代碼
useBuiltIns 告訴 babel-preset-env 如何配置 Polyfill
,這裏我配置爲:entry
,而後在 webpack 入口文件中引入 import '@babel/polyfill'
便可,這裏注意不能屢次引入 import '@babel/polyfill'
,不然會報錯。
.browserslistrc
> 1%
Last 2 versions
複製代碼
這樣就完成了自動根據 .browserslistrc注入 Polyfill
,可是這樣有一個問題,就是全部的瀏覽器都會有 Polyfill
的並集。每一個瀏覽器之間的特性具備很大的差別,爲了儘量的減少包的大小,咱們能夠爲每一個主流瀏覽器單獨生成 Polyfill
,不一樣的瀏覽器加載不一樣的 Polyfill
。
SPA 程序打包出來的html文件通常都是很小的,也就2kb左右,彷佛咱們還能夠利用下這個大小作個優化,有了解初始擁塞窗口 的同窗應該知道,一般是14.6KB,也就意味着這咱們還能利用剩下的12KB左右的大小去幹點什麼,這了我建議內聯一些首屏關鍵的css文件(可使用 criticalCSS ),或者將css初始化文件內聯進去,固然你也能夠放其餘東西,這裏只是充分利用下初始擁塞窗口 特性。
這裏順便講下css初始化,css初始化有不少種選擇,其中有三種比較出名的,分別是:normalize.css 、sanitize.css 和 reset.css 。關於這三種的區別我就直接引用了。
normalize.css and sanitize.css correct browser bugs while carefully testing and documenting changes. normalize.css styles adhere to css specifications. sanitize.css styles adhere to common developer expectations and preferences. reset.css unstyles all elements. Both sanitize.css and normalize.css are maintained in sync.
在利用 webpack 打包完以後,咱們有些文件幾乎不會變動,好比我這裏列舉的react
、redux
、polyfill
相關的文件。
entry: {
react_vendor: ['react', 'react-dom', 'react-router-dom'],
redux_vendor: ['react-redux','redux', 'redux-immutable','redux-saga', 'immutable'],
polyfill: '@babel/polyfill',
app: path.join(process.cwd(),'app/app.js')
}
複製代碼
這些不變的文件咱們就能夠好好的利用下,常見(http 1.1)的就是設置 Etag
,Last-Modified
和 Cache-Control
。前面兩種屬於對比緩存,仍是須要和服務器通訊一次,只有當服務器返回 304
,瀏覽器纔會去讀取緩存文件。而 Cache-Control
屬於強制緩存,服務器設定 max-age
當過了設定的時間後纔會向服務器發起請求。這裏打包再配上 chunk-hash
幾乎能夠完美的配置緩存。
固然還能夠利用 localStorage 來作緩存,這裏提出一種思路,是我之前在效仿百度首頁緩存機制想的。咱們能夠在把js文件版本號弄成一個配置,同時存儲在服務端和客戶端,好比:
{
"react_version": 16.4,
"redux_version": 5.0.6,
"web_version": 1.0
}
複製代碼
客戶端將該版本號存儲在 cookie
或其餘存儲引擎中,這裏推薦 localForage 來作存儲。服務端將最新版本號渲染到html文件中,而後經過js腳本對比版本號,如若版本號不一樣,則進行加載對應的js文件,加載成功後再存儲到本地存儲中。若是相同,則直接取本地存儲文件。
還有一種緩存的場景,就是有一些api服務端更新的進度很慢,好比一天以內訪問的數據都是同樣的,這樣就能夠對客戶端進行請求緩存並攔截請求,從而優化速度,減少服務器壓力。
還有其餘不少能夠優化的地方,好比減小http請求、圖片懶加載等等,就不一一列舉了,你們能夠看雅虎34條軍規:
儘可能減小 HTTP 請求個數——須權衡
使用 CDN(內容分發網絡)
爲文件頭指定 Expires 或 Cache-Control ,使內容具備緩存性。
避免空的 src 和 href
使用 gzip 壓縮內容
把 CSS 放到頂部
把 JS 放到底部
避免使用 CSS 表達式
將 CSS 和 JS 放到外部文件中
減小 DNS 查找次數
精簡 CSS 和 JS
避免跳轉
剔除重複的 JS 和 CSS
配置 ETags
使 AJAX 可緩存
儘早刷新輸出緩衝
使用 GET 來完成 AJAX 請求
延遲加載
預加載
減小 DOM 元素個數
根據域名劃分頁面內容
儘可能減小 iframe 的個數
避免 404
減小 Cookie 的大小
使用無 cookie 的域
減小 DOM 訪問
開發智能事件處理程序
用 代替 @import
避免使用濾鏡
優化圖像
優化 CSS Spirite
不要在 HTML 中縮放圖像——須權衡
favicon.ico要小並且可緩存
保持單個內容小於25K
打包組件成複合文本
關於優化的文章網上太多太多,這篇文章並非告訴你們如何優化,而是在平時寫代碼時可以培養一種習慣、一種意識,就是作咱們力所能及的優化以及要知其因此然。
文 / GoDotDotDot
LESS is MORE
編 / 熒聲
本文已由做者受權發佈,版權屬於創宇前端。歡迎註明出處轉載本文。本文連接:knownsec-fed.com/2018-09-25-…
想要訂閱更多來自知道創宇開發一線的分享,請搜索關注咱們的微信公衆號:創宇前端(KnownsecFED)。歡迎留言討論,咱們會盡量回復。
感謝您的閱讀。