In-depth understanding of requireJS-Implementing a simple module loader and in-depth understanding of requirejs-

Source: Internet
Author: User
Tags define function

In-depth understanding of requireJS-Implementing a simple module loader and in-depth understanding of requirejs-

In the previous article, we have emphasized more than once the importance of Modular programming and the problems that can be solved:

① Solve the naming conflict problem of single file variables

② Solve the problem of front-end multi-person collaboration

③ Solve the file dependency problem

④ Load on demand (this statement is false)

⑤ ......

I have read the source code of requireJS in the middle to gain an in-depth understanding of the loader. However, for many students, the implementation of the loader is still unclear.

As a matter of fact, it is not implemented through code. simply by reading a library or framework, you can only achieve a minimum understanding. So today we will implement a simple loader.

Analysis of the loader Principle

Integration

In fact, a program requires a complete module. The following code is used as an example:

// Obtain the performance coefficient var performanceCoefficient = function () {return 0.2 ;}; // var companyReserve = function (salary) {return salary * 0.2 ;}; // Personal Income Tax var incomeTax = function (salary) {return salary * 0.2 ;}; // basic salary var salary = 1000; // final salary var mySalary = salary + salary * performanceCoefficient (); mySalary = mySalary-companyReserve (mySalary)-incomeTax (mySalary-companyReserve (mySalary); console. log (mySalary );

For my complete salary, the company will have performance rewards, but its algorithm may be very complicated, which may involve attendance and fulfillment.

However, if there is an increase, there will be a reduction. Therefore, we will pay the housing provident fund and deduct personal income tax, which will ultimately be my salary.

For a complete program, the above process is indispensable, but each function may be exceptionally complicated, and everything related to money is complicated, therefore, the company's performance alone may exceed 1000 lines of code.

So here we will start to score:

<Script src = "companyReserve. js "type =" text/javascript "> </script> <script src =" incomeTax. js "type =" text/javascript "> </script> <script src =" cececoefficient. js "type =" text/javascript "> </script> <script type =" text/javascript "> // basic salary var salary = 1000; // final salary var mySalary = salary + salary * performanceCoefficient (); mySalary = mySalary-companyReserve (mySalary)-incomeTax (mySalary-companyReserve (mySalary); console. log (mySalary); </script>

The code table above indicates that "splitting" is enabled, and in fact it also creates a "merging" problem. How can I better merge them together, after all, the files may also involve dependencies. Here we enter our require and define

Require and define

In fact, the above scheme is still divided by files rather than modules. If the file name changes, the page will change. In fact, there should be a path ing to deal with this problem.

var pathCfg = { 'companyReserve': 'companyReserve', 'incomeTax': 'incomeTax', 'performanceCoefficient': 'performanceCoefficient'};

Therefore, a module corresponds to a path js file, and the rest is to load the corresponding module, because the front-end module involves requests. So this writing method:

companyReserve = requile('companyReserve');

It is not applicable to the front-end. Even if you see this, it must have some "Hands and feet". Here we need to follow AMD specifications:

Require. config ({'companyreserv': 'companyreserv', 'inmetax ': 'inmetax', 'authorization': 'performancecoefficient '}); require (['companyreserv', 'encoding ', 'performancecoefficient '], function (companyReserve, incomeTax, performanceCoefficient) {// basic salary var salary = 1000; // final salary var mySalary = salary + salary * performanceCoefficient (); mySalary = mySalary-companyReserve (mySalary)-incomeTax (mySalary-companyReserve (mySalary); console. log (mySalary );});

Here is the writing of a standard requireJS. First, define the module and its path ing, and define the dependencies.

require(depArr, callback)

This is basically the case for a simple and complete module loader. The first is a dependent array and the second is a callback. The callback requires that all dependencies be loaded before running, the callback parameter is the execution result of the dependent item. Therefore, the define module generally requires a return value.

How can we implement the solution?

Implementation Scheme

When it comes to module loading, the first response is ajax, because the content of module files can be obtained at any time, which is basically modular, but ajax cannot be used, because ajax has cross-origin problems

While the modular solution inevitably needs to deal with cross-origin issues, it becomes the first choice to use dynamic creation of script tags to load js files. However, the ajax solution is not used, there are still requirements for implementation difficulty

PS: in our actual work, there will be scenarios for loading html template files. Let's talk about it later.

We usually do this. require serves as the program portal to schedule javascript resources. After being loaded to each define module, each module quietly creates a script tag to load.

After loading, report to the require module queue that the loading is complete. When all the dependent modules in require are loaded, execute the callback.

The principle is roughly the same, but the rest is the concrete implementation, and then it is necessary to prove whether the theory is reliable.

Loader castrated implementation

Core Module

Based on the above theory, we first use three basic functions

var require = function () {};require.config = function () {};require.define = function () {};

These three modules are indispensable:

① Config is used to configure the ing between modules and paths, or it is useful for other purposes.

② Require is the program entry

③ Define design each module to respond to require scheduling

Then we will have a method to create the script tag and listen to its onLoad event.

④ LoadScript

Next, after loading the script tag, we should have a global module object to store the loaded modules. Therefore, we have two requirements:

⑤ Require. moduleObj module storage object

⑥ Module: constructor of the Module

With the above core modules, we have formed the following code:

(Function () {var Module = function () {this. status = 'loading'; // only the loading and loaded statuses exist. this. depCount = 0; // module dependency this. value = null; // return of callback execution of the define function}; var loadScript = function (url, callback) {}; var config = function (){}; var require = function (deps, callback) {}; require. config = function (cfg) {}; var define = function (deps, callback ){};})();

So the next step is the specific implementation, and then fill in the interface and details that are not available in the implementation process, often in the final implementation and the initial design does not have a half-cent relationship ......

Code Implementation

At the beginning of this implementation, I wanted to directly refer to the implementation of requireJS, but our boss smiled and took out a loader he wrote. I had to admit that it was a little demon.

So here I learned from its implementation and made a simple transformation:

(Function () {// store the loaded module var moduleCache ={}; var require = function (deps, callback) {var params = []; var depCount = 0; var I, len, isEmpty = false, modName; // obtain the JavaScript code snippet currently being executed. This command runs modName = document before the onLoad event. currentScript & document. currentScript. id | 'require _ main'; // simple implementation. The parameter check is not performed here. Only the array case if (deps. length) {for (I = 0, len = deps. length; I <len; I ++) {(function (I) {// dependent on the addition of depCount ++; // This callback is critical to loadMod (deps [I], function (param) {params [I] = param; depCount --; if (depCount = 0) {saveModule (modName, params, callback) ;}}) ;}( I) ;}} else {isEmpty = true ;}if (isEmpty) {setTimeout (function () {saveModule (modName, null, callback) ;}, 0) ;}; // consider the simplest logic to var _ getPathUrl = function (modName) {var url = modName; // not rigorous if (url. indexOf ('. js') =-1) url = url + '. js'; ret Urn url ;}; // module load var loadMod = function (modName, callback) {var url = _ getPathUrl (modName), fs, mod; // if the module has been loaded if (moduleCache [modName]) {mod = moduleCache [modName]; if (mod. status = 'loaded') {setTimeout (callback (this. params), 0);} else {// if the value is not directly inserted into onLoad, the dependency mod will be removed after the dependency is loaded. onload. push (callback) ;}} else {/* Here we will focus on the Module object status, which indicates how many times the Module is referenced and executed when onLoad actually corresponds to the requireJS Event callback. Callback: indicates that the dependency is removed from the notification. */mod = moduleCache [modName] = {modName: modName, status: 'loading', export: null, onload: [callback]}; _ script = document. createElement ('script'); _ script. id = modName; _ script. type = 'text/javascript '; _ script. charset = 'utf-8'; _ script. async = true; _ script. src = url; // This code is of little significance in this scenario and commented out // _ script. onload = function (e) {}; fs = document. getElementsByTagName ('script') [0]; fs. p ArentNode. insertBefore (_ script, fs) ;}}; var saveModule = function (modName, params, callback) {var mod, fn; if (moduleCache. hasOwnProperty (modName) {mod = moduleCache [modName]; mod. status = 'loaded'; // output item mod. export = callback? Callback (params): null; // remove parent class dependency. In fact, it is better to use event listening here while (fn = mod. onload. shift () {fn (mod. export) ;}} else {callback & callback. apply (window, params) ;}}; window. require = require; window. define = require ;})();

First, this code has some problems:

No parameter issues are processed, and no such string is processed.

Unprocessed circular dependency issues

Unprocessed CMD writing

Html template loading not processed

The parameter configuration is not processed, and baseUrl is useless.

Based on this, it is impossible to package files.

......

But these 100 lines of code are the core of the loader. The code is very short and helpful for you to understand the loader. There are two points to note:

① RequireJS uses event listening to process its own dependencies. Here, it is directly placed in the onLoad array.

② Here is a very interesting thing.

document.currentScript

This can get the code segment currently executed

RequireJS processes each module in onLoad. A different implementation is used here. After each js file is loaded, the require (define) method is executed.

After execution, the file being executed is obtained and the file name is loaded. Because of this, the onLoad event of the script is saved ......

Demo implementation

//utildefine([], function () { return {  formatNum: function (n) {   if (n < 10) return '0' + n;   return n;  } };});
//mathdefine(['num'], function (num) { return {  getRadom: function () {   return parseInt(Math.random() * num);  } };});
//mathdefine(['num'], function (num) { return {  getRadom: function () {   return parseInt(Math.random() * num);  } };});
Summary

Today, we have implemented a simple module loader that helps you understand requireJS or seaJS, and finally successfully enters the ranks of Modular programming.

The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.