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'
}
    
複製代碼
相關文章
相關標籤/搜索