js使用require 和 import 引入依賴的區別?

語言: CN / TW / HK

最近在專案裡面經常會看到require和import這兩種引入依賴的方式,一直不太瞭解這兩種引入方式的區別,趁著有時間,打算捋一捋,整理一下。

首先,我們要知道require和import本質上都是為了JS模組化程式設計使用的一個語法,語法一般都遵循這一定的語法規範,require遵循的是AMD/CommonJS規範,而import是es6新引入的一個語法標準,如果要相容瀏覽器的話必須轉化成es5的語法。

有童鞋會問,AMD,CommonJS是個啥東東?下面我會簡單的講一下。

我們知道,任何語言都有它的語言規範,有了一個好的規範,才會有利於維護一個良好的語言生態。

AMD:即Asynchronous Module Definition,中文名是非同步模組定義,一個在瀏覽器端模組化開發的規範,它完整描述了模組的定義,依賴關係,引用關係以及載入機制。

CommonJS:和AMD類似,它與AMD的區別是它載入模組是同步的,也就是說,只有載入完成,才能執行後面的操作。

一, import和require的用法詳解:

(1)import和require區別

require是賦值過程,其實require的結果就是物件、數字、字串、函式等,再把require的結果賦值給某個變數

import是解構過程,但是目前所有的引擎都還沒有實現import,我們在node中使用babel支援ES6,也僅僅是將ES6轉碼為ES5再執行,import語法會被轉碼為require

(2) js使用環境搭建

注:import是ES6的語法,有些地方不支援,可能報以下的錯誤

解決方法如下:

用babel來將import轉換為es5語法。

安裝:

npm install --save-dev babel-preset-env babel-cli
複製程式碼

新建.babelrc檔案

{    
"presets": ["env"] 
}
複製程式碼

執行

babel-node xxx.js
複製程式碼

嗯~以為完美解決了,運行了之後又報了錯

查了一波資料,說是我的babel沒有全域性引用的原因,不太想直接全域性引用,所以用package-json配置進來,配置如下:


{
  "name": "demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "runImport": "babel-node import.js",
    "runR": "babel-node require.js"
  },
  "author": "tanla"
}
複製程式碼
(3) import使用示例

import.js

import {a,c} from './a'; 
import * as lib from './a';
console.log(a());
console.log(lib.a());
console.log(c);
console.log(lib.c);

複製程式碼

a.js

export function a(){
    let a = 10;
    let b = 20;
    return a + b;
}
export let c = 40
複製程式碼

執行結果

30
30
40
40
複製程式碼

可以看到import引入有兩種方式:一種是按需引入,引入當前所需要的方法和變數,一直是全部引入,可以用as重新命名一下。其實import還可以為模組指定預設輸出(export default),使用方式如下:

import-default.js

export default function () {
    console.log('foo');
  }
複製程式碼

import.js

import defaultI from './import-default'
defaultI();
複製程式碼

輸出

foo
複製程式碼

好吧,講一下這兩個的區別和應用場景吧

import命令接受一對大括號,裡面指定要從其他模組匯入的變數名。大括號裡面的變數名,必須與被匯入模組(a.js)對外介面的名稱相同。可以看到我上面要引入export中的變數和方法時,都是要在大括號裡面指定一樣的變數名,否則就會報錯。

如果想為輸入的變數重新取一個名字,import命令要使用as關鍵字,將輸入的變數重新命名。

這樣的話export就要求我們清楚的知道引入的模組裡的屬性和方法,可是在實際開發過程中,我們未必有那麼多時間去閱讀文件來了解每個模組,所以我們需要export-default命令,我們可以看到我們引入的時候,並沒有指定引入模組的方法和變數,我們可以隨意命名,一個模組只能使用一次export-default命令,所以這個時候也不用加大括號。export default命令其實只是輸出一個叫做default的變數。接下來,我進行了一個這樣的操作

import.js

import defaultI from './import-default'
console.log(defaultI());
複製程式碼

輸出

foo
undefined
複製程式碼

當我試著把這個dedault物件打印出來的時候,我發現輸出undefined,很疑惑,這一點沒有很明白。猜測估計是和這個物件有關。

(4) require引入

require.js

const a = require('./b');
console.log(a.c(2,4));
複製程式碼

b.js

let f = function a(a,b){
    return a+b;
}
let e = function ee(a,b){
    return a-b;
}
exports.c = e;
module.exports.c = f;
複製程式碼

輸出

6
複製程式碼

好吧,又看到了兩個不一樣匯出模版的方式module.exports和exports

我們知道module和exports是Node.js給每個js檔案內建的兩個物件,可以打印出來看到,一開始,他們都是{},並且他們指向的是同一塊記憶體,所以不去改變他們指向的記憶體地址的話,其實他們是等價的。所以我們可以試一下,將上面b.js中的7行和8行替換一下位置,下面輸出的是-2。require引入的物件本質上是module.exports。但是因為export和他指向的是同一塊記憶體,所以當export將其裡面的值修改了之後,require獲取到的值也會發生變化。接下來,可以看一下下面這波操作

c.js

module.exports = {name: 'uuuu'};
exports = {name: 'yyy'};
複製程式碼

require.js

const c = require('./c');
console.log(c);
複製程式碼

輸出

{ name: 'uuuu' }
複製程式碼

這時,module.exports指向的是一塊新的記憶體{name: 'uuuu'},而exports也指向一個新的記憶體{name: 'yyy'},require獲取的是module.exports的值,所以,module.exports和exports的賦值順序就不會影響到它的引入了。