github

In this article, you learned how to implement a simple require in simple browser-side JS module loader.

I recently read an article on how to implement an MVVM and realized that module loaders can use a similar approach. Here, each callback is placed in an instantiated Watcher object.

Refer to vue.js’s bidirectional data binding implementation to put each module into a subscriber Dep and each task(the callback function that depends on that module) into a Watcher. The same Dep has multiple Watcher dependencies on it.Len minus one. When $len is zero, execute the Watcher task

function createWatcher(callback, deps, dep) {
    return new Watcher(callback, deps, dep, Module)
}
Copy the code

The final requirejs we exposed will point to the following object. Once baseUrl is set, all paths are relative to the baseUrl. If not set, we will store the module in the current require.js address as baseUrl, module.

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

Copy the code

Callback is the function we need to execute. This callback can be a function to be executed in require, or a function to be executed in define that depends on another define. If you don’t have dependencies in define, you don’t put them in Wathcer.

require(['./ww.js'.function(ww){
      / /...
}]);

define(['./aa.js'.function(aa){
    return aa
}]);
Copy the code

Let’s look at the Watcher constructor again. Task is a callback to be executed, URis is the module (address) on which the asynchronous callback depends, and DEP is a subscriber.Len minus one. For a Watcher, we don’t care which module is currently loaded, but the task can only be executed if all dependent modules are loaded. So when $len is zero and the surface dependencies are all loaded, the Wathcer executes the 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;
}
Copy the code

For every update Watcher executes, this.$len– When zero, the this.run() method is executed. This.run () executes if task is a function. Because in define, if there is no dependency in define, its callback is put directly into Watcher. If there are dependencies, a task object is created and the SRC of the current define script is stored in task to trigger the notify method of the DEP.

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}};Copy the code

Now let’s talk about the Dep subscriber. For each module, we use a subscriber to hold it, its subs array, for all tasks that depend on it to execute, namely the Watcher. No matter how deep the define is, module A depends on module B, and module B depends on module C. When module C is loaded (the convention is that module C is not dependent on any other module), module C’s subscriber DEP triggers notify and the Update method of the Watcher in subs.

function Dep(depName){
    this.id = uid++;
    this.subs = [];
    this.depName = depName;
}

Dep.prototype = {
    Subs * @param {Object} task new watcher() */
    addSubs: function(task){
        this.subs.push(task);
    },
    * @param {Object} task new watcher() */
    removeSub: function(task){
        let index = this.subs.indexOf(task); (index ! =- 1) && this.subs.splice(index, 1);
    },
    /** * Notifies all dependent tasks */ when the module is loaded
    notify: function(){
        this.subs.forEach(task= >{ task.update(); }); }};Copy the code

This is part of the code parsing…