zoukankan      html  css  js  c++  java
  • 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,则在大型重历史包袱的应用中,可以很好地将多个技术体系共存而不产生恶心的冲突问题。

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

  • 相关阅读:
    学习进度条(六)
    结对开发——-四则运算四
    结对开发——最大联通子数组的和
    团队项目成员和题目
    下载与安装APache Cordova
    学习进度条(五)
    构建之法阅读笔记05
    敏捷开发方法综述
    学习进度条(四)
    psp项目计划
  • 原文地址:https://www.cnblogs.com/justany/p/5864069.html
Copyright © 2011-2022 走看看