不知不覺,已經來到了最後的下篇 其實我寫的東西你若是認真去看,跟着去寫,應該能有很多的收穫。
最近一些跨平臺技術,React-native
和flutter
之類的,比較火。可是,我仍是不許備把它們放進來,由於那是爲作App
而生,我想把Electron
這個桌面端跨平臺的技術放進來。理由是什麼,後面說html
這是上篇和中篇,若是你是第一次看這個系列文章,歡迎去從頭開始學習:前端
前端20個靈魂拷問 完全搞明白你就是中級前端工程師 【上篇】
前端20個靈魂拷問 完全搞明白你就是中級前端工程師 【中篇】vue
以及一些比較不錯的文章:node
從零編寫一個React框架react
咱們爲何要熟悉這些通訊協議nginx
單頁面應用SPA原理git
9102年:手寫一個React腳手架 【優化極致版】github
性能優化不徹底手冊web
上面的文章,
gitHub
上,都有對應的源碼。
每一個人評判的標準不同,咱們惟有拿出碾壓這個層級的能力的時候,才能堵住質疑者的嘴。固然,咱們不作技術槓精,技術自己沒有好壞。不喜歡就不理會
預解析地址 首次請求解析地址若是沒有緩存 那麼可能消耗60-120ms
性能優化不徹底手冊這裏面有介紹
preload
預請求必要內容,prefetch
預請求可能須要內容
這種請求方式不會阻塞瀏覽器的解析,並且能將預請求的資源緩存起來,並且能夠設置crossorgin進行跨域資源的緩存,不會推遲首屏的渲染時間,還會加快後面的加載時間,由於後面的自己須要的資源會直接從緩存中讀取,而不會走網絡請求。
使用 preload
前,在遇到資源依賴時進行加載:
使用 preload
後,無論資源是否使用都將提早加載:
能夠看到,preload 的資源加載順序將被提早:
使用 preload 後,Chrome 會有一個警告:
preload 和 prefetch 混用的話,並不會複用資源,而是會重複加載。
若不肯定資源是一定會加載的,則不要錯誤使用 preload,以避免本末倒置,給頁面帶來更沉重的負擔。
preload
加載頁面必需的資源如 CDN
上的字體文件,與 prefetch
預測加載下一屏數據,興許是個不錯的組合。
preload和prefetch詳解 這篇文章寫得很棒 感謝做者
部分圖片base64
處理,而後使用雪碧圖。多張圖拼成一張傳輸
固然base64
這個東西慎用,實際開發中它表現並那麼好
例如後端返回數據:「該用戶沒有擁有權限」
能夠改爲:0
約定優於配置的思想必定要有
probbuffer
協議ProtoBuffer
是由谷歌研發的對象序列化和反序列化的開源工具
它的本質就是將一段數據序列化,轉變成二進制形式傳輸
而後另外的服務器端或者客戶端接受到以後 反序列化,轉換成對應的數據格式(json
)
好像還有人沒有據說這個傳輸協議 其實它傳輸過程就是2進制
流的形式
用得最多的是和GRPC
配合Go
語言或者服務器之間傳輸數據
例如IM
應用,每一個IM
應用都是一個服務端 也是一個客戶端
那麼對於這種頻繁傳輸數據的時候,可使用protobuffer
傳輸協議
protobuffer
有幾個優勢:
1.平臺無關,語言無關,可擴展;
2.提供了友好的動態庫,使用簡單;
3.解析速度快,比對應的XML快約20-100倍;
4.序列化數據很是簡潔、緊湊,與XML相比,其序列化以後的數據量約爲1/3到1/10。
protobuffer.js - 咱們可使用這個庫來解析
protobuf.js
提供了幾種方式來處理proto
。
直接解析,如protobuf.load("awesome.proto", function(err, root) {...})
轉化爲JSON
或js
後使用,如protobuf.load("awesome.json", function(err, root) {...})
固然咱們通常轉換成.js
後使用
vue使用protobuffer 我這裏不作大篇介紹,由於有人徹底用不到
能夠用對象進行大數據封裝,儘可能用對象key-value
形式封裝
若是須要對象遍歷 其實也有不少種方法能夠作到
用對象有個好處 就是數據量大起來可是須要查找的時候會很是快
無論前端怎麼發展,js
主線程是單線程,而且與GUI
渲染線程互斥仍是沒有變
爲何?
由於js
能夠進行dom
操做 爲了防止在渲染過程出現dom
操做而形成不可預見後果
現代框架的底層其實仍是dom
操做 而且直接的dom
操做比數據驅動要快多!
例如:
for(let i=0; i< 100000; i++){ console.log(i) } console.log(1)
for
循環其實很快,可是走完這100000
次循環的耗時,到打印出1
,有可能超過100ms
那麼若是這個打印輸出1
是一個用戶交互操做 就會讓用戶有了延遲卡頓的現象
所謂的卡,並非電腦或者手機帶不動咱們的代碼,而是js
線程和GUI
渲染互斥形成的假象。(大部分是這狀況,也有配置特別低的)
若是非要同步代碼的場景?
那麼我建議ES6
的異步方案,或者改變實現方案,由於大部分性能方案優化是卡在這個點。
淘寶等task-slice
方案
先不說這篇文章實現最終效果怎樣,可是這種思想在前端裏是能夠大量使用的,Go
語言裏就有切片
渲染任務分割後打開性能調試面板
能夠看到是一點點渲染出來的 也算是加快了首屏渲染吧!
切片隊列的核心代碼:
function* sliceQueue({ sliceList, callback }) { let listOrNum = (isNum(sliceList) && sliceList) || (isArray(sliceList) && sliceList.length); for (let i = 0; i < listOrNum; ++i) { const start = performance.now(); callback(i); while (performance.now() - start < 16.7) { yield; } } }
跟個人React框架編寫
的每幀清空渲染隊列有點相似:
/** * 隊列 先進先出 後進後出 ~ * @param {Array:Object} setStateQueue 抽象隊列 每一個元素都是一個key-value對象 key:對應的stateChange value:對應的組件 * @param {Array:Component} renderQueue 抽象須要更新的組件隊列 每一個元素都是Component */ const setStateQueue = []; const renderQueue = []; function defer(fn) { //requestIdleCallback的兼容性很差,對於用戶交互頻繁屢次合併更新來講,requestAnimation更有及時性高優先級,requestIdleCallback則適合處理能夠延遲渲染的任務~ // if (window.requestIdleCallback) { // console.log('requestIdleCallback'); // return requestIdleCallback(fn); // } //高優先級任務 return requestAnimationFrame(fn); } export function enqueueSetState(stateChange, component) { if (setStateQueue.length === 0) { //清空隊列的辦法是異步執行,下面都是同步執行的一些計算 defer(flush); } ...//dosomething }
數據持久化存儲
PWA
,漸進式web
應用
將數據資源儲存在緩存中,每次請求前判斷是否在Service Worker
中,若是沒有再請求網絡資源
PWA 的主要特色包括下面三點:
可靠 - 即便在不穩定的網絡環境下,也能瞬間加載並展示
體驗 - 快速響應,而且有平滑的動畫響應用戶的操做
粘性 - 像設備上的原生應用,具備沉浸式的用戶體驗,用戶能夠添加到桌面
當用戶打開咱們站點時(從桌面 icon 或者從瀏覽器),經過 Service Worker 可以讓用戶在網絡條件不好的狀況下也能瞬間加載而且展示。
Service Worker
是用 JavaScript
編寫的 JS
文件,可以代理請求,而且可以操做瀏覽器緩存,經過將緩存的內容直接返回,讓請求可以瞬間完成。開發者能夠預存儲關鍵文件,能夠淘汰過時的文件等等,給用戶提供可靠的體驗。
若是站點加載時間超過 3s,53% 的用戶會放棄等待。頁面展示以後,用戶指望有平滑的體驗,過渡動畫和快速響應。
爲了保證首屏的加載,咱們須要從設計上考慮,在內容請求完成以前,能夠優先保證 App Shell 的渲染,作到和 Native App 同樣的體驗,App Shell 是 PWA 界面展示所需的最小資源。
文檔寫得最好的,仍是百度的lavas
Service Worker
生命週期分爲這麼幾個狀態 安裝中, 安裝後, 激活中, 激活後, 廢棄
安裝( installing )
:這個狀態發生在 Service Worker 註冊以後,表示開始安裝,觸發 install 事件回調指定一些靜態資源進行離線緩存。
安裝後( installed )
:Service Worker 已經完成了安裝,而且等待其餘的 Service Worker 線程被關閉。
激活( activating )
:在這個狀態下沒有被其餘的 Service Worker
控制的客戶端,容許當前的 worker 完成安裝,而且清除了其餘的 worker 以及關聯緩存的舊緩存資源,等待新的 Service Worker 線程被激活。
激活後( activated )
:在這個狀態會處理 activate 事件回調 (提供了更新緩存策略的機會)。並能夠處理功能性的事件 fetch (請求)、sync (後臺同步)、push (推送)。
廢棄狀態 ( redundant )
:這個狀態表示一個 Service Worker 的生命週期結束。
Service Worker
本質,能夠當作另一個線程啓動,作爲一箇中間件在發揮做用。
緩存的資源都是能夠在這裏看到
Service Worker
只能在localhost
調試中或者https
中使用,由於它的權限過於強大,能夠攔截請求等。因此要確保安全,目前PWA
並不成熟,瀏覽器兼容性仍是不那麼好,可是它用起來是真的很舒服
另一種持久化存儲方案:
localStorage
瀏覽器API
有localStorage.getItem
等...
有相似將js
文件緩存寫入localStorage
而後經過與服務端對比版本號再決定是否更新js
文件
還有在進入首頁時,將詳情頁的模版先存入localStorage
當進入詳情頁時候直接取出,而後發請求,把請求回來的一小部份內容(好比圖片,渲染上去)
固然,還有種種的應用,騷操做。
最後是框架,如今的單頁面框架,其實很簡單。 每次更新頁面,diff
對比差別後,更新差別部分。
精細化拆分組件 , 常常變和不常常變的分拆
精細化定製數據來源,最好作到單向數據流,只有一個數據改變能夠影響從新渲染
並非全部的都須要在shouldComponentUpdate
中對比而後決定是否要更新
實踐證實 複用1000個組件渲染在頁面中
用immutable
去生成不可變數據對比
跟用PureComponent
淺比較 後者會快不少不少
永遠別忘了js
主線程和GUI
渲染線程互斥。
合理手段減小重複渲染次數
如何優化你的超大型React應用
前端性能優化不徹底手冊 - 很早前寫的文章
發現性能優化其實要寫的太多太多,可是,核心點在上面和文章裏了,特別是個人那個清空渲染隊列的代碼,我決定能解決很大部分的性能瓶頸。
Nginx
和pm2
配置在理解Nginx的用途以前先了解正向代理、反向代理的概念:
正向代理:是一個位於客戶端和原始服務器(origin server)之間的服務器,爲了從原始服務器取得內容,客戶端向代理髮送一個請求並指定目標(原始服務器),而後代理向原始服務器轉交請求並將得到的內容返回給客戶端。
反向代理:在計算機網絡中,反向代理是代理服務器的一種。它根據客戶端的請求,從後端的服務器上獲取資源,而後再將這些資源返回給客戶端。與正向代理不一樣,正向代理做爲一個媒介將互聯網上獲取的資源返回給相關聯的客戶端,而反向代理是在服務器端做爲代理使用,而不是客戶端。
PM2是一款很是好用的Node.js服務啓動容器。它可讓你保持應用程序永遠運行,要從新加載它們無需停機(我是這麼理解的:PM2是一個監控工具)。
nginx是一款輕量化的web服務器。相較於Apache具備佔有內存少,併發高等優點。使用epoll模型,nginx的效率很高。而且能夠熱升級。
Nginx與PM2的區別:
pm2
是在應用層面單機的負載,nginx
是多用於多機集羣的負載PM2 Cluster
是對單臺服務器而言的,而 nginx
是對多臺服務器而言的,它們能夠很好的結合在一塊兒。全篇看下來會發現,其實Nginx與PM2
徹底是不同的,二者之間沒有很大的相同點讓人混淆。換一種更容易理解的說法是:nginx配置多站點(域名),pm2管理nodejs後臺進程
使用PM2永動機啓動Node.js項目,再使用nginx作反向代理,簡直完美。
由於node.js程序監聽的是服務器端口,使用nginx作反向代理,就能夠任意配置你的二級域名來訪問你的程序
這裏咱們主要介紹nginx
的負載模塊
HTTP負載均衡模塊(HTTP Upstream)
這個模塊爲後端的服務器提供簡單的負載均衡(輪詢(round-robin
)和鏈接IP(client IP))
以下例:
upstream backend { server backend1.example.com weight=5; server backend2.example.com:8080; server unix:/tmp/backend3; } server { location / { proxy_pass http://backend; } }
Nginx
的負載均衡算法:
1.round robin
(默認)
輪詢方式,依次將請求分配到各個後臺服務器中,默認的負載均衡方式。
適用於後臺機器性能一致的狀況。
掛掉的機器能夠自動從服務列表中剔除。
2.weight
根據權重來分發請求到不一樣的機器中,指定輪詢概率,weight和訪問比率成正比,用於後端服務器性能不均的狀況。
例如:
upstream bakend { server 192.168.0.14 weight=10; server 192.168.0.15 weight=10; }
3.IP_hash
根據請求者ip的hash值將請求發送到後臺服務器中,能夠保證來自同一ip的請求被打到固定的機器上,能夠解決session問題。
例如:
upstream bakend { ip_hash; server 192.168.0.14:88; server 192.168.0.15:80; }
4.fair
根據後臺響應時間來分發請求,響應時間短的分發的請求多。
例如:
upstream backend {
server server1;
server server2;
fair;
}
5.url_hash
根據請求的url
的hash
值將請求分到不一樣的機器中,當後臺服務器爲緩存的時候效率高。
例如:
在upstream中加入hash語句,server語句中不能寫入weight等其餘的參數,hash_method是使用的hash算法
upstream backend { server squid1:3128; server squid2:3128; hash $request_uri; hash_method crc32; }
常見的負載均衡算法
使用pm2
:
npm install pm2 -g pm2 start app.js
PM2
的主要特性
內建負載均衡(使用 Node cluster 集羣模塊) 後臺運行 0 秒停機重載,我理解大概意思是維護升級的時候不須要停機. 具備 Ubuntu 和 CentOS 的啓動腳本 中止不穩定的進程(避免無限循環) 控制檯檢測 提供 HTTP API 遠程控制和實時的接口 API ( Nodejs 模塊,容許和 PM2 進程管理器交互 )
pm2
的使用,讓咱們避開了本身配置負載均衡,守護進程等一系列。可是高併發場景,Nginx
和內置的負載均衡,僅僅只講到了皮毛,這裏只是入個門。
還剩下最後三個問題,我想寫得質量高一些,若是感受寫得不錯能夠點個贊,關注下。gitHub
倉庫也歡迎去star
~哦
歡迎加入咱們的segmentFault
前端交流羣,由於羣里人數比較多了,加個人我的微信:CALASFxiaotan
我會拉你進羣