require和import的區(qū)別

當前端應用越來越復雜時,我們想要將代碼分割成不同的模塊,便于復用、按需加載等。

require 和 import 分別是不同模塊化規(guī)范下引入模塊的語句,下文將介紹這兩種方式的不同之處。

1. 出現(xiàn)的時間、地點不同

年份 出處
require/exports 2009 CommonJS
import/export 2015 ECMAScript2015(ES6)

2. 不同端(客戶端/服務器)的使用限制

require/exports import/export
Node.js 所有版本 Node 9.0+(啟動需加上 flag --experimental-modules)
Node 13.2+(直接啟動) 如何使用?
Chrome 不支持 61+
Firefox 不支持 60+
Safari 不支持 10.1+
Edge 不支持 16+

CommonJS 模塊化方案 require/exports 是為服務器端開發(fā)設計的。服務器模塊系統(tǒng)同步讀取模塊文件內容,編譯執(zhí)行后得到模塊接口。(Node.jsCommonJS 規(guī)范的實現(xiàn))。

在瀏覽器端,因為其異步加載腳本文件的特性,CommonJS 規(guī)范無法正常加載。所以出現(xiàn)了 RequireJS、SeaJS 等(兼容 CommonJS )為瀏覽器設計的模塊化方案。

兩種方案各有各的限制,需要注意以下幾點:

  • 原生瀏覽器不支持 require/imports,可使用支持 CommonJS 模塊規(guī)范的 Browsersifywebpack 等打包工具,它們會將 require/imports 轉換成能在瀏覽器使用的代碼。
  • import/export 在瀏覽器中無法直接使用,我們需要在引入模塊的 <script\> 元素上添加type="module屬性。
  • 即使 Node.js 13.2+ 已經(jīng)支持 import/export,官方不建議在正式環(huán)境使用,目前可以使用 babelES6 的模塊系統(tǒng)編譯成 CommonJS 規(guī)范(注意:語法一樣,但具體實現(xiàn)還是require/exports)。

3. require/exports 是運行時動態(tài)加載,import/export 是靜態(tài)編譯

CommonJS 加載的是一個對象(即 module.exports 屬性),該對象只有在腳本運行完才會生成。而 ES6 模塊不是對象,它的對外接口只是一種靜態(tài)定義,在代碼靜態(tài)解析階段就會生成。- 阮一峰

4. require/exports 輸出的是一個值的拷貝,import/export 模塊輸出的是值的引用

require/exports 輸出的是值的拷貝。也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值。

import/export 模塊輸出的是值的引用。JS 引擎對腳本靜態(tài)分析的時候,遇到模塊加載命令import,就會生成一個只讀引用。等到腳本真正執(zhí)行時,再根據(jù)這個只讀引用,到被加載的那個模塊里面去取值。

若文件引用的模塊值改變,require 引入的模塊值不會改變,而 import 引入的模塊值會改變。

5. 用法不一致

(1). require/exports 的用法

const fs = require('fs')
exports.fs = fs
module.exports = fs

exports 是對 module.exports 的引用,相當于

exports = module.exports = {};

在不改變 exports 指向的情況下,使用 exportsmodule.exports 沒有區(qū)別;如果將 exports 指向了其他對象,exports 改變不會改變模塊輸出值。示例如下:

//utils.js
let a = 100;

exports.a = 200;
console.log(module.exports) //{a : 200}
exports = {a:300}; //exports 指向其他內存區(qū)

//test.js

var a = require('./utils');
console.log(a) // 打印為 {a : 200} 

(2). import/export 的寫法

import fs from 'fs'

import {readFile} from 'fs' //從 fs 導入 readFile 模塊
import {default as fs} from 'fs' //從 fs 中導入使用 export default 導出的模塊
import * as fileSystem from 'fs' //從 fs 所有導出模塊,引用對象名為 fileSystem
import {readFile as read} from 'fs' //從 fs 導入 readFile 模塊,引用對象名為 read
import('/modules/my-module.js') //動態(tài)導入
  .then((module) => {
    // Do something with the module.
});

export default fs
export const fs
export function readFile
export {readFile, read}
export * from 'fs'

建議1:建議明確列出我們要引用的內容。,使用 * 雖然很方便,但是不利于現(xiàn)代的構建工具檢測未被使用的函數(shù),影響代碼優(yōu)化。

建議2: 請不要濫用動態(tài)導入 import()(只有在必要情況下采用)。靜態(tài)框架能更好的初始化依賴,而且更有利于靜態(tài)分析工具和 tree shaking 發(fā)揮作用

同時需要注意

  1. 引入 export default 導出的模塊不用加 {},引入非 export default 導出的模塊需要加 {}。
import fileSystem, {readFile} from 'fs' 
  1. 一個文件只能導出一個 default 模塊。

  2. 在驗證代碼的時候遇到如下報錯

access to script from origin 'null' has been blocked by CORS policy

前面提到過,瀏覽器引入模塊的 <script> 元素要添加 type="module 屬性,但 module 不支持 file:// 文件協(xié)議,只支持 HTTP 協(xié)議,所以本地需要使用 http-server 等本地網(wǎng)絡服務器打開網(wǎng)頁文件。

(3). import/export 不能對引入模塊重新賦值/定義

當我嘗試給 import 的模塊重新賦值時

  import {e1} from './webUtils.js';
  e1=234;

瀏覽器顯示

Uncaught TypeError: Assignment to constant variable.

當我重新定義引用的模塊

 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) 

程序報錯

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())

程序報錯

Uncaught SyntaxError: Unexpected token '{'
前面提到過 import/export 在代碼靜態(tài)解析階段就會生成,不會去分析代碼塊里面的 import/export,所以程序報語法錯誤,而不是運行時錯誤。

這里需要注意,動態(tài)導入 import() 任何地方使用,因為他是運行時加載

6. 是否采用嚴格模式

嚴格模式是采用具有限制性JavaScript變體的一種方式

import/export 導出的模塊默認調用嚴格模式。

var fun=()=>{
    mistypedVaraible = 17; //報錯,mistypedVaraible is not defined
};
export default fun;

require/exports 默認不使用嚴格模式,可以自定義是否使用嚴格模式。
例如

exports.fun = ()=>{
 mistypedVaraible = 17; //沒有調用嚴格模式,不會報錯
}; 

7. 參考資料

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容