最近在專案裡面經常會看到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的賦值順序就不會影響到它的引入了。