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:
- Dependent modules also depend on what to do
- 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.