您好,登錄后才能下訂單哦!
這篇文章主要介紹了如何使用imba.io框架,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
上圖表示了vue, react 以及 imba 在 todo 這個項目中擁有60個 todoItem 不同進行 crud 操作的表現。可以看到 imba 達到了每秒操作5w次以上。如果你也想試一試該測試,可以訪問Todos Bench 。測試使用的是 Benchmark.js 。
imba 簡單介紹
imba 是一種新的編程語言,可以編譯為高性能的 JavaScript。可以直接用于 Web 編程(服務端與客戶端)開發。
下面是語法:
// 自定義標簽 tag App // 屬性 prop items // 方法定義 def addItem if @input.value items.push(title: @input.value) @input.value = "" def toggleItem item item:completed = !item:completed // 掛載 Imba.mount(element, into) // 如果沒有第二個參數,默認掛載到 document.body 上面 Imba.mount <App.vbox items=[] -> <form.bar :submit.prevent.addItem> <input@input> <button> 'add' <ul> for item in items <li .done=item:completed :tap.toggleItem(item)> item:title
可以看出作者喜歡 ruby 以及 pug與,偏向于縮進類風格(個人并不是很喜歡這種語法風格)。具體語法可以參考 imba 文檔 。當然了,因為可以編譯成js,所以服務端編譯成 js 進行node開發也是可以實現的。
imba 框架極速的性能基礎
任何一個實現的性能優化都有其理論基礎,那么 imba 性能那么快的基礎究竟是什么呢?答案也就是 memoized DOM(記憶DOM)。
理論基礎
瀏覽器的 DOM 操作可以說是瀏覽器最終要的功能,無論框架是基于虛擬 DOM 或者是真實 DOM,最終離不開操作 DOM 對象。
HTML DOM 是瀏覽器定義了訪問和操作 HTML 文檔的標準方法。但是操作 DOM 的接口是 JavaScript。但是瀏覽器通常會把 js 引擎和渲染引擎分開實現。也就是頁面實際渲染部分是和解析js部分分開的。
借著《高性能的 JavaScript》話說,如果把 DOM 和 js 各自想象為島嶼。他們需要一座橋進行溝通。所以每一次執行 DOM 操作就過橋一次。
那我們先談談虛擬DOM,虛擬DOM 的性能提升在于是將 DOM 的對比放在了js層。進而通過對比不同之處來進行實際的 DOM 渲染。也就是說,其實虛擬DOM 并沒有“實際”的性能收益,橋仍舊還在那邊。僅僅在 js引擎需要過橋的那邊找到了一位聰明睿智的大叔,對過橋的人和過橋的貨物進行優化和限制(虛擬DOM 高性能的diff算法,狀態批量更新)。
那么 memoized DOM 又是怎么做的呢?把 DOM 節點的控制直接放入內存之中。類似于此類優化.
function getEls(sel) { // 設置緩存 if (!getEls.cache) getEls.cache = {}; // 如果緩存中存在 el,直接返回 if (getEls.cache[sel]) { return getEls.cache[sel]; } // 沒有去通過 DOM 查詢 const r = document.querySelectorAll(sel || '?'), length = r.length; // 緩存并返回元素節點 return getEls.cache[sel] = (length == 1) ? r[0] : r; }
我們可以測試一下。這里我寫一個 getElsByDocument 以及 simplePerTest。
// 直接通過 querySelectorAll 獲取節點 function getElsByDocument(sel) { const r = document.querySelectorAll(sel || '?'), length = r.length; return length == 1 ? r[0] : r; } // 簡單性能測試 function simplePerTest(fn, el) { const fnName = fn.name console.time(fnName) // 2000 次操作 for(let i = 0,len = 2000; i < len; i++) { fn(el) } console.timeEnd(fnName) }
這個緩存的節點查詢可要比 querySelectorAll 快了 140倍以上啊,隨著 img 節點越多,得到的性能提升也越高啊。如果imba 框架中所有的節點都在內存中呢?同時,我們還會得到一個 js 運行時優化( GC 的大量減少),因為虛擬DOM 要維護一個樹,在進行多次 crud 之后就會產生大量無用對象從而導致瀏覽器進行 GC,而 memoized DOM 在多次 crud 不會進行多次 GC。(可能會在渲染引擎中 GC?但我感覺渲染引擎中GC 要比JS 中影響要小很多。挖個坑,研究完渲染引擎再來探討一下)
框架實踐
實例如下所示:
tag Component def render <self> <h2.title> "Welcome" <p.desc> "I am a component"
上面的自定義組件會編譯成下面的js
var Component = Imba.defineTag('Component', function(tag){ tag.prototype.render = function (){ var $ = this.$; // 返回dom return this.setChildren($.$ = $.$ || [ createElement('h2',$,0,this).flag('title').setText("Welcome"), createElement('p',$,1,this).flag('desc').setText("I am a component") ]).synced(); }; });
仔細觀察一下這里的函數,你會看到該組件在第一次調用渲染時,將使用 createElement 創建兩個子節點,并設置它們的屬性并且緩存。第二次或者第一萬次調用時,children-array將被緩存,不會發生任何調用。
// 在第一次調用時候 $.$不存在 $.$會等于 后面的數組 // 第二次調用 $.$ 是存在的,無運行時消耗 $.$ = $.$ || 數組
其中查看源碼,我們可以看到 setChildren 函數都是對真實DOM 進行了操作。獲取之前的DOM節點進行一系列操作后將當前節點返回并緩存。
tag.prototype.setChildren = function (new$,typ){ var old = this._tree_; if (new$ === old && (!(new$) || new$.taglen == undefined)) { return this; }; if (!old && typ != 3) { this.removeAllChildren(); appendNested(this,new$); } else if (typ == 1) { var caret = null; for (var i = 0, items = iter$(new$), len = items.length; i < len; i++) { caret = reconcileNested(this,items[i],old[i],caret); }; } else if (typ == 2) { return this; } else if (typ == 3) { var ntyp = typeof new$; if (ntyp != 'object') { return this.setText(new$); }; if (new$ && new$._dom) { this.removeAllChildren(); this.appendChild(new$); } else if (new$ instanceof Array) { if (new$._type == 5 && old && old._type == 5) { reconcileLoop(this,new$,old,null); } else if (old instanceof Array) { reconcileNested(this,new$,old,null); } else { this.removeAllChildren(); appendNested(this,new$); }; } else { return this.setText(new$); }; } else if (typ == 4) { reconcileIndexedArray(this,new$,old,null); } else if (typ == 5) { reconcileLoop(this,new$,old,null); } else if ((new$ instanceof Array) && (old instanceof Array)) { reconcileNested(this,new$,old,null); } else { // what if text? this.removeAllChildren(); appendNested(this,new$); }; this._tree_ = new$; return this; };
如果我們使用了動態屬性。代碼如下
tag Component def render <self> <h2.title> "Welcome" # 有 50% 幾率 擁有 red class <p.desc .red=(Math.random > 0.5)> "IMBA"
可以得到如下代碼,詳細查看可以看出,imba 提取了可變量,放入了 synced 函數中,每次渲染中只會執行 synced 里面的數據,所以依然會得到極高的渲染速度
var Component = Imba.defineTag('Component', function(tag){ tag.prototype.render = function (){ var $ = this.$; return this.setChildren($.$ = $.$ || [ _1('h2',$,0,this).flag('title').setText("Welcome"), _1('p',$,1,this).flag('desc').setText("Roulette") ],2).synced(( $[1].flagIf('red',Math.random() > 0.5) ,true)); }; });
精確的抽取不可變量,然后無需虛擬DOM 計算,同時對于真實DOM 還進行了緩存,我們可以看出 memoized DOM 與 虛擬DOM 不同,memoized DOM 是具有實際的性能收益。
imba 框架“虛假”的性能測試
我們在上面看到了 imba 框架的理論基礎,那么他是否真的比vue快50倍?當然不是,這也就是在上面說我是標題黨的原因。
瀏覽器的運行機制
瀏覽器本身只能達到 60 fps( 1 秒刷新了60次 )。當然了,其實對于體驗而言,60fps的體驗已經差不多夠用了,也就是瀏覽器渲染上大概需要 17ms 去渲染一次。事實上無論是每秒操作dom 5w次還是 1000次,瀏覽器渲染引擎也只會記錄當前的臟數據。然后在需要渲染時候再進行重繪與重排。
真實世界的內存限制
面對 memoized DOM 的緩存優化以及更少 GC 帶來的運行時提升,我們需要更多內存來對每一個 dom節點進行緩存。這個在初始化渲染時有大量的消耗。同時我們的瀏覽器執行速度和渲染速度已經足夠快了,虛擬DOM已經完全夠用了。
imba 框架與瀏覽器的暢想
Google io 大會 chorme Portals 技術
單頁應用程序(Single Page Applications,SPA)提供了很好的頁面交互,但代價是構建的復雜性更高,多頁面應用程序(Multi-page Applications,MPA)更容易構建,但最終會在頁面之間出現空白屏幕。
Portals 結合了這兩者的優勢,主要用于改進網頁交互體驗,目標是無縫導航。它類似于 iframe ,內嵌在網頁上,但可以導航到頁面內容上。用戶在一個頁面跳轉另一個內容時,雖然 URL 相應地發生變化,但是不需要打開另一個窗口,此時該內容標記的 Portals 會變成原來頁面的頂級頁面,同時原來頁面在其后保持主進程地位。現場演示了這對于購物體驗的極大便利,此外還有對漫畫這類單頁面應用的演示。
js引擎 與 渲染引擎的關聯
在之前,瀏覽器 js引擎和渲染引擎是沒有任何關聯的,我們去寫動畫只能通過 setTimeout 或者 setInterval,更加沒有辦法知道瀏覽器什么時候處于空閑狀態,但是隨著時間的發展,我們可以通過 requestAnimationFrame 和 requestIdleCallback。requestAnimationFrame 要求瀏覽器在下次重繪之前調用指定的回調函數更新動畫。requestIdleCallback方法將在瀏覽器的空閑時段期間對要調用的隊列函數進行執行。
那么內置DOM 操作是否能夠在js引擎中,是否能夠減少過橋的性能消耗或者完全把橋打通。讓我們拭目以待。
感謝你能夠認真閱讀完這篇文章,希望小編分享的“如何使用imba.io框架”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。