Typescript 內置的模塊化兼容方式

歡迎關注個人公衆號睿Talk,獲取我最新的文章:
clipboard.pngjavascript

1、前言

前端的模塊化規範包括 commonJSAMDCMDES6。其中 AMDCMD 能夠說是過渡期的產物,目前較爲常見的是commonJSES6。在 TS 中這兩種模塊化方案的混用,每每會出現一些意想不到的問題。前端

2、import * as

考慮到兼容性,咱們通常會將代碼編譯爲 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

2、esModuleInterop

爲了兼容這種這種狀況,TS 提供了配置項 esModuleInteropallowSyntheticDefaultImports,加上後就不會有報錯了: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

3、Tree Shaking

若是把 TS 按照 ES6 規範編譯,就不須要加上 esModuleInterop,只須要 allowSyntheticDefaultImports,防止靜態類型檢查時報錯。

{
  "compilerOptions": {
    "module": "es6",
    "target": "es6",
    "allowSyntheticDefaultImports": true
  }
}

什麼狀況下咱們會考慮導出成 ES6 規範呢?多數狀況是爲了使用 webpacktree 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');

4、總結

本文講解了 TypeScript 在導入不通模塊標準打包的代碼對應的處理方式。不管你導入的是 commonJS 的代碼仍是 ES6 的代碼,比較萬無一失的方式是把 esModuleInteropallowSyntheticDefaultImports 都配置上。

相關文章
相關標籤/搜索