前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Zone.js 简介 & 抛砖引玉

Zone.js 简介 & 抛砖引玉

作者头像
IMWeb前端团队
发布2019-12-04 16:56:05
8340
发布2019-12-04 16:56:05
举报
文章被收录于专栏:IMWeb前端团队

本文作者:IMWeb 杨文坚 原文出处:IMWeb社区 未经同意,禁止转载

Zone.js是angular团队参照NodeJS的Domain,Dart的Zone,为angular 2开发的核心组件。

一开始,我对Zone.js是拒绝的。我们知道类似的 Domain 模块,主要是为了解决异步错误跟踪问题。所以,当我没有太强烈的错误跟踪需求的时候,Zone.js有啥用?

然而execution context不仅仅可以用来跟踪异步错误,还可以做一些猥琐而实用的事情。

先来理解一下 execution context

代码语言:javascript
复制
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。

好神奇,这个怎么做到的呢?

我们想像上面那个过程是同步的,那么发生了什么呢?

代码语言:javascript
复制
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)

很好,同步没有什么问题,那么异步怎么办呢?其实很简单,就是在每一个异步入口加一个看门人,就可以了。

代码语言:javascript
复制
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就可以达成我们的目标了。

  • 我们先简单写一个模块执行器(意思是我才不想管加载的事情):
代码语言:javascript
复制
// 写的巨简单,不要吐槽
!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)
  • 试用一下:
代码语言:javascript
复制
// 模拟两个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,则在大型重历史包袱的应用中,可以很好地将多个技术体系共存而不产生恶心的冲突问题。

或者做一个对任意模块依赖注入的方案,对模块之间做完全解耦。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016-07-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 先来理解一下 execution context
  • 好神奇,这个怎么做到的呢?
  • 在同一个项目使用不同版本的 jQuery
  • 更进一步
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档