以前在CommonJS中,我們用module.exports和require來導出和導入模塊,而到了ES6卻變成了export和import了,這兩者到底有什么區(qū)別呢?
一句話總結(jié):CommonJS模塊是運行輸出(加載)一個值(或?qū)ο?的拷貝,而ES6模塊則是編譯時輸出(加載)一個值的引用(或者叫做連接).
這樣的差異在平常使用是不易被察覺的,可是一旦出現(xiàn)循環(huán)引用,兩者的差異就很明顯了。直接的循環(huán)引用(a引用b,b又引用a)一般不會有,但在依賴關系復雜的大項目中,很容易出現(xiàn)a引用b,b引用其它模塊,在若干次引用后,模塊n又引用回a這樣的情況。為了講解的方便我們直接構造出一個a,b相互引用的項目。
首先,我們來看看CommonJS模塊中的現(xiàn)象:
// APage.js 關鍵代碼
let BPage = require('./BPage');
class APage extends Component {
render() {
return (
<View style={styles.containerAll} >
<TouchableOpacity style={styles.btn} onPress={this.onPress.bind(this)}>
<Text>PushToB</Text>
</TouchableOpacity>
</View>
);
}
onPress() {
this.props.navigator.push(BPage);
}
}
var route = {
key: 'APage',
component: APage,
};
module.exports = route;
// BPage.js 關鍵代碼
let APage = require('./APage');
class BPage extends Component {
constructor(props) {
super(props);
console.log('BPage alloc');
}
render() {
return (
<View style={styles.containerAll} >
<TouchableOpacity style={styles.btn} onPress={this.onPress.bind(this)}>
<Text>resetToA</Text>
</TouchableOpacity>
</View>
);
}
onPress() {
this.props.navigator.resetTo(APage);
}
}
var route = {
key: 'BPage',
component: BPage,
};
module.exports = route;

commonJS.png
可以看到,APage正常顯示,并且點擊PushToB可以正常顯示出BPage,可從BPage再Reset到APage就成了空白了。這是為什么呢?
我們來仔細分析一下整個過程:CommonJS的一個模塊,就是一個腳本文件,require命令第一次加載該腳本,就會執(zhí)行整個腳本,然后在內(nèi)存生成一個對象。本例在index.js中先require了APage.js,那就開始執(zhí)行該腳本,可是在執(zhí)行過程中先遇到了
let BPage = require('./BPage');這時候就會先去執(zhí)行BPage.js。在BPage.js中又會遇到let APage = require('./APage');但這時候APage.js已經(jīng)開始執(zhí)行了,不會重復執(zhí)行,所以系統(tǒng)會去模塊對應的exports屬性取值,可是因為APage.js還沒執(zhí)行完,從exports屬性中只能取回已經(jīng)執(zhí)行的部分,所以APage還是空的,也就是說resetTo(APage)其實是reset到一個空。接著BPage.js會繼續(xù)往下執(zhí)行,等到全部執(zhí)行完畢,再把執(zhí)行權還給APage.js。從上面的例子可以看出,在復雜項目中加載CommonJS模塊需要非常小心處理各模塊之間的引用關系。接下來我們來看看同樣的場景在ES6中會是怎樣:
// APage.js 關鍵代碼
import BPage from './BPage';
...//中間部分與上文相同,故不重復貼代碼
export default route;
// BPage.js 關鍵代碼
import APage from './APage';
...//中間部分與上文相同,故不重復貼代碼
export default route;

ES6.png
只是把導入和導出改為import和export,這一次就可以順利走完整個流程,得到我們想要的。這是因為import只是指向被加載模塊,我們只需要保證真正取值的時候能夠取到值即可。與require時相同,在
APage.js中遇到import BPage from './BPage';也是會先去執(zhí)行BPage.js,也就是說BPage.js在遇到import APage from './APage';時,APage.js同樣是沒執(zhí)行完,這時候APage是undefined。不同的是使用import從一個模塊加載變量,那些變量不會被緩存,而是成為一個指向被加載模塊的引用。所以等到BPage.js執(zhí)行完,把控制權交回給APage.js,這時就一切正常了。