歡迎關注個人公衆號睿Talk
,獲取我最新的文章:javascript
前端的模塊化規範包括 commonJS
、AMD
、CMD
和 ES6
。其中 AMD
和 CMD
能夠說是過渡期的產物,目前較爲常見的是commonJS
和 ES6
。在 TS 中這兩種模塊化方案的混用,每每會出現一些意想不到的問題。前端
考慮到兼容性,咱們通常會將代碼編譯爲 es5
標準,因而 tsconfig.json
會有如下配置:java
{ "compilerOptions": { "module": "commonjs", "target": "es5", } }
代碼編譯後最終會以 commonJS
的形式輸出。
使用 React
的時候,這種寫法 import React from "react"
會收到一個莫名其妙的報錯:react
Module "react" has no default export
這時候你只能把代碼改爲這樣:import * as React from "react"
。
究其緣由,React
是以 commonJS
的規範導出的,而 import React from "react"
這種寫法會去找 React
模塊中的 exports.default
,而 React
並無導出這個屬性,因而就報瞭如上錯誤。而 import * as React
的寫法會取 module.exports
中的值,這樣使用起來就不會有任何問題。咱們來看看 React
模塊導出的代碼究竟是怎樣的(精簡過):webpack
... var React = { Children: { map: mapChildren, forEach: forEachChildren, count: countChildren, toArray: toArray, only: onlyChild }, createRef: createRef, Component: Component, PureComponent: PureComponent, ... } module.exports = React;
能夠看到,React
導出的是一個對象,天然也不會有 default
屬性。es6
爲了兼容這種這種狀況,TS 提供了配置項 esModuleInterop
和 allowSyntheticDefaultImports
,加上後就不會有報錯了:web
{ "compilerOptions": { "module": "commonjs", "target": "es5", "allowSyntheticDefaultImports": true, "esModuleInterop": true } }
其中 allowSyntheticDefaultImports
這個字段的做用只是在靜態類型檢查時,把 import
沒有 exports.default
的報錯忽略掉。
而 esModuleInterop
會真正的在編譯的過程當中生成兼容代碼,使模塊能正確的導入。仍是開始的代碼:element-ui
import React from "react";
如今 TS 編譯後是這樣的:json
var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var react_1 = __importDefault(require("react"));
編譯器幫咱們生成了一個新的對象,將模塊賦值給它的 default
屬性,運行時就不會報錯了。segmentfault
若是把 TS 按照 ES6
規範編譯,就不須要加上 esModuleInterop
,只須要 allowSyntheticDefaultImports
,防止靜態類型檢查時報錯。
{ "compilerOptions": { "module": "es6", "target": "es6", "allowSyntheticDefaultImports": true } }
什麼狀況下咱們會考慮導出成 ES6
規範呢?多數狀況是爲了使用 webpack
的 tree shaking
特性,由於它只對 ES6
的代碼生效。
順便再發散一下,講講 babel-plugin-component
。
import { Button, Select } from 'element-ui'
上面的代碼通過編譯後,是下面這樣的:
var a = require('element-ui'); var Button = a.Button; var Select = a.Select;
var a = require('element-ui')
會引入整個組件庫,即便只用了其中的 2 個組件。babel-plugin-component
的做用是將代碼作以下轉換:
// 轉換前 import { Button, Select } from 'element-ui' // 轉換後 import Button from 'element-ui/lib/button' import Select from 'element-ui/lib/select'
最終編譯出來是這個樣子,只會加載用到的組件:
var Button = require('element-ui/lib/button'); var Select = require('element-ui/lib/select');
本文講解了 TypeScript
在導入不通模塊標準打包的代碼對應的處理方式。不管你導入的是 commonJS
的代碼仍是 ES6
的代碼,比較萬無一失的方式是把 esModuleInterop
和 allowSyntheticDefaultImports
都配置上。