Zone.js 簡介 & 拋磚引玉


Zone.js是angular團隊參照NodeJS的Domain,Dart的Zone,為angular 2開發的核心組件。

 screenshot

一開始,我對Zone.js是拒絕的。我們知道類似的 Domain 模塊,主要是為了解決異步錯誤跟蹤問題。所以,當我沒有太強烈的錯誤跟蹤需求的時候,Zone.js有啥用?

然而execution context不僅僅可以用來跟蹤異步錯誤,還可以做一些猥瑣而實用的事情。

先來理解一下 execution context

Zone.current.fork({}).run(function () { Zone.current.inTheZone = true; setTimeout(function () { console.log('in the zone: ' + !!Zone.current.inTheZone); // 'in the zone: true' }, 0); }); console.log('in the zone: ' + !!Zone.current.inTheZone); // 'in the zone: false' 

execution context,我們可以理解成只與當前 fork 出來的Zone實例相關的上下文。

上面的例子很明顯,因為只有在 fork 中 Zone 的實例我們才設置了 Zone.current.inTheZone 為 true,所以在外面打印出來的結果是 false。

好神奇,這個怎么做到的呢?

我們想像上面那個過程是同步的,那么發生了什么呢?

const defaultZone = Zone.current // 生成一個新的Zone const zone = new Zone() // 設置當前zone Zone.current = zone // 對當前zone設值 Zone.current.inTheZone = true console.log('in the zone: ' + !!Zone.current.inTheZone) // 退出當前zone Zone.current = defaultZone console.log('in the zone: ' + !!Zone.current.inTheZone) 

很好,同步沒有什么問題,那么異步怎么辦呢?其實很簡單,就是在每一個異步入口加一個看門人,就可以了。

const defaultZone = Zone.current // 生成一個新的Zone const zone = new Zone() // 設置當前zone Zone.current = zone // 對當前zone設值 Zone.current.inTheZone = true const anonymousA = function () { console.log('in the zone: ' + !!Zone.current.inTheZone); // 'in the zone: true' } // 給進入異步的函數配發令牌映射到對應zone anonymousA._zone = zone // 退出當前zone Zone.current = defaultZone setTimeout(() => { // 函數重新回來,設置當前 zone Zone.current = anonymousA._zone anonymousA.call(this) // 退出當前zone Zone.current = defaultZone }, 0) console.log('in the zone: ' + !!Zone.current.inTheZone) 

當然Zone.js實現比上面復雜得多,有興趣的同學可以看看源代碼。

在同一個項目使用不同版本的 jQuery

從上面的例子看,我們可以看到,我們可以在 zone 實例上保存只有該 zone 使用的屬性。那么我們在利用Object.defineProperty就可以達成我們的目標了。

  • 我們先簡單寫一個模塊執行器(意思是我才不想管加載的事情):
// 寫的巨簡單,不要吐槽 !function (win, Zone) { var map = {}; var noop = {}; var dependence = {}; var alias = {}; var hasSet = {}; // 因為懶,僅支持 define(name, factory),反正只是 demo function define(name, factory) { if (typeof factory === 'function') { map[name] = { factory: factory, exports: noop }; } else { map[name] = { exports: factory }; } } function require(name) { var module = map[name] if (module.exports !== noop) return module.exports; if (dependence[name]) { var properties = {}; // 利用Object.defineProperty 組裝 window.xxx -> require('xxx') 的映射 Object.keys(dependence[name]).forEach(function (key) { var res; if (alias[key]) res = alias[key]; else res = key; properties[res] = require(key + '@' + dependence[name][key]); if (!hasSet[res]) { hasSet[res] = true; Object.defineProperty(window, res, { get: function () { return Zone.current.get(res) } }); } }); // 對每個模塊,fork 一個 Zone 實例進行執行 Zone.current.fork({ properties: properties }).run(function () { module.exports = module.factory() }); } else { module.exports = module.factory(); return module.exports; } } function config(opt) { Object.assign(dependence, opt.dep); Object.assign(alias, opt.alias); } require.config = config; window.define = define; window.require = require; }(window, Zone) 
  • 試用一下:
// 模擬兩個jQuery define('jquery@1.4', { version: '1.4', bind: function () { console.log('call bind'); } }) define('jquery@1.8', { version: '1.8', on: function () { console.log('call on'); } }) // 僅僅打印版本,不做任何事情 function logVersion() { console.log('version === ', $.version) } // 要運行的第一段代碼 define('module1', function module1() { // 使用1.8版本 $.on(); // 證明即使異步調用,這里面的 $ 依然指向正確 setTimeout(logVersion, 100) }) // 要運行的第二段代碼 define('module2', function module2() { // 使用1.4版本 $.bind(); // 證明即使異步調用,這里面的 $ 依然指向正確 setTimeout(logVersion, 300) }) // 載入依賴 require.config({ dep: { module1: { 'jquery': '1.8' }, module2: { 'jquery': '1.4' } }, alias: { 'jquery': '$' } }) require('module1') require('module2') 

具體實現參見:(two-different-jquery)[https://github.com/miniflycn/async-technique-you-may-do-not-know/tree/master/two-different-jquery]

更進一步

其實我們可以基於 Zone.js 做一個 Sandbox,則在大型重歷史包袱的應用中,可以很好地將多個技術體系共存而不產生惡心的沖突問題。

或者做一個對任意模塊依賴注入的方案,對模塊之間做完全解耦。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM