Webpack 知識補充之模塊

CommonJs

這塊標準是在 2009 年提出來的,包含模塊、IO、文件等。通過 Node.js 採用並作調整,因此提及 CommonJS 一般是 Node.js 中的版本了。在使用 Node.js 包管理器的年代,CommonJs 成爲一顆有流量的明星了。webpack

自身做用域

CommonJs 的模塊自然有自身的做用域,全部變量和函數聲明只能本身訪問,外人想都別想,這個保護機制太 nice 了。git

// order.js
const name = '訂單系統';
複製代碼
// index.js
const name = '首頁';
require('./order.js');
console.log(`------\n${name}`);
複製代碼

導出機制

模塊對外暴露的方式。對於要暴露的內容可以使用 module.exports 來導出,其導出內容格式是一個對象。也可以使用簡化形式 exportses6

// module.exports.js
module.exports = {
  name: '訂單系統',
  total: (a, b) => {
    return (a * b);
  }
}
複製代碼
// exports.js
exports.name = '訂單系統';
exports.total = (a, b) => {
  return (a * b);
}
複製代碼

上面兩種所要表達的功能是同樣的,內在邏輯是 exports 指向 module.exportsmodule.exports 是初始化時建的一個空對象。因此千萬不要直接給 exports 賦值,還有 module.exportsexports 不要並存。上面第二個文件 exports.js 可這麼來理解:github

// 初始化(便於理解 exports 與 module.exports 的關係)
const module = {
  exports: {}
};
const exports = module.exports;

// exports.js
exports.name = '訂單系統';
exports.total = (a, b) => {
  return (a, b);
}
複製代碼

導入機制

經過 require 導入。web

// 04/src/order.js
console.log('模塊文件 order.js');

exports.name = 'order name';

module.exports = {
  name: '訂單系統',
  total: (a, b) => {
    return a * b;
  }
};

exports.title = 'order title';
複製代碼
// 04/src/index.js
const title = require('./order.js').title;
const name = require('./order.js').name;

console.log(`exports.name 能展現麼?-------\n${name}`);

console.log(`exports.title 能展現麼?-------\n${title}`);

const total = require('./order.js').total;
setTimeout(function() {
  console.log(`module.exports 能展現麼?${total(10, 10)}`);
}, 3000);

console.log('動態加載');
const modulesName = ['product.js', 'shopcar.js'];
modulesName.forEach(name => {
  require(`./${name}`).name();
});
複製代碼

1.緩存加載,第二次導入文件時,無需加載,由於第一次導入已經加載過了,第二次直接使用上次導入的結果; 發現沒有?這是 order.js 文件 這個通知在控制檯裏面只打印了一次,而文件 order.js 實打實的引入了兩次。其原理是:咱們已經知道導出文件有 module 這個對象,咱們可能不知道的是這個對象有 loaded 這麼個屬性(記錄模塊是否被夾在過),其默認值是 false,即沒有加載過。當該模塊第一次被加載後,loaded 值會變爲 true,因此第二次引入該模塊就不會加載該模塊了。緩存

2.加載模塊支持動態加載;app

3.exportsmodule.exports 不要混合使用,不然 exports 會失效哦;函數

完整代碼可查看目錄 04 =>O(∩_∩)O~工具

ES6 Module

ES6 Module 一樣是將每一個文件做爲一個模塊,模塊自身有其做用域。所不一樣的是,引入了關鍵字 import(導入)和 exports (導出),例子仍是前面的例子,語法糖發生了變化。post

導出機制

1.默認導出,上面例子咱們都已接觸過了。不過每次只能導出一個,可直接導出對象、字符串、函數等。

// 導出字符串
export default '訂單系統';
複製代碼
// 導出對象,05/src/order.js
console.log('模塊文件 order.js');

export default {
  name: '訂單系統',
  total: (a, b) => {
    return a * b;
  }
};
複製代碼

2.命名導出,可以使用關鍵字 as 對導出變量重命名。

// 方式一,05/src/order1.js
export const name = '訂單系統1';

export const total = (a, b) => {
  return a * b;
};
複製代碼
// 方式二,,05/src/order2.js
const name = '訂單系統2';

const total = (a, b) => {
  return a * b;
};

export { name, total as getTotal };
複製代碼

導入機制

使用關鍵字 import 導入,也可以使用關鍵字 as 對導入變量重命名,也可以使用 as 將導入變量的屬性添加到後面對象(order1)中。

// 方式一
import { name, total as getProduct } from './order1';
複製代碼
// 方式二
import * as order2 from './order2';
複製代碼

示例

// 05/src/index.js
import order from './order';
import { total as getProduct } from './order1';
import * as order2 from './order2';

console.log(`order.name: ${order.name}`);
console.log(`order1 getProduct: ${getProduct(10, 10)}`);
console.log(`order2 getTotal: ${order2.getTotal(10, 10)}`);
複製代碼

完整代碼可查看目錄 05 =>O(∩_∩)O~

CommonJS 與 ES6 Module

二者本質區別在於:CommonJS 對模塊依賴是「動態」的,ES6 Module 是「靜態」的

1.動態,模塊依賴關係是在代碼運行階段

  • require 路徑可動態指定;
  • 支持表達式動判斷加載某個模塊;

2.靜態,模塊依賴關係是在代碼編譯階段

  • 導入、導出語句是聲明式的;
  • 路徑不支持表達式;
  • 導入和導出語句必須位於模塊的頂層做用域(不能放在 if 語句中);

ES6 Module 優點

咋一看,CommonJS 完美 KO ES6 Module 的方式。可事實並不是如此,ES6 Module 這種「靜態」方式有優點:

  • 殭屍代碼檢測和排除,減少資源打包體積。即用靜態分析工具檢測模塊或接口中哪些沒有被調用過(好比某個組件只用了部分功能,但有可能全部代碼都被加載進來了),這些加載進來未被調用的代碼就是殭屍代碼。靜態分析能夠在打包時將這些殭屍代碼去掉,減少資源打包體積;
  • 編譯器優化,動態模塊的導入是一個對象,而 ES6 Module 可直接導入變量,減小引用層級,提升程序效率;

值拷貝和動態映射

場景:導入一個模塊時,不一樣模塊模式是不同的。

  • CommonJS 是值拷貝,可編輯;
  • ES6 Module 是址引用,即映射,只讀,即不可編輯;
// 06/src/commonJs.js
let csCount = 0;

module.exports = {
  csCount,
  csCountAdd: () => {
    csCount += 10;
  }
};
複製代碼
// 06/src/es6-module.js
let esCount = 0;
const esCountAdd = () => {
  esCount += 10;
};

export { esCount, esCountAdd };
複製代碼
// 06/src/index.js
// CommonJS Module
let csCount = require('./commonJs').csCount;
let csCountAdd = require('./commonJs').csCountAdd;

console.log(`----commonjs 初次加載----\n${csCount}`);
csCountAdd();
console.log(`----commonjs 內部自加 10 -----\n${csCount}`);
csCount += 20;
console.log(`----commonjs 啓動項自加 20------\n${csCount}`);

// Es6 Module
import { esCount, esCountAdd } from './es6-module.js';
console.log(`----es6 初次加載----\n${esCount}`);
esCountAdd();
console.log(`----es6 內部自加 10 -----\n${esCount}`);
esCount += 20;
console.log(`----es6 啓動項自加 20------\n${esCount}`);
複製代碼

經過例子及上圖運行結果,可剖析

  • CommonJs 是一份值的拷貝,雖然調用了 csCount(),可是並無形成在文件 index.js 中副本(csCount)的影響;而副本(csCount)在文件 index.js 中可更改;
  • ES6 是一份值的動態映射,調用了 esCount(),文件 index.js 中副本(esCount)的也隨之變化;而副本(esCount)在文件 index.js 中是不可更改,便是隻讀的;

完整代碼可查看目錄 06 =>O(∩_∩)O~

循環依賴

一般工程中是應該儘可能避免這種噁心的循環依賴的產生,由於會帶來複雜度。可儘管咱們知道這是很差的,也理性地避免發生。但鏈條長了,業務多了,人員多了,仍是不知不覺中「造孽般地」寫出這樣的代碼。

場景: A 依賴 B,B 依賴 C,C 依賴 D,D 依賴 A。其實 A 與 B 就互相依賴了。

CommonJs Module

// 07/src/a.js
const fileB = require('./b.js');
console.log(`----- a.js 文件展現 b.js 內容 ------\n`, fileB);

module.exports = '這是 a.js 文件';
複製代碼
// 07/src/b.js
const fileA = require('./a.js');
console.log(`----- b.js 文件展現 a.js 內容 ------\n`, fileA);

module.exports = '這是 b.js 文件';
複製代碼
// 07/src/index.js
require('./a.js');
複製代碼

咱們腦海中自運行結果是

----- b.js 文件展現 a.js 內容 ------
 這是 a.js 文件
----- a.js 文件展現 b.js 內容 ------
 這是 b.js 文件
複製代碼

可控制檯是

反覆檢查了代碼,沒錯啊,可本該顯示 這是 a.js 文件,爲什麼展現 {}?不行,我仍是要好好捋一捋

  • 文件 index.js 導入文件 a.js,開始執行文件 a.js,第一行導入文件 b.js,此時進入文件 b.js 內部;
  • 文件 b.js 第一行又導入文件 a.js,循環依賴由此產生。這時執行權並無交回給文件 a.js,而是直接取導出值,此刻文件 a.js 還未執行技結束,導出值就默認爲空對象,所以文件 b.js 執行打印語句時,顯示 ----- b.js 文件展現 a.js 內容 ------ {};
  • 文件 b.js 執行結束了,執行權接着交回給文件 a.js, 文件 a.js 繼續執行第二行,而後在控制檯打印 ----- a.js 文件展現 b.js 內容 ------ 這是 b.js 文件。至此,這個流程結束;

完整代碼可查看目錄 07 =>O(∩_∩)O~

ES6 Module

// 08/src/a.js
import fileB from './b.js';
console.log(`----- a.js 文件展現 b.js 內容 ------\n`, fileB);

export default '這是 a.js 文件';
複製代碼
// 08/src/b.js
import fileA from './a.js';
console.log(`----- b.js 文件展現 a.js 內容 ------\n`, fileA);

export default '這是 b.js 文件';
複製代碼
// 07/src/index.js
import a from './a.js';
複製代碼

咱們腦海中自運行結果天然也是

----- b.js 文件展現 a.js 內容 ------
 這是 a.js 文件
----- a.js 文件展現 b.js 內容 ------
 這是 b.js 文件
複製代碼

可控制檯是

完整代碼可查看目錄 08 =>O(∩_∩)O~

我暈,仍是不對,此次不是空對象了,倒是 undefined。那循環依賴該如何解決呢?

---> 等我下篇 <---

上一篇:webpack 簡介

相關文章
相關標籤/搜索