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 passrequireMethod ‘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

  1. Can be run directly in the browser without transcoding
  2. Multiple dependencies can be loaded
  3. 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

  1. 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
  1. 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
  1. 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