前言
說到import和require,大家平時開發中一定不少見,尤其是需要前端工程化的項目現在都已經離不開node了,在node環境下這兩者都是大量存在的,大體上來說他們都是為了實現JS代碼的模塊化,那為什么會出現兩種方案呢,又有什么不同呢?
模塊化的不同解決方案
追根溯源,JS這門腳本語言設計伊始就是沒有模塊化的,所以早期的全局變量容易造成命名沖突。但隨着web項目越來越大,JS的代碼量也與日俱增,於是社區就自發約定了幾種模塊化的方案:requirejs遵循AMD,seajs遵循CMD,node的module遵循CommonJS規范,雖然寫法上有所不同,都是為了能夠間接實現模塊化的基礎上保持較為一致的代碼風格。
隨着ES2015的發布,官方標准定義了一種模塊化的方案,那就是import、export。可是,標准畢竟是標准,各大瀏覽器和node終端要實現標准還是有一段距離的,目前來說都2018年了主流瀏覽器都還沒實現,還得依賴轉換工具(例如babel)轉為ES5的代碼之后瀏覽器才能解析。所以這也就解釋了為什么我們的工程化代碼中nodeJS遵循的CommonJS規范和ES6的模塊化方案並存的現象。
兩者的區別
- import是ES6標准中的模塊化解決方案,require是node中遵循CommonJS規范的模塊化解決方案
- 后者支持動態引入,也就是require(${path}/xx.js),前者目前不支持,但是已有提案
- 前者是關鍵詞,后者不是
- 前者是編譯時加載,必須放在模塊頂部,在性能上會比后者好一些,后者是運行時加載,理論上來說放在哪里都可以
- 前者采用的是實時綁定方式,即導入和導出的值都指向同一個內存地址,所以導入值會隨着導出值變化。而后者在導出時是指拷貝,就算導出的值變化了,導入的值也不會變化,如果想要更新值就要重新導入
- 前者會編譯成require/exports來執行
用法上的區別
import導入模塊
- 導入模塊根據模塊導出時的寫法有不同寫法,具體可以參考這里,如果模塊是普通導出:
// test.js var firstName = 'Michael'; var lastName = 'Jackson'; var year = 1958; export {firstName, lastName, year}; // demo.js import { firstName } from './test.js' console.log(firstName); // 'Michael' import * as test from './test.js' console.log(test); //Module{} 所有內容 import { firstName as key, lastName as value } from './test.js' console.log(key + '--' + value); // Michael--Jackson - 帶有默認值時,也是我們日常開發中常用的方式:
實際上這個default就是一個語法糖,只不過defaul是一個關鍵字,在這里等同於// test.js var firstName = 'Michael'; var lastName = 'Jackson'; var year = 1958; export default { firstName, lastName, year }; // demo.js import test from './test.js' console.log(test); // {firstName: "Michael", lastName: "Jackson", year: 1958}import { default as test } from './test.js' - 特別注意的是,export命令規定的是對外的接口,必須與模塊內部的變量建立一一對應關系。可以理解為export導出的是一種引用關系而不是一個具體的值,它們的實質是,在接口名與模塊內部變量之間,建立了一一對應的關系,例如下面這樣就會報錯:
但是改為用花括號包起來變成對象之后就成了輸出引用關系就可以正常導出了:export 1; var m = 1; export m;var m = 1; export { m }
require導入模塊
- require導入模塊就沒那么復雜了,導出時是什么樣,導入時就還是什么樣:
// test.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
module.exports = { firstName, lastName, year };
// demo.js
const test = require('./test.js');
console.log(test); // {firstName: "Michael", lastName: "Jackson", year: 1958}
- 還有一種不常用的用法,直接導出:
// test.js
var year = 1958;
exports.year = year;
// demo.js
const test = require('./test.js');
console.log(test); // {year: 1958}
- 需要注意的是,module.exports導出之后,后面的代碼就對導出的模塊無效了,例如上例中
// test.js
module.exports = { firstName, lastName, year };
exports.name = '222';
// demo.js
const test = require('./test.js');
console.log(test); // {firstName: "Michael", lastName: "Jackson", year: 1958}
- 特別說明一下,由於require是可以在任意地方引入的,所以,我們在開發中用~引入圖片的方式實際上等效於require:
<img src="~assets/img/icon/red_logo.png" class="logo" alt="">
//等效於
<img :src="require('assets/img/icon/red_logo.png')" class="logo" alt="">
總結
import和require就是兩種不同的JS模塊化實現方式而已,由於之前npm生態的很多包都是基礎CommonJS規范寫的,所以相當一段時間之內必然是import和require這兩種模塊引入方式共存的。
總體來說時代總是發展的,ES6作為語言規范是遲早會被各大主流瀏覽器支持的,不然也就稱不上主流瀏覽器了。所以為了長遠考慮,我們還是盡量使用ES6的import來引入模塊,等以后瀏覽器支持了我們也就可以少改一些代碼了。
