require OR import

Jason(楊)發表於2017-06-28

  以前在CommonJS中,我們用module.exports和require來匯出和匯入模組,而到了ES6卻變成了export和import了,這兩者到底有什麼區別呢?
  一句話總結:CommonJS模組是執行輸出(載入)一個值(或物件)的拷貝,而ES6模組則是編譯時輸出(載入)一個值的引用(或者叫做連線).
  這樣的差異在平常使用是不易被察覺的,可是一旦出現迴圈引用,兩者的差異就很明顯了。直接的迴圈引用(a引用b,b又引用a)一般不會有,但在依賴關係複雜的大專案中,很容易出現a引用b,b引用其它模組,在若干次引用後,模組n又引用回a這樣的情況。為了講解的方便我們直接構造出一個a,b相互引用的專案。
  首先,我們來看看CommonJS模組中的現象:

// 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   可以看到,APage正常顯示,並且點選PushToB可以正常顯示出BPage,可從BPage再Reset到APage就成了空白了。這是為什麼呢?
  我們來仔細分析一下整個過程:CommonJS的一個模組,就是一個指令碼檔案,require命令第一次載入該指令碼,就會執行整個指令碼,然後在記憶體生成一個物件。本例在index.js中先require了APage.js,那就開始執行該指令碼,可是在執行過程中先遇到了let BPage = require('./BPage');這時候就會先去執行BPage.js。在BPage.js中又會遇到let APage = require('./APage');但這時候APage.js已經開始執行了,不會重複執行,所以系統會去模組對應的exports屬性取值,可是因為APage.js還沒執行完,從exports屬性中只能取回已經執行的部分,所以APage還是空的,也就是說resetTo(APage)其實是reset到一個空。接著BPage.js會繼續往下執行,等到全部執行完畢,再把執行權還給APage.js
  從上面的例子可以看出,在複雜專案中載入CommonJS模組需要非常小心處理各模組之間的引用關係。接下來我們來看看同樣的場景在ES6中會是怎樣:

// APage.js 關鍵程式碼
import BPage from './BPage';
...//中間部分與上文相同,故不重複貼程式碼
export default route;

// BPage.js 關鍵程式碼
import APage from './APage';
...//中間部分與上文相同,故不重複貼程式碼
export default route;

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

相關文章