Zone.js 簡介 & 拋磚引玉

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

 screenshot

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

然而execution context不單單能夠用來跟蹤異步錯誤,還能夠作一些猥瑣而實用的事情。jquery

先來理解一下 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實例相關的上下文。git

上面的例子很明顯,由於只有在 fork 中 Zone 的實例咱們才設置了 Zone.current.inTheZone 爲 true,因此在外面打印出來的結果是 false。github

好神奇,這個怎麼作到的呢?

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

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) 

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

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就能夠達成咱們的目標了。oop

  • 咱們先簡單寫一個模塊執行器(意思是我纔不想管加載的事情):
// 寫的巨簡單,不要吐槽 !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]ui

更進一步

其實咱們能夠基於 Zone.js 作一個 Sandbox,則在大型重歷史包袱的應用中,能夠很好地將多個技術體系共存而不產生噁心的衝突問題。

或者作一個對任意模塊依賴注入的方案,對模塊之間作徹底解耦。

相關文章
相關標籤/搜索