Import與Require

1、基本語法

require用於讀取並執行js文件,並返回該模塊的exports對象。若無指定模塊,會報錯。javascript

Node使用CommonJS模塊規範,require屬於node的內置命令。CommonJS規範加載模塊是同步的,也就是說,只有加載完成,才能執行後面的操做。css

// example.js
var invisible = function () {
  console.log("invisible");
}
//在另外一個文件中引入example.js
var example = require('./example.js');
console.log(example);

import用於引入外部模塊、其餘腳本等的函數、對象或者基本類型。html

import屬於ES6的命令,ES6模塊的運行機制與CommonJS不同,它遇到模塊加載命令import時,不會去執行模塊,而是隻生成一個引用。等到真的須要用到時,再到模塊裏面去取值。java

import defaultMember from "module-name";
import * as name from "module-name";
import { member } from "module-name";
import { member as alias } from "module-name";
import { member1 , member2 } from "module-name";
import { member1 , member2 as alias2 , [...] } from "module-name";
import defaultMember, { member [ , [...] ] } from "module-name";
import defaultMember, * as name from "module-name";
import "module-name";


2、require的靜態編譯與import的動態編譯

靜態編譯

 

第一次加載某個模塊時,Node會緩存該模塊。之後再加載該模塊,就直接從緩存取出該模塊的module.exports屬性。例如:node

 

require('./example.js');
require('./example.js').message = "hello Lily";
var test = require('./example.js').message
console.log(test);// "hello"

能夠看到test的打印結果爲:hello。webpack

這是由於即便./example.js模塊引用了三次,可是第一次加載該模塊時,Node會緩存該模塊。之後兩次再加載該模塊,就直接從緩存取出該模塊的module.exports屬性。因此,require('./example.js').message = "hello";只是往已緩存的模塊添加一個屬性,當再次取引用require('./example.js')時會發現message屬性任然存在,說明example.js模塊沒有被從新加載,與上一次的引用使用的是同一緩存。es6

動態編譯

ES6模塊的動態編譯:遇到模塊加載命令import時,不會去執行模塊,而是隻生成一個引用。等到真的須要用到時,再到模塊裏面去取值。web

即,ES6模塊是動態引用,不存在緩存值的問題,並且模塊裏面的變量,綁定其所在的模塊。例如:緩存

// base.js
export var foo = 'bar';
setTimeout(() => foo = 'baz change', 500);
// import.js
import { foo } from './baseEs6';

console.log(foo); // bar
setTimeout(() => console.log(foo), 600);// baz changed

上面代碼中,base.js的變量foo,在剛加載時等於bar,過了500毫秒,又變爲等於‘baz changed’。依據動態加載的原理,m2.js正確地讀取到了這個變化。babel

使用babel編譯文件import.js

var _baseEs = require('./baseEs6');

console.log(_baseEs.foo);
setTimeout(function () {
  return console.log(_baseEs.foo);
}, 600);

咱們能夠清楚地看到遇到import文件./baseEs6的foo變量時,會先不會去執行模塊,而是隻生成一個引用_baseEs。等到真的須要用到變量foo時,再到模塊裏面去取值(_baseEs.foo)。因此在第一次打印foo時輸出的是foo的初始值。等到模塊中的setTimeout(() => foo = 'baz change', 500);執行完畢,foo的值變爲'baz change',此時import.js文件再打印foo就發現foo確實變爲'baz change'。

但這隻能保證不會出現引用的異常,仍是會可能出現模塊沒有徹底加載完就在另外一個文件中掉用這個模塊的狀況,此時會出現undefined。

// baseEs6.js
export var fish;

setTimeout(() => fish = 'fish', 500);


// es6.js
import { fish } from './baseEs6';

console.log(fish); //undefined

 

babel對於Import命令的轉碼

babel是一個普遍使用的轉碼器,能夠將ES6代碼轉爲ES5代碼,從而在現有環境執行。

在咱們的項目中,可使用的語言特性有環境區分,對 server 端運行的代碼,webpack將src/*下的es6代碼 轉換成 lib/* 下的符合commonJS規範的es5,而後node執行lib/* 的代碼。這裏,咱們主要來了解一下babel對於Import命令的轉碼。

代碼分析

// es6Test.js
import * as actions from './searchAccount.actions';
import kdbTheme from '../../../common/Dialog/panguTheme.css';
import { createCommonAccount } from './createDialog.actions';

console.log('createCommonAccount###', createCommonAccount);
// babel編譯es6Test.js
/* import * as actions from './searchAccount.actions' */
var _searchAccount = require('./searchAccount.actions'); 

var actions = _interopRequireWildcard(_searchAccount);

/* import kdbTheme from '../../../common/Dialog/panguTheme.css' */
var _panguTheme = require('../../../common/Dialog/panguTheme.css');

var _panguTheme2 = _interopRequireDefault(_panguTheme);

/* import { createCommonAccount } from './createDialog.actions'*/
var _createDialog = require('./createDialog.actions');

console.log('createCommonAccount###', _createDialog.createCommonAccount);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }


對於import XXX from 'a',a模塊中首先會生成一個object來存儲這個模塊的全部要export出去的變量:Object.defineProperty(exports, "__esModule", {value: true});這裏給模塊引用添加__esModule屬性是爲了方便babel識別當前import的模塊是不是已經被babel編譯過的模塊仍是第三方模塊。

在經歷一系列的屬性添加後,import會首先返回這個模塊的引用,而後根據具體的import的命令來處理這個引用。

Import A from './module'

若是import的目標是一個default的export,import會先獲取目標模塊的引用,再調用_interopRequireDefault函數處理這個引用。_interopRequireDefault的做用就是判斷require的模塊是不是已經被babel編譯過的模塊,若是是,則當前require的引用必定存在一個default屬性;不然爲他加一個default屬性,這樣便不會調用模塊的default爲undefined的狀況了。

Import * as A from './module'

若是import的目標是一個整個模塊全部export出去的屬性,import會先獲取目標模塊的引用,再調用_interopRequireWildcard函數處理這個引用。_interopRequireWildcard首先判斷require的模塊引用視爲已經被babel正確編譯過,當判斷結果爲FALSE且在當前引用不爲空的狀況下,會對當前引用作淺度克隆,併爲其添加一個default屬性。防止調用模塊的屬性時出現undefined的狀況了。

Import { A } from './module'

若是import的目標是一個整個模塊的普通export出去的屬性,import會先獲取目標模塊的引用,並不當即讀取模塊對象的A屬性,等到真正調用目標屬性時再去讀取模塊對象的A屬性。

 

參考文檔

http://exploringjs.com/es6/ch_modules.html

Babel

http://www.ruanyifeng.com/blog/2016/01/babel.html

相關文章
相關標籤/搜索