有關於webpack模塊化打包工具javascript
流程基本是定義模塊,導出模塊 ,使用模塊,java
好比同時引進jquery和zepto ,這二者同時引用了 '$' ,此時就會產生命名衝突,這樣會形成咱們沒有辦法管理依賴,也沒有辦法控制依賴加載的順序,那麼咱們有如下幾種解決方案node
require
方法‘同步’加載依賴其餘模塊,經過moudle.exports 導出須要暴露的接口 =>CommonJS 是AMD最具表明性的實現, node 內部實現了Common.jsa.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優勢json
經過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)
})
複製代碼
ConmonJS規範
下面咱們根據以上三個特色,考慮各類狀況,一步步實現一個簡單的CommenJS引入功能
首先咱們要引入所用到的模塊
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是一個絕對路徑
}
複製代碼
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;
}
複製代碼
//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'
}
複製代碼