There is a modular packaging tool about Webpack
- seajs
- requirejs
- Conmon.js
- Esmoudle = > web browser
- Umd => Compatibility processing
The process is basically defining a module, exporting a module, using a module,
Let’s first look at modularity
When we got really big, we would modularize our JS code,
For example, if you introduce jquery and Zepto, both of which reference ‘$’, there will be a naming conflict, which will result in no way to manage dependencies and no way to control the order in which dependencies are loaded
- CMD (which is a widely used JS specification his core idea is to pass
require
Method ‘synchronous’ loads dependent on other modules, exports exposed interfaces via moudle.exports =>CommonJS is AMD’s most representative implementation, node implements common.js internally
A. s file
import.exports = 'Hello world';
Copy the code
B.j s file
let str = require(./a.js);
Copy the code
If the require method is synchronized in Node, how does it work?
let fs = require('fs');// Read the file
function require(moduleName){ / / moduleName path
let content = fs.readFileSync(moduleName,'utf8');
// The last argument is the content body of the function
let fn = new function("Exports",'moudle'.'require'.'_dirname'.'_filename',content + '\n return moudle.response')
let moudle = {
exports:{}
}
return fn(moudle.exports,moudle,require,_dirname,_dirname)
}
/* function(' exports ',' moudle','require','_dirname','filename',content + '\n return moudle.response'){import.exports = 'Hello world'; return import.exports } */
Copy the code
- Common. Js is only for Node. This method can be used in browsers, and the most representative implementation is
AMD advantages
- Can be run directly in the browser without transcoding
- Multiple dependencies can be loaded
- Runs in Node and browsers
Declare through the require module
define('name'[],function(){
})
define('age'[],function(){})require(['name'.'age'].function(name,age){
console.log(name,age)
})
let factories = {};// Bind the name to the dependency
function define(moudlName,dependencies,factory){// Module name, dependency, factory function
factories[moudlName] = factory;
}
let function require(mods,callback){
let result = mods.map(function(mod){
let factory = factory[mod];// Retrieve each corresponding function
let exports;
exports = factory();
return exports;// The result is a new array
})
calback.apllay(null,result)
}
Copy the code
So we have a simple AMD implementation, and then there is a more complicated situation assuming we add dependencies
Declare through the require module
define('name'[],function(){
})
define('age'['name'].function(name){
return name + 9
})
require(['name'.'age'].function(name,age){
console.log(name,age)
})
Copy the code
So how do you manage dependencies now?
let factories = {};// Bind the name to the dependency
function define(moudlName,dependencies,factory){
// Module name, dependency, factory function
factory.dependencies = dependencies;// Attach dependencies to functions
factories[moudlName] = factory;
require(dependencies,fucntion(... args)){ factory.applay(null.arguments);
exports = factory.apply(null ,arguments)};return exports;
}
let function require(mods,callback){
let result = mods.map(function(mod){
let factory = factory[mod];// Retrieve each corresponding function
let exports;
exports = factory();
return exports;// The result is a new array
})
calback.apllay(null,result)
}
require(['age'].function(name,age){
console.log(name,age)
})
Copy the code
Let’s focus on CommonJS
Conmon. Js
ConmonJS specification
- Defines how to import the module require
- Exports of module. Exports exports XXX
- It also defines a JS as a module
- If the first load is complete, it is stored in the cache and read from the cache the second time
Below we according to the above three characteristics, consider various situations, step by step to achieve a simple CommenJS introduction function
-
First we will introduce the modules used
A. read the file
B. Obtain the file path
C. Operating environment
D. Load the policy => For the JS/JSON /node file
e. Module
F. the cache
G. requier method
let fs = require('fs');
let path = require('path');
let vm = require('vm');
function Module(p) {
this.id = p; // Identifies the current module
this.exports = {}; // Each module has an exports property
this.loaded = false; // This module is not loaded by default
}
Module._extensions = {
// Js takes precedence over JSON and node
'.js': function (Module) {},
'.json': function (Module) {},
'.node': 'xxx'
}
Module._cacheModule = {}// Cache based on absolute path
function require(moduleId){// We pass the loading module as an argument
}
Copy the code
2. When require receives a path, we first need to parse the path, assuming that we give _resolveFileName(moduleId) to parse the path
// The method that parses absolute paths returns an absolute path
Module._resolveFileName = function (moduleId) {
let p = path.resolve(moduleId);
// No suffixes are added
if(! path.extname(moduleId)) {//extname is an internal path method
//keys converts an object into an array
let arr = Object.keys(Module._extensions);
If there is no last name, because there are only three cases, we can compare them one by one, it is in order here, if it is transmitted to A, we can identify A. s now
for (let i = 0; i < arr.length; i++) {
let file = p + arr[i];
try {
fs.accessSync(file);//accessSync synchronizes to determine whether the sorting exists
return file;
} catch (e) {
console.log(e)
}
}
} else {
return p;// If there is a drop, the file will be found directly}}function req(moduleId) {
let p = Module._resolveFileName(moduleId);// p is an absolute path
}
Copy the code
- Once we’ve parsed the absolute path, we need to look for the file to load, and we said if it’s a second load, we need to look in the cache, so we need to check whether there’s a cache or not
Module._extensions = {
// Js takes precedence over JSON and node
'.js': function (Module) {},// Function refers to the method that loads a file of this type
'.json': function (Module) {},
'.node': 'xxx'
}
function req(moduleId) {
let p = Module._resolveFileName(moduleId);// p is an absolute path
if (Module._cacheModule[p]) {
// The module exists. If there is one, it can be replenished later
}
// generates a module without caching
let module = new Module(p);
// Load the module
let content = module.load(p); // Load the module
Module._cacheModule[p] = module;
module.exports = content; // Finally export as module.export
return module.exports
}
Copy the code
So in this process, we generate a module, new a moudle, pass the path, remember the code above, in the new process, we add id to the module,exports,load, and then I load this module and I add it to the cache and the load method is called on the instance, We will write this method on the prototype Module
// After new there are these flags
function Module(p) {
this.id = p; // Identifies the current module
this.exports = {}; // Each module has an exports property
this.loaded = false; // This module is not loaded by default
}
Module.prototype.load = function (filepath) {
// Determine whether the loaded file is JSON, Node, or JS
let ext = path.extname(filepath);
// Add methods according to the file type
let content = Module._extensions[ext](this); // Successfully read the contents of the file
// This indicates that the current module has id exports loaded
returnThe content; }Copy the code
- If it is JSON, we parse it directly. If it is JS, we say that a JS is a module. That means that each JS read is equivalent to reading a closure file. Then export using moudul. Export
//exports,require,module
Module.warpper = ['(function(exports,require,module){'.'\n})'];
Module._extensions = {
// Js takes precedence over JSON and node
'.js': function (Module) {
let script = fs.readFileSync(module.id, 'utf8');
let fn = Module.warpper[0] + script + Module.warpper[1];
// We need to execute this file, but eval will look up in the current scope. We only want to execute this file after require, so we use hourglass to limit the environment here
vm.runInThisContext(fn).call(Module.exports, Module.exports, req, module)
return module.exports;
},
'.json': function (Module) {
return JSON.parse(fs.readFileSync(module.id, 'utf8')); // Read that file
},
'.node': 'xxx'
}
Copy the code