雙線程前端框架:Voe.js

halo,你們好,我是132,你們很久不賤了哈!html

最近上課真的很忙,時間不多的用來寫代碼了::>_<::,今天主要給你們帶來的是一個新框架 voe前端

看一眼 API,乍一看彷彿和 fard 相似的 API,彷彿又寫了個跨端小程序框架?

然而並非……vue

voe 是一個底層小程序框架node

意思是它實現了小程序的雙線程,利用「沙箱」 隔離 web 環境,屏蔽 dom 能力react

接下來結合 微信小程序 介紹一下主要思路:git

目標

絕對的控制力,意思是用戶不能操做 dom,不能使用 web API,不能使用完整的 jsx 和 html,不能……反正就是啥都不能!github

就好像 sm 角色同樣,s 對 m 的絕對控制與虐待,m 只能服從命令與受虐web

因此我把小程序的雙線程架構又稱爲 【主奴架構】算法

沙箱

小程序中不能操做 dom,不是由於它屏蔽了 dom API 或者屏蔽了事件,這樣作是不切實際的小程序

你們都是尋找一個非 dom 環境做爲沙箱,好比 v8,好比 worker,好比 native,好比 wasm

以上都是 OK 的,我猜微信小程序等也是用的相似的沙箱

voe 使用 web worker 做爲沙箱

爲何不使用 v8 或 native?

這就是純粹的我的選擇了,不選擇 v8 或 native 應該是,可是恰恰我我的更偏前一點,web worker 的計算力默認是優於 v8 或 native 的(同等硬件水平),可是 v8 也有好處,好比 node 可使用 cookie,而後擁有一些先進的 API

將框架拆到兩個不一樣的線程中

重點來了,兩個線程中,如何安排框架工做呢?

有幾個原則:

  1. 用戶邏輯必須跑在 worker 中,徹底不讓碰 主線程
  2. 計算力的邏輯儘量多的放到 worker 中

因而乎,就變成下面這個樣子:

而後,困難如約而至,沙箱與主線程之間的鴻溝來自 dom 元素和 事件函數,這二者沒法傳遞

我絞盡腦汁,想了一個徹底之策

將不能傳遞的東西保存到本身線程中並創建映射,將索引傳到另外一個線程

好比,事件是這樣傳遞的:

let handlers = new WeakSet()
  if (props) {
    for (const k in props) {
      if (k[0] === 'o' && k[1] === 'n') {
        let e = props[k]
        let id = handlers.size + 1
        handlers.set({ id: e })
        props[k] = id      
      }
    }
  }
複製代碼

將事件放到 map 中存起來,而後將 id 傳給主線程,主線程事件觸發的時候,將 id 和 event 參數交還給 worker

for (const k in props) {
    if (k[0] === 'o' && k[1] === 'n') {
      let id = props[k]
      props[k] = event => {
        // 不能傳太多,此處省略對事件的簡化操做
        worker.postMessage({
          type: EVENT,
          event,
          id
        })
      }
    }
}
複製代碼

而後在 worker 中,根據索引映射,找到對應的事件並觸發

是的沒錯就是這樣,這個方法是萬能的,好比咱們的 diff 方法

既然 diff 沒法將 dom 傳出去,那麼咱們就傳 dom 的 index

if (oldVNode ==null||oldVNode.type!==newVNode.type) {    
  parent.insertBefore(createNode(newVNode), node)
}
複製代碼

好比這個方法,parent 和 node 都是 dom 元素,是沒辦法傳遞的,咱們就……傳他們的索引,may be 長這樣:

[ [0,'insertBefore',1] ]
複製代碼

dom 是這樣的:

<div id="root" index="0">
  <ul index="1">
    <li index="2" />
    <li index="3" />
  </ul>
</div>
複製代碼

若是此時咱們要刪除 index 爲 3 的節點,那對應生成的結構,應該是這樣:

[ [1,'removeChild',3] ]
複製代碼

刺不刺激,咱們成功找到了一個思路,可以實現不一樣的 diff 算法啦

要知道,微信小程序就沒有找到相似的思路,他們的 diff 仍是 virtual-dom 的那套古老的深度遍歷,代碼多性能差……

按照一樣的思路,咱們還能夠在在 worker 中將主線程的函數的參數傳到主線程

好比咱們模擬一個 alert 方法,就能夠這麼作:

function alert(text){
  postMessage({
    name:'alert',
    text
  })
}
複製代碼

而後主線程接收參數,並執行

worker.onmessage = {data} => window[data.name](data.text)
複製代碼

如此,完美,這樣咱們就能夠在 worker 中選擇性的使用主線程的方法和變量了

綜上所述,上面介紹了雙線程的主要思路,這些思路不只僅適用於這個框架,一樣適用於其餘跨端的場景

vue 3

這裏簡單說一下 vue 3,嗯你們看到,voe 的名字和 API 神似 vue 3,事實上我確實將 vue 3 的核心抄過來了,包括依賴收集,響應式,狀態更新,組合函數……

這只是順手的事兒,其實比起 voe 的核心思路,API 是沒什麼所謂的

由於幾乎全部的公司,若是想要搞本身的小程序,都只能過來參考思路,而後 API 極可能就和微信保持一致了

因此我以爲 vue 3 的 API 足夠簡單,正好能夠弱化 API

就抄過來了……

你們能夠能夠將 voe 做爲 vue 3 的最小實現,用於協助閱讀源碼也是很 OK 的哈!

雙線程 vs 異步渲染

題外話,你們應該都知道我以前寫的框架 fre.js,也應該對 concurrent(時間切片)、suspense 等異步渲染的機制有所瞭解

現在我又來搞 web worker,是由於它倆的思路是相似的,場景也是同樣的

  1. 時間切片是利用宏任務,將 diff 等低優先級任務後放到宏任務隊列中,從而模擬了雙線程,不阻斷 UI
  2. 雙線程是利用 web worker,將 diff 等高計算力的任務放到 worker 中,從而不阻斷主線程 UI 渲染

二者的場景一樣都是可視化,高幀率動畫,大量數據與計算……

惋惜自己這種場景需求實在太少了,因此 preact 和 vue 團隊紛紛發聲,表示不想搞也不須要搞::>_<::

可是到我這來講的話,其實我不在乎框架有沒有人用,也不在於業務的,我更加傾向於一種技術創新,因此從這個方面,只要是新的思路,總歸有它的價值

總結

呼~終於寫完了,在掘金髮文,必需要長啊

最後放上 voe 的 github:

github.com/132yse/voe

歡迎關注與 star!

另外創建了一個前端羣,用於交流各類 new idea:813783512

相關文章
相關標籤/搜索