js模块化

有关于webpack模块化打包工具javascript

  • seajs
  • requirejs
  • Conmon.js
  • esmoudle=>浏览器
  • umd=> 兼容处理

流程基本是定义模块,导出模块 ,使用模块,java

咱们首先要了解一下模块化

当咱们项目特别大的时候,咱们会将js代码模块化,

好比同时引进jquery和zepto ,这二者同时引用了 '$' ,此时就会产生命名冲突,这样会形成咱们没有办法管理依赖,也没有办法控制依赖加载的顺序,那么咱们有如下几种解决方案node

  • CMD (这是一种普遍使用的js规范 他的核心思想是经过require方法‘同步’加载依赖其余模块,经过moudle.exports 导出须要暴露的接口 =>CommonJS 是AMD最具表明性的实现, node 内部实现了Common.js

a.js文件jquery

import.exports  = 'Hello world';
复制代码

b.js文件webpack

let str = require(./a.js);
复制代码

若是说require方法在node中是同步的,那么他是如何实现的呢?web

let fs = require('fs');//读取文件
function require(moduleName){  //moduleName路径
    let content = fs.readFileSync(moduleName,'utf8');
    //最后一个参数是函数的内容体
    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 } */
复制代码
  • 另外一种方式是AMD require.js AMD也是一种javascript模块化规范,与Common.js最大的不一样在于它采用的异步的方式,,,Common.js仅适用于node 此方法能够在浏览器中使用,,最具表明性的实现是

AMD优势json

  1. 可在不转码的状况下直接在浏览器中运行
  2. 可加载多个依赖
  3. 可在node 和 浏览器中运行

经过require声明模块数组

define('name',[],function(){
    
})
 define('age',[],function(){
    
})
require(['name','age'],function(name,age){
    console.log(name,age)
})

let factories = {};//将name和依赖绑定
function define(moudlName,dependencies,factory){//模块名字,依赖,工厂函数
    factories[moudlName] = factory;
}
let function require(mods,callback){
    let result = mods.map(function(mod){
        let factory = factory[mod];//取出来每一个相对应的函数
        let exports;
        exports = factory();
        return exports;//执行结果是一个新的数组
    })
    calback.apllay(null,result) 
}
复制代码

这样咱们就实现了简单的AMD的实现,那么还有更复杂的状况 假设咱们加依赖浏览器

经过require声明模块缓存

define('name',[],function(){
    
})
 define('age',['name'],function(name){
    return name + 9
})
require(['name','age'],function(name,age){
    console.log(name,age)
})
复制代码

##那么如今如何管理依赖呢?

let factories = {};//将name和依赖绑定
function define(moudlName,dependencies,factory){

    //模块名字,依赖,工厂函数
    factory.dependencies = dependencies;//将依赖系在函数上
    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];//取出来每一个相对应的函数
        let exports;
        exports = factory();
        return exports;//执行结果是一个新的数组
    })
    calback.apllay(null,result) 
}
require(['age'],function(name,age){
    console.log(name,age)
})
复制代码

咱们重点说CommonJS

Conmon.js 实现

ConmonJS规范

  • 定义了如何导入模块 require
  • 还定义了如何导出模块 module.exports 导出xxx
  • 还定义了一个js就是一个模块
  • 若是第一次加载完成,则会存到缓存里,第二次从缓存中读取

下面咱们根据以上三个特色,考虑各类状况,一步步实现一个简单的CommenJS引入功能

  1. 首先咱们要引入所用到的模块

    a. 读文件

    b. 获取文件路径

    c. 运行环境

    d. 加载策略=>针对js/json/node文件

    e. Module

    f. 缓存

    g. requier方法

let fs = require('fs');
let path = require('path');
let vm = require('vm');
function Module(p) {
    this.id = p; // 当前模块的标识
    this.exports = {}; // 每一个模块都有一个exports属性
    this.loaded = false; // 这个模块默认没有加载完
}
Module._extensions = {
    //js优先级高于json,和node 
    '.js': function (Module) {},
    '.json': function (Module) {},
    '.node': 'xxx'
}
Module._cacheModule = {}// 根据的是绝对路径进行缓存的 
function require(moduleId){//咱们将加载模块以参数形式传递过来
    
}
复制代码

以上是咱们读取模块必备的条件,下面咱们挨个增长内容, 2. 在require接收到路径的时候,咱们首先要对此路径作解析,假设咱们给个方法_resolveFileName(moduleId)对路径做出解析

// 解析绝对路径的方法 返回一个绝对路径
Module._resolveFileName = function (moduleId) {
  let p = path.resolve(moduleId);
  // 没有后缀在加上后缀 若是传过来的有后缀就不用加了
  if (!path.extname(moduleId)) {//extname是path内部方法,在这里用到的相似用法,清自行查阅,笔者就先很少作解释了
    //keys将一个对象转成数组
    let arr = Object.keys(Module._extensions);
    //若是没有后蕞名称,由于只有三种状况,咱们挨个对比,在这里是有前后顺序的,假设传过来a,咱们会现识别a.js
    for (let i = 0; i < arr.length; i++) {
      let file = p + arr[i];
      try {
        fs.accessSync(file);//accessSync 同步断定分拣是否存在
        return file;
      } catch (e) {
        console.log(e)
      }
    }
  } else {
    return p;//若是有后坠就直接查找此文件,无需匹配
  }
}
function req(moduleId) {
  let p = Module._resolveFileName(moduleId);// p是一个绝对路径
}
复制代码
  1. 当咱们解析完绝对路径以后,就须要去查找要加载的文件了,咱们以前说若是是第二次加载,就从缓存中查找,因此这里咱们首先须要判断有没有缓存
Module._extensions = {
    //js优先级高于json,和node 
    '.js': function (Module) {},//这里的function指的是加载该类型文件的方法
    '.json': function (Module) {},
    '.node': 'xxx'
}
function req(moduleId) {
  let p = Module._resolveFileName(moduleId);// p是一个绝对路径
  if (Module._cacheModule[p]) {
    // 模块存在,若是有直接把对象返回便可稍后补充
  }
  // 表示没有缓存就生成一个模块
  let module = new Module(p);
  // 加载模块
  let content = module.load(p); // 加载模块
  Module._cacheModule[p] = module;
  module.exports = content; //最终以module.export导出
  return module.exports
}
复制代码

在此过程当中 ,咱们生成一个模块,new了一个moudle, 将路径传过去,还记得上面的代码,在new的过程当中,咱们给模块加了id,exports,load,而后我加载此模块,而且将它添加到缓存中 load方法是在实例上调用的,咱们将吧这个方法写在Module的原型上

//在new以后就有了这些标识
function Module(p) {
    this.id = p; // 当前模块的标识
    this.exports = {}; // 每一个模块都有一个exports属性
    this.loaded = false; // 这个模块默认没有加载完
}
Module.prototype.load = function (filepath) {
    //判断加载的文件是json仍是node,仍是js
    let ext = path.extname(filepath);
    //根据文件类型添加方法
    let content = Module._extensions[ext](this); //成功读取文件内容
    //this指当前模块的实例 有id exports loaded
    return content;
}
复制代码
  1. 最后咱们来补充下加载文件的方法,这里只介绍json和js的 若是是json咱们直接parse,若是是js,咱们说一个js是一个模块,那说明每读取一个js至关于读取一个闭包文件,咱们会在js文件内容外包一个闭包,而后导出用moudul.export
//exports,require,module
Module.warpper = ['(function(exports,require,module){', '\n})'];
Module._extensions = {
    //js优先级高于json,和node 
    '.js': function (Module) {
        let script = fs.readFileSync(module.id, 'utf8');
        let fn = Module.warpper[0] + script + Module.warpper[1];
        //咱们须要执行此文件,可是eval会在当前做用域向上查找,咱们只想在require以后执行此文件,所以这里使用沙漏限制环境
        vm.runInThisContext(fn).call(Module.exports, Module.exports, req, module)
        return module.exports;
    },
    '.json': function (Module) {
        return JSON.parse(fs.readFileSync(module.id, 'utf8')); // 读取那个文件
    },
    '.node': 'xxx'
}
    
复制代码
相关文章
相关标签/搜索