博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
原生JS+观察者模式实现一个模块加载器
阅读量:6847 次
发布时间:2019-06-26

本文共 2901 字,大约阅读时间需要 9 分钟。

在 ,这篇文章中,了解了一个简单的require是如何实现的。

最近看了如何实现一个MVVM的文章,恍然大悟,其实模块加载器也可以使用类似的方法。这里,每一个callback就被放在一个实例化的Watcher对象里。

参考Vue.js的数据双向绑定实现方式,将每一个模块放入一个订阅器Dep中,将每一个task(依赖于该模块的回调函数)放入一个Watcher中。同一个Dep有多个依赖它的Watcher。len是每一个Wathcer所依赖模块的数目。当模块装载好后,notify它的Watcher,len减一。当$len为零,执行这个Watcher的task

function createWatcher(callback, deps, dep) {    return new Watcher(callback, deps, dep, Module)}复制代码

我们最后暴露的requirejs会指向下面这个对象。baseUrl被设置后,所有的路径都相对于该baseUrl,如果没有被设置,那么我们会将当前require.js的地址作为baseUrl,module存放模块。

let Module = {    config: {        baseUrl: "",        paths: {}    },    module: {    },    host: location.protocol + '//' + location.host};复制代码

callback是我们需要执行的函数。这个callback可以是require里面的待执行函数,也可以是define里面有依赖另一个define的函数。如果define里面没有依赖,则不会放入Wathcer里面。

require(['./ww.js', function(ww){      //...}]);define(['./aa.js', function(aa){    return aa}]);复制代码

我们再来看看Watcher这个构造函数。task是一个待执行的callback,uris是这个异步callback所依赖的模块(地址),dep是一个订阅器。len是依赖模块的数组长度。如果一个模块加载好了,那么通知这个Watcher,这个Watcher的len变量就减一。对于一个Watcher,我们不用关心当前到底是哪个模块加载好了,反正只能是所有依赖模块加载好,这个task才能被执行。所以当$len为零的时候,表面依赖全部加载好,那么这个Wathcer就执行这个task

function Watcher(task, uris, dep, Module){    this.$task = task;    this.$uris = uris;    this.dep = dep;    this.$Module = Module;    this.modArr = [];    this.$len = this.$uris.length;}复制代码

Watcher每执行一次update,this.$len--。当为零的时候,执行this.run()方法。this.run()中,如果task是一个函数,那么执行执行。因为在define函数中,如果define里面没有依赖,就会将其callback直接放入Watcher。如果有依赖,则会先创建一个task对象,将当前define脚本的src存入task,以便触发该dep的notify方法。

Watcher.prototype = {    update: function () {        this.$len--;        if (this.$len <= 0) {            this.run();        }    },    run: function () {        let mod = this.$Module.module,            task = this.$task;        this.$uris.forEach(uri => {            this.modArr.push(mod[uri].obj);        });        //this.$Module.module[this.dep.depName].obj =        if (typeof task == 'function') {            task.apply(null, this.modArr);            return        }        let src = task.currentSrc;        mod[src].obj = task.callback.apply(null, this.modArr);        mod[src].dep.notify();        this.dep.removeSub(this);        return    }};复制代码

下面我们来讲讲Dep订阅器。对于每一个模块,我们用一个订阅器来存放它,它的subs数组,存放所有依赖于它才能执行的task,即Watcher。不管define有多深,模块a依赖于模块b,模块b依赖于模块c。当模块c加载好后(约定模块c是不依赖于任何其他模块的),模块c的订阅器dep触发notify方法,subs里面的Watcher的update方法。

function Dep(depName){    this.id = uid++;    this.subs = [];    this.depName = depName;}Dep.prototype = {    /**     * 添加订阅, 将watcher添加进数组subs     * @param {Object} task new watcher()     */    addSubs: function(task){        this.subs.push(task);    },    /**     * 删除订阅, 将watcher从数组subs中删除     * @param {Object} task new watcher()     */    removeSub: function(task){        let index = this.subs.indexOf(task);        (index != -1) && this.subs.splice(index, 1);    },    /**     * 当该模块加载好的时候, 通知所有依赖它的task     */    notify: function(){        this.subs.forEach(task => {            task.update();        });    }};复制代码

以上是代码的部分解析...

转载于:https://juejin.im/post/5aa2cb0a51882555627ce3b0

你可能感兴趣的文章
PostgreSQL 10 GIN索引 锁优化
查看>>
《AngularJS深度剖析与最佳实践》一1.7 实战小结
查看>>
rlite —— 兼容 Redis 的嵌入式 NoSQL 引擎
查看>>
《MATLAB神经网络超级学习手册》——2.5 本章小结
查看>>
SkyNet:用开源系统管理物联网
查看>>
《Linux内核修炼之道》——1.2 内核的版本
查看>>
因为人人都作弊 Google 淘汰 Octane JS 基准测试
查看>>
《深入理解Elasticsearch(原书第2版)》一2.1 Apache Lucene默认评分公式解释
查看>>
《金蝶ERP-K/3模拟实战——财务/供应链/生产制造(第2版)》——1.3 安装金蝶ERP-K/3(V11.X)...
查看>>
Linux 开发者考虑为内核崩溃引入条形码
查看>>
Nike 发布其开源项目,意欲何为?
查看>>
Maven项目中获取classpath和资源文件的路径
查看>>
《Adobe Dreamweaver CC经典教程》——1.6 个性化首选项
查看>>
为啥HashMap的长度一定是2的n次方
查看>>
Elasticsearch java简单增删改查
查看>>
Xcode8使用体验
查看>>
springmvc+mybatis+restful+webservice分布式架构
查看>>
厉害了,他用PS不是P照片而是……
查看>>
java B2B2C Springcloud电子商务平台源码 -Feign之源码解析
查看>>
Spring 源码分析之 bean 实例化原理
查看>>