1. 出現(xiàn)的時(shí)間贺纲、地點(diǎn)不同
年份 | 出處 | |
---|---|---|
require/exports | 2009 | CommonJS |
import/export | 2015 | ECMAScript2015(ES6) |
2. 不同端(客戶端/服務(wù)器)的使用限制
require/exports | import/export | |
---|---|---|
Node.js | 所有版本 | Node 9.0+(啟動(dòng)需加上 flag --experimental-modules)Node 13.2+(直接啟動(dòng)) |
Chrome | 不支持 | 61+ |
Firefox | 不支持 | 60+ |
Safari | 不支持 | 10.1+ |
Edge | 不支持 | 16+ |
CommonJS 模塊化方案 require/exports 是為服務(wù)器端開發(fā)設(shè)計(jì)的。服務(wù)器模塊系統(tǒng)同步讀取模塊文件內(nèi)容,編譯執(zhí)行后得到模塊接口啤贩。(Node.js 是 CommonJS 規(guī)范的實(shí)現(xiàn))。
在瀏覽器端,因?yàn)槠?strong>異步加載腳本文件的特性,CommonJS 規(guī)范無法正常加載焰坪。所以出現(xiàn)了 RequireJS、SeaJS 等(兼容 CommonJS )為瀏覽器設(shè)計(jì)的模塊化方案聘惦。直到 ES6 規(guī)范出現(xiàn)某饰,瀏覽器才擁有了自己的模塊化方案 import/export。
兩種方案各有各的限制善绎,需要注意以下幾點(diǎn):
- 原生瀏覽器不支持 require/exports黔漂,可使用支持 CommonJS 模塊規(guī)范的 Browsersify、webpack 等打包工具禀酱,它們會(huì)將 require/exports 轉(zhuǎn)換成能在瀏覽器使用的代碼炬守。
- import/export 在瀏覽器中無法直接使用,我們需要在引入模塊的 <script> 元素上添加type="module" 屬性剂跟。
- 即使 Node.js 13.2+ 可以通過修改文件后綴為 .mjs 來支持 ES6 模塊 import/export减途,但是Node.js 官方不建議在正式環(huán)境使用。目前可以使用 babel 將 ES6 的模塊系統(tǒng)編譯成 CommonJS 規(guī)范(注意:語法一樣曹洽,但具體實(shí)現(xiàn)還是 require/exports)鳍置。
3. require/exports 是運(yùn)行時(shí)動(dòng)態(tài)加載,import/export 是靜態(tài)編譯
CommonJS 加載的是一個(gè)對(duì)象(即 module.exports 屬性)送淆,該對(duì)象只有在腳本運(yùn)行完才會(huì)生成税产。而 ES6 模塊不是對(duì)象,它的對(duì)外接口只是一種靜態(tài)定義,在代碼靜態(tài)解析階段就會(huì)生成辟拷。- 阮一峰
4. require/exports 輸出的是一個(gè)值的拷貝撞羽,import/export 模塊輸出的是值的引用
require/exports 輸出的是值的拷貝。也就是說衫冻,一旦輸出一個(gè)值诀紊,模塊內(nèi)部的變化就影響不到這個(gè)值。
import/export 模塊輸出的是值的引用隅俘。JS 引擎對(duì)腳本靜態(tài)分析的時(shí)候渡紫,遇到模塊加載命令import,就會(huì)生成一個(gè)只讀引用考赛。等到腳本真正執(zhí)行時(shí),再根據(jù)這個(gè)只讀引用莉测,到被加載的那個(gè)模塊里面去取值颜骤。
若文件引用的模塊值改變,require 引入的模塊值不會(huì)改變捣卤,而 import 引入的模塊值會(huì)改變忍抽。
5. 用法不一致
(1). require/exports 的用法
const fs = require('fs')
exports.fs = fs
module.exports = fs
exports 是對(duì) module.exports 的引用,相當(dāng)于
exports = module.exports = {};
在不改變 exports 指向的情況下董朝,使用 exports 和 module.exports 沒有區(qū)別鸠项;如果將 exports 指向了其他對(duì)象,exports 改變不會(huì)改變模塊輸出值子姜。示例如下:
//utils.js
let a = 100;
exports.a = 200;
console.log(module.exports) //{a : 200}
exports = {a:300}; //exports 指向其他內(nèi)存區(qū)
//test.js
var a = require('./utils');
console.log(a) // 打印為 {a : 200}
(2). import/export 的寫法
import fs from 'fs'
import {readFile} from 'fs' //從 fs 導(dǎo)入 readFile 模塊
import {default as fs} from 'fs' //從 fs 中導(dǎo)入使用 export default 導(dǎo)出的模塊
import * as fileSystem from 'fs' //從 fs 導(dǎo)入所有模塊祟绊,引用對(duì)象名為 fileSystem
import {readFile as read} from 'fs' //從 fs 導(dǎo)入 readFile 模塊,引用對(duì)象名為 read
export default fs
export const fs
export function readFile
export {readFile, read}
export * from 'fs'
建議1:建議明確列出我們要引用的內(nèi)容哥捕。牧抽,使用 * 雖然很方便,但是不利于現(xiàn)代的構(gòu)建工具檢測(cè)未被使用的函數(shù)遥赚,影響代碼優(yōu)化扬舒。
同時(shí)需要注意
- 引入 export default 導(dǎo)出的模塊不用加 {},引入非 export default 導(dǎo)出的模塊需要加 {}。
import fileSystem, {readFile} from 'fs'
2. 一個(gè)文件只能導(dǎo)出一個(gè) default 模塊凫佛。
3. 在驗(yàn)證代碼的時(shí)候遇到如下報(bào)錯(cuò)
access to script from origin 'null' has been blocked by CORS policy
前面提到過讲坎,瀏覽器引入模塊的 <script>
元素要添加 type="module
屬性,但 module 不支持 FTP 文件協(xié)議(file://)愧薛,只支持 HTTP 協(xié)議晨炕,所以本地需要使用 http-server 等本地網(wǎng)絡(luò)服務(wù)器打開網(wǎng)頁(yè)文件。
(3). import/export 不能對(duì)引入模塊重新賦值/定義
當(dāng)我嘗試給 import 的模塊重新賦值時(shí)
import {e1} from './webUtils.js';
e1=234;
瀏覽器顯示
Uncaught TypeError: Assignment to constant variable.
當(dāng)我重新定義引用的模塊
import {e1} from './webUtils.js';
var e1=1;
瀏覽器顯示
(index):17 Uncaught SyntaxError: Identifier 'e1' has already been declared
(4). ES6 模塊可以在 import 引用語句前使用模塊毫炉,CommonJS 則需要先引用后使用
ES6 模塊
//webUtils.js
export var e='export';
console.log(e) //export
import {e} from './webUtils.js';
console.log(e) //export
CommonJS
//utils.js
exports.e = 'export';
console.log(a)
a = require('./utils');
console.log(a)
程序報(bào)錯(cuò)
ReferenceError: a is not defined
(5)import/export 只能在模塊頂層使用府瞄,不能在函數(shù)、判斷語句等代碼塊之中引用;require/exports 可以遵馆。
import fs from './webUtils.js';
function a(){
import {e1} from './webUtils.js';
console.log(e1)
}
a();
console.log(fs())
程序報(bào)錯(cuò)
Uncaught SyntaxError: Unexpected token '{'
前面提到過 import/export 在代碼靜態(tài)解析階段就會(huì)生成鲸郊,不會(huì)去分析代碼塊里面的 import/export,所以程序報(bào)語法錯(cuò)誤货邓,而不是運(yùn)行時(shí)錯(cuò)誤秆撮。
6. 是否采用嚴(yán)格模式
嚴(yán)格模式是采用具有限制性JavaScript變體的一種方式
import/export 導(dǎo)出的模塊默認(rèn)調(diào)用嚴(yán)格模式。
var fun=()=>{
mistypedVaraible = 17; //報(bào)錯(cuò)换况,mistypedVaraible is not defined
};
export default fun;
require/exports 默認(rèn)不使用嚴(yán)格模式职辨,可以自定義是否使用嚴(yán)格模式。 例如
exports.fun = ()=>{
mistypedVaraible = 17; //沒有調(diào)用嚴(yán)格模式戈二,不會(huì)報(bào)錯(cuò)
};
7. 其他模塊化方法
動(dòng)態(tài)導(dǎo)入 import()
import(modulePath) 表達(dá)式加載模塊并返回一個(gè) promise舒裤,該 promise resolve 為一個(gè)包含其所有導(dǎo)出的模塊對(duì)象。
我們可以在代碼中的任意位置動(dòng)態(tài)地使用它觉吭。例如:
import('/modules/my-module.js') //動(dòng)態(tài)導(dǎo)入
.then((module) => {
// Do something with the module.
});
建議: 請(qǐng)不要濫用動(dòng)態(tài)導(dǎo)入 import()(只有在必要情況下采用)腾供。靜態(tài)框架能更好的初始化依賴,而且更有利于靜態(tài)分析工具和 tree shaking 發(fā)揮作用