大前端時代,淺談JavaScript開發重型跨平臺應用以及架構

Peter譚金傑發表於2019-09-28

clipboard.png

大前端時代以及即將到來的5G時代,3D視覺化,音視訊直播技術,IM即時通訊場景應用我覺得都是大有可為的。前段時間爆款換臉應用出現,到近段時間頭像加?的火爆,這是好事。

不知不覺,九月就要過去,由於這個月工作上,被C++折磨得很難受,而且其他時間都在學習,所以沒有時間寫文章,好在技術提升很大。今天準備好好談一談重型應用的架構以及技術選型,為接下來我的正式架構設計做個鋪墊。

為什麼寫重型應用的架構和技術選型

  1. 傳統的web前端,只能發個ajax請求,畫畫頁面。了不得寫個webApp
  2. 想讓後端的同學們,瞭解下目前大前端的世界,現在的前端跟以前不一樣了,特別現在市場很缺高階前端,但是術業有專攻,這點我承認
  3. 大前端的定義,太廣泛,在我看來,必須深入前端某個方向,以及能獨立設計不那麼複雜場景下的後端架構。
  4. 在極客時間上提問了winter老師,我自我感覺已經良好,但是迷茫了。他回懟了我:想一想你用你的技術做出了什麼nb的東西把。
  5. 是人都想做出點什麼事情,我想引起大家的共鳴去使用某些技術,或者朝著這個方向去發展,共同提升 社群的技術整體層次

什麼是重型應用

例如微信,QQ,Telegram, 以及一些工具類的應用

說到這些大家肯定覺得,為什麼不說是遊戲? 當然遊戲也算,可是我相信做出1000萬人每天都在用的產品是大家的夢想,起碼能吹一輩子吧

工具類的東西其實是最難做的,比如vsCodeExcelPhotoShop這些。這也是為什麼這麼多年出現成功的工具類產品這麼少。這裡不得不提到vsCode,它其實就是用ELectron開發,基於TypeScript。當然肯定使用了不少C++外掛,說到這裡,留下傷心的眼淚,最近也是被折磨得很難受

成功開發一個重型應用的好處

  1. 出去面試基本上很容易成功,特別是專業性強的崗位,例如你在QQ開發了十幾年,你根本不用出去找工作,當然你應該也不會跑
  2. 技術全面,複雜場景你都能hold
  3. 有能吹的地方,可以跟誰說:我開發的東西,多少萬人在用,老了還能吹。 程式設計師嘛,一半時間都在吹水,還有接近一半時間在划水,只有一丁點時間在寫程式碼
  4. 更容易財務自由,生活自由,例如現在很多有過成功的重型應用開發者已經不單純靠程式碼產出維持生活。他們做技術顧問,賣課程,出書,辦培訓都甚至比單純寫業務程式碼賺得多很多

正式開始

  • 目前跨平臺框架,移動端比較成熟的是React-native,但是大家有所瞭解的都應該知道,這個框架雖然生態比較成熟,但是在面對眾多手機的適配難度,以及效能方面存在的缺陷,如果用它製作重型應用我覺得是不合適的,如果要做重型應用,移動端應該使用原生。
  • 庫克說過,中國的移動端開發確實很強,美國人要做一個應用,首先考慮的是PC桌面端,而國內首先考慮的是移動端。
國內移動端開發人員,在我看來已經人目前已經夠多了,如果說你現在不會React-nativeFlutter,我也不建議你去深入學習,特別是Flutter.

為什麼要這麼說?

React-native剛出來的時候,坑多吧。現在Flutter也是,可是當你從RN最初的版本踩坑踩到現在,之前踩的坑大都沒有意義(說這些話想過被噴,但是...此處省略一萬字,建議去了解下原理和基本使用,不要耗費太多時間)

一個技術你去使用,並不是它多流行,只要它足夠流行。 ---來自某位國內大佬

技術的學習,應該多往底層鑽研,如果你走錯了路,鑽錯了方向,浪費了時間得不償失,我之前有說過,前端最核心的幾個基礎知識點,應用層的東西從來不會很難。前提你的基礎足夠紮實

前端20個真正靈魂拷問,吃透這些你就是中級前端工程師 【上篇】

前端20個靈魂拷問 徹底搞明白你就是中級前端工程師 【中篇】

前端20個靈魂拷問 徹底搞明白你就是中級前端工程師 【下篇】

這些文章很多同學應該都看過,爭議也很大,在我現在看也寫得很爛,但是它裡面的知識點是夠的。當然你必須要去結合起來,然後深入學習每個知識點。

既然說了移動端沒有合適的重型跨平臺應用開發框架,那麼只有PC端了。還有多少小夥伴在PC端開發呢?

Electron開發,來了

clipboard.png

我不止一次提到過這個框架,我覺得它真是一個非常棒的框架,為什麼這麼說呢?

  1. 我跟很多朋友說過,如果想開發APP,不會寫原生,那麼你肯定達不到某種境界。因為你始終有很多很多的黑盒過程,可是Electron就會大大降低這個概率。
  2. 基本上沒有適配和差異性,linuxMac以及Windows三者都可以執行,除了Mac上某些特殊場景需要自己設計下選單快捷鍵之類,以及一些檔案IOMAC預設行為
  3. 最新的Node版本、執行的V8環境以及最新的谷歌瀏覽器一起被打包,最新的技術和API都可以用,無需適配擔心相容性,真正放飛自我,可以隨時隨刻用Node.js實現功能,甚至呼叫大量C++外掛,著名的VSCode就是這樣而來

clipboard.png

你甚至可以看成Electronweb網頁套上一層殼,你可以在主程式寫你的Node.js去實現功能,渲染程式你怎麼寫怎麼寫,還可以呼叫封裝好的原生介面。遇到特別複雜的需求,用C++外掛去實現吧

最終打包出來的安裝包跟正常的桌面應用是一樣的,正常安裝解除安裝等,都已經封裝好。

clipboard.png

目前GitHub上已經有77.2Kstar

應用層面的東西,大都不會太難,Electron的文件已經非常全面,基於它出現了很多複雜,而且成功的工具類重型應用。我相信它

clipboard.png

whatsApp也是基於它,國外還有一些很NB的應用也是。這裡不做過多闡述,可以確定它是一個成熟而且成功的框架

可能很多人看到這裡又要說標題黨了,別急,下面來乾貨。

重型應用架構注重的核心問題

  1. 專案本身的最重要功能是什麼
  2. 專案本身出發點是為客戶提供什麼方便
  3. 專案的核心競爭力是什麼
一個好的開發,它一定能懂一些產品,甚至測試,當然他也應該會炒河粉,35歲以後好維持生活

我們今天舉一個例子,IM,即時通訊,Telegram,20萬人超級群端到端加密的核心賣點產品

clipboard.png

電報Telegram

clipboard.png

現在回答上面三個問題:

專案本身的最重要功能是什麼

答案:即時通訊,資訊的收發

專案本身出發點是為客戶提供什麼方便

使用產品進行訊息傳遞

專案的核心競爭力是什麼

20萬人的超級群,端到端加密,隱私足夠安全

核心競爭力,往往代表了這個應用產物的技術最難點,因為誰都能做,那麼就不是核心競爭力了

所以我們其他的都忽略,關注第三點,開始進行技術選型,架構。

單執行緒的Node.jsJavaScript重型應用架構設計

要想寫好這個架構,我覺得你首先在自身的擅長領域不能有太多的黑盒過程。例如框架原始碼,庫原理實現,瀏覽器和Node.js的事件EventLopp以及他們的缺陷,你要熟知在心。因為像這種應用,一個小方向可能就會掏空你的技術棧,耗盡你的精力,例如音視訊、圖片處理等。

單執行緒的Node.js以及js主解析引擎,讓我們又愛又恨。

這款應用的核心競爭力,是20萬人超級群,那麼資料量很大,大批量渲染壓力和頻繁加解密計算耗時、頻繁資料庫寫入壓力都是不可避免的,那麼我們的Node.js擅長非同步非阻塞,以及前端渲染程式的非同步就顯得尤為重要

前端架構整體的核心除了技術選型以及大體框架外,就是任務排程。

這裡的任務排程分兩種:

1.渲染任務排程

2.非渲染任務排程

單個渲染任務排程

1.React框架中,多次傳入物件,setState會自動合併到一次執行,其實就是一種節流思想

2.ReactFiber架構思想,把若干個任務分割成多個小任務執行,中間根據你的任務優先順序安排去選擇時機執行

clipboard.png

3.淘寶的分片渲染方案,跟上面第二條有一些類似
clipboard.png

我之前說過,應用層的東西都不難,只要你基礎足夠紮實,能手寫出簡單的框架,以及庫。你絕對能非常輕鬆應對前端百分80以上的效能問題和需求,技術最終都是相似的

上面只是說了別人的一些比較簡單的優化方案,下面才是開始我們自己的渲染任務排程:

clipboard.png

回到我們的Telegram架構設計方案:

渲染任務架構過程需要著重考慮的幾個問題:

1.渲染資料量特別大

2.更新特別頻繁

3.儘可能手動回收垃圾,避免訊息量過大,v8垃圾回收的時間不確定性導致記憶體被白白佔用,引起卡頓

4.考慮大批量資料到達渲染程式的使用者應用體驗,確定使用者互動屬於高優先順序任務,其他的哪些是低優先順序-但必須執行的任務,哪些是中優先順序任務,這裡說的任務,都是渲染任務。

今天在學習一篇小冊,裡面有一句話引起了我的共鳴,在計算機的世界,如果有解決不了的問題,那就加一箇中間層,如果還不行,那就加兩個。 -後面這句是我加的

這個是我自己編寫的Reactmini-react原始碼地址

PReact原始碼中,是將需要更新的元件放入佇列中,然後一次清空,虛擬碼:

   if (setStateQueue.length === 0) {
    //清空佇列的辦法是非同步執行 
    defer(flush);
  }
     setStateQueue.push({
    stateChange,
    component
  });

    function defer(fn) {
      //requestIdleCallback的相容性不好,對於使用者互動頻繁多次合併更新來說,requestAnimation更有及時性高優先順序,requestIdleCallback則適合處理可以延遲渲染的任務~
      //   if (window.requestIdleCallback) {
      //     console.log('requestIdleCallback');
      //     return requestIdleCallback(fn);
      //   }
      //高優先順序任務
      return requestAnimationFrame(fn);
    }


  while ((component = renderQueue.shift())) {
    renderComponent(component);
  }


上面這段程式碼其實很重要,核心思想就是:

每當進入這個函式,如果發現佇列佇列裡沒有任務就去執行defer函式

defer函式執行是非同步,此時defer下面的setStateQueue已經被push了進去資料,這樣達到一幀完成一次渲染任務排程

當然上面僅僅一個小的任務排程,這個必須要了解,才能往下看

requestAnimationFramerequestIdleCallback使用:

clipboard.png

clipboard.png

你需要深入瞭解React框架的Fiber架構,這塊尤其重要,是效能優化,任務排程的基礎,上面有提到,React在每次diff對比階段,將任務分割成若干個小任務,此時如果有RAFRID的任務,就要考慮去執行了

RAF的任務會每次在下一次小任務前執行

RID的任務只有在下一次小人物前,有空餘時間才會執行,所以它不一定會執行。(特別高頻必須執行的任務)

Fiber架構配合單個任務分割已經介紹完畢,下面出思維導圖出整體的任務排程


整體渲染任務排程

clipboard.png

核心的兩點:

1.釋放主執行緒的佔用,讓使用者的操作最快得到響應

2.合理排程任務,分高、中、低三種優先順序別任務

理清思路:

1.資料通過IPC通訊到達渲染程式

2.全部交給子執行緒去進行計算,組裝資料,通過非同步的postMessage事件通訊,拿到渲染資料

3.排程渲染任務,使用者操作互動

4.釋放主執行緒

這裡特別提示,為什麼我一直強調不要使用定時器,一旦應用變得很複雜,如果任務排程不合理,定時器裡的程式碼是要很久很久才能執行的。當然,只有重型應用會這樣

渲染任務排程這塊,主要是微任務,RAF,RID分片渲染以及同步程式碼,佇列排程等手段。

主程式,接入層任務排程

clipboard.png

核心思想跟渲染程式大概一致:

1.儘量釋放主程式,保持空閒,讓使用者的操作即時得到反饋,因為很多操作會呼叫主程式的介面

2.非同步排程任務,寫入資料庫非同步,解密計算可以使用nextTick等方式去排程新增佇列

這裡提到,任務排程的核心一點就是,頻繁觸發的任務必須加入佇列,非同步清空,否則像解密這種同步計算耗時,一旦被頻繁觸發就會引起阻塞。即使交給了其他程式,也要做佇列

整體架構以及技術選型注意點

clipboard.png

1.技術選型時,儘量選擇自己熟悉它原理的庫,以及能用Demo測試模擬場景的技術,測試通過再選用,不同技術之間解決問題出發點不一樣,可能會有衝突

2.佇列和多程式、多執行緒的開啟,並不是一定需要的,你可以自己設定一套規則,當一段時間的任務到達多少次被觸發時候選擇開啟多執行緒,多程式。否則就是浪費

3.重型應用架構遠不止這些,所以標題是淺談,等下個月技術作者再度飛速提升一波,再來談這些。

這裡推薦關注作者的微信公眾號:前端巔峰

傳送加群,我會將你拉進segmentFault的前端交流群,很多小姐姐哦~

技術氛圍槓槓滴~ 關鍵漂亮的sf小姐姐也在裡面哦~

如果覺得寫的不錯,一定要點個贊再走,一定哦~

相關文章