github https://github.com/trueFeeling/Module_Loader
在 简单的浏览器端 js 模块加载器 [代码解读], 这篇文章中, 了解了一个简单的 require 是如何实现的
最近看了如何实现一个 MVVM 的文章, 恍然大悟, 其实模块加载器也可以使用类似的方法这里, 每一个 callback 就被放在一个实例化的 Watcher 对象里
参考 vue.js 的数据双向绑定实现方式, 将每一个模块放入一个订阅器 Dep 中, 将每一个 task(依赖于该模块的回调函数) 放入一个 Watcher 中同一个 Dep 有多个依赖它的 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, 我们不用关心当前到底是哪个模块加载好了, 反正只能是所有依赖模块加载好, 这个 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