Without tools, js directly realizes modularization on Web pages

Keywords: Javascript

Probably the principle is to load a stack of script files asynchronously. Personal projects can be tried. Enterprise projects can be used after careful consideration.
The method I learned came from "Javaascript Design Patterns", written by Zhang Rongming, because I think it's interesting. Let's share it with you.

Since it is necessary to add script files asynchronously to realize modularization directly on the web page, two troublesome problems need to be solved:

  1. Dependent modules also depend on what to do
  2. How to know the loading status of asynchronously added js files

//main.js does not need to pay attention to details, quickly understand the call mode can be
m.module(['dom/index'], function (dom) {
    var test = document.getElementById('test')
    console.log(dom(test).attr('id'))
    dom(test).css({
        'background': '#000',
        'color': '#fff'
    })
})
......
//dom/css.js
m.module('dom/css', ['shared/util'], function (util) {
    return function (ele, css, value) {
        if (util.isString(css)) {
            if (util.isNumber(value)) {
                return ele.style.css = value
            }
            return ele.style.css
        } else {
            for (var k in css) {
                ele.style[k] = css[k]
            }
        }
    }
})
......
//shared/util.js
m.module('shared/util', function () {
    return {
        isNumber: function (num) {
            return num === (0 || num) && num.constructor === Number
        },
        isString: function (str) {
            return str === ("" || str) && str.constructor === String
        }
    }
})

Let's start implementing this exposed module function

Toe the mark

Hide the implementation of the module and create a closure

(function(m){
    var m = m()
})(function(){
    window.m = {}
})

Instrumental function

Add two tool functions, loadScript and getUrl

//Loading script
var loadScript = function (src) {
        var _script = document.createElement('script')
        _script.type = 'text/javascript'
        _script.async = true
        _script.src = src

        document.getElementsByTagName('head')[0].appendChild(_script)
    },
    //Add A. js to the address
    getUrl = function (moduleName) {
        return String(moduleName).replace(/\.js$/g, '') + '.js'
    }

Implementation of module Function

As you can see from the above image example, the module function includes the function of creating and calling modules. It has three parameters.

  • url address
  • deps data type is array dependent module
  • callback The main function of this module

Get parameters

m.module=function(){
    var args = [].slice.call(arguments),
        //Take the last parameter, callback
        callback = args.pop(),
        //Get dependencies, and data types are arrays
        deps = (args.length && args[args.length - 1] instanceof Array) ? args.pop() : [],
        //address
        url = args.length ? args.pop() : null
...

This is the realization of the complete module function. At first glance, it is very complicated. Don't worry. The key of module lies in the following two functions (load module and setModule). Every asynchronous principle is against the brain. Habit is the door of the new world. Don't stick to the reading order.

m.module = function () {
    var args = [].slice.call(arguments),
        callback = args.pop(),
        deps = (args.length && args[args.length - 1] instanceof Array) ? args.pop() : [],
        url = args.length ? args.pop() : null
        
        params = [],//Depending on the sequence of modules, parameters for the use of the main function (callback function)
        depsCount = 0,//The number of dependencies on which the module has not been loaded
        i = 0
        
    if (deps.length) {
        while (i < deps.length) {
            //Closure preservation i
            (function (i) {
                //So each module executed by the script will have a depsCount
                depsCount++
                //Initialization of loadModule does not call its callback function (buffer). (See the implementation of loadModule below.)
                //But it adds callback functions to moduleCache and loads the dependent scripts at the same time.
                loadModule(deps[i], function (mod) {
                    //Here mod is the output of the dependent module
                    params[i] = mod
                    //When it equals zero, it performs its own callback
                    depsCount--  
                    if (depsCount === 0) {
                        //Add the output of the dependent module to the callback parameter so that the main function can be invoked directly with the parameter
                        setModule(url, params, callback)
                    }
                })
            })(i)
            i++
        }
    } else {
        //Once the dependency goes to the bottom, the module in a script file has no dependency (see setModule below).
        //Load Module initialization callbacks added to moduleCache are executed, and depsCount is - 1
        setModule(url, [], callback)
    }
}

If there is no dependency, setModule will be executed directly. If the module is dependent, it will call the buffer of loadModule cache, which is its callback function.
Let's first look at the implementation of loadModule and setModule

if(deps.length){
    ...
}else{
    setModule(url, [], callback)
}

Add a moduleCache variable for caching modules

//Inside closure
var moduleCache = {}
var setModule = function (moduleName, params, callback) {
    var _module, fn
    if (moduleCache[moduleName]) {
        _module = moduleCache[moduleName]
        _module.status = 'loaded'
        //export is the output of the module
        _module.exports = callback ? callback.apply(_module, params) : null
        while (fn = _module.onload.shift()) {
            //Execute callbacks and output your modules to buffers
            fn(_module.exports)
        }
    } else {
        callback && callback.apply(null, params)
    }
}
//Here the parameter callback is not the main function, but the saved buffer, flipping back the complete function of module in detail.
var loadModule = function (moduleName, callback) {
    var _module
    //Initialized
    if (moduleCache[moduleName]) {
        _module = moduleCache[moduleName]
        if (_module.status === 'loaded') {
            //Get it directly from the moduleCache cache
            setTimeout(callback(_module.exports), 4)
        } else {
            _module.onload.push(callback)
        }
    } else {
        //Initialization
        moduleCache[moduleName] = {
            //Address, or module name
            moduleName: moduleName,
            status: 'loading',
            //The output of the module return
            exports: null,
            onload: [callback]
        }
        //Add script
        loadScript(getUrl(moduleName))
    }
}

When writing an article for the first time, if you feel that it is difficult to understand or imprecise, you can comment on it or send me a private letter to revise it.
Give me a compliment if it helps.

Posted by JChilds on Tue, 07 May 2019 04:15:39 -0700