遷移React項目至TypeScript(babel版)

上期我們說到了TypeScript裝飾器(decorators)和JavaScript裝飾器編譯出的代碼不同窒典,雖然我們的庫是用TypeScript寫的肾筐,但很多時候需要提供給JavaScript使用吻商,所以這里來說說怎么將項目遷移至TypeScript甲馋,并用babel編譯既忆。

安裝TypeScript

npm install typescript

書寫配置文件

TypeScript使用tsconfig.json文件管理工程配置铅祸,例如你想包含哪些文件和進行哪些檢查。 讓我們先創(chuàng)建一個簡單的工程配置文件:

{
    "compilerOptions": {
        "outDir": "./dist/",
        "sourceMap": true,
        "noImplicitAny": true,
        "strictNullChecks": false,
        "module": "commonjs",
        "target": "ESNext",
        "jsx": "react",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "moduleResolution": "node",
        "allowJs": true
    },
    "include": [
        "./src/**/*"
    ]
}

這里我們?yōu)門ypeScript設置了一些東西:

讀取所有可識別的src目錄下的文件(通過include)硬梁。
接受JavaScript做為輸入(通過allowJs)前硫。
生成的所有文件放在dist目錄下(通過outDir)。
...
你可以在這里了解更多關于tsconfig.json文件的說明荧止。

創(chuàng)建一個webpack配置文件

在工程根目錄下創(chuàng)建一個webpack.config.js文件屹电。

module.exports = {
    context: __dirname,
    entry: './src/index.tsx',
    output: {
        filename: 'bundle.js',
        path: `${__dirname}/dist`
    },

    // Enable sourcemaps for debugging webpack's output.
    devtool: "#source-map",

    resolve: {
        // Add '.ts' and '.tsx' as resolvable extensions.
        extensions: ['.js', '.ts', '.tsx']
    },

    module: {
        rules: [
            // All files with a '.ts' or '.tsx' extension will be handled by 'babel-loader'.
            {
                test: /\.tsx?$/,
                loader: 'babel-loader'
            },
        ]
    }
};

修改babel配置文件

安裝需要的包

npm install @babel/preset-typescript @babel/plugin-transform-typescript

將上面安裝的包加入工程目錄下的babel.config.js文件。

module.exports = {
    presets: ["@babel/preset-typescript", '@babel/preset-react', '@babel/preset-env', 'mobx'],
    plugins: [
        ['@babel/plugin-transform-typescript', { allowNamespaces: true }],
        // ... other
    ]
}

這里我們使用@babel/plugin-transform-typescript插件來處理TypeScript跃巡。

那么危号,TypeScript的類型檢測怎么辦呢?不是相當于廢了嗎素邪?這里我們使用 fork-ts-checker-webpack-plugin來啟用TypeScript類型檢測外莲。

配置TypeScript類型檢查器

Install

npm install fork-ts-checker-webpack-plugin fork-ts-checker-notifier-webpack-plugin

webpack.config.js

const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const ForkTsCheckerNotifierWebpackPlugin = require('fork-ts-checker-notifier-webpack-plugin');

module.exports = {
    // ...
    plugins: [
        new ForkTsCheckerWebpackPlugin({
            // 將async設為false,可以阻止Webpack的emit以等待類型檢查器/linter娘香,并向Webpack的編譯添加錯誤苍狰。
            async: false
        }),
        // 將TypeScript類型檢查錯誤以彈框提示
        // 如果fork-ts-checker-webpack-plugin的async為false時可以不用
        // 否則建議使用,以方便發(fā)現(xiàn)錯誤
        new ForkTsCheckerNotifierWebpackPlugin({
            title: 'TypeScript',
            excludeWarnings: true,
            skipSuccessful: true,
        }),
    ]
};

準備工作完成烘绽。
終于能試試期待已久的TypeScript了淋昭,心情好happy ??
但是,等等安接,What翔忽?為什么報錯了?

TS2304: Cannot find name 'If'.
TS2304: Cannot find name 'Choose'.
TS2304: Cannot find name 'When'.

原來是我們在React項目中使用了jsx-control-statements導致的盏檐。
怎么辦歇式?在線等,挺急的... ??
我們發(fā)現(xiàn)胡野,這里我們可以用tsx-control-statements來代替材失。

配置 tsx-control-statements

安裝

npm install tsx-control-statements

tsconfig.json文件的files選項中添加

{
    "compilerOptions": {
        "outDir": "./dist/",
        "sourceMap": true,
        "noImplicitAny": true,
        "strictNullChecks": false,
        "module": "commonjs",
        "target": "ESNext",
        "jsx": "react",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "moduleResolution": "node",
        "allowJs": true
    },
    "files": [
        "./node_modules/tsx-control-statements/index.d.tsx"
    ]
}

接下來我們按照TypeScript官網(wǎng)指南來把我們的代碼改成TypeScript就可以了,這里就不作詳細介紹了硫豆。

更便利的與ECMAScript模塊的互通性

但是這就結(jié)束了么龙巨,no no no...
在編譯過程中,我們發(fā)現(xiàn)有些包的導入有問題
比如熊响,將i18next作為外部資源引用時(webpackexternals可以幫助我們實現(xiàn)該方式)旨别,我們發(fā)現(xiàn)代碼被編譯成

i18next_1['default'].t

但是i18next_1['default']的值是undefined,執(zhí)行出錯
為什么汗茄?哪里又雙叒叕...有問題了秸弛???

ECMAScript模塊在ES2015里才被標準化,在這之前,JavaScript生態(tài)系統(tǒng)里存在幾種不同的模塊格式递览,它們工作方式各有不同叼屠。 當新的標準通過后,社區(qū)遇到了一個難題绞铃,就是如何在已有的“老式”模塊模式之間保證最佳的互通性环鲤。

TypeScript與Babel采取了不同的方案,并且直到現(xiàn)在憎兽,還沒出現(xiàn)真正地固定標準冷离。
在之前的版本,TypeScript 對 CommonJs/AMD/UMD 模塊的處理方式與 ES6 模塊不同纯命,這會導致一些問題:

  • 當導入一個 CommonJs/AMD/UMD 模塊時西剥,TypeScript 視 import * as koa from 'koa'const koa = require('koa') 等價,但使用 import * as 創(chuàng)建的模塊對象實際上不可被調(diào)用以及被實例化亿汞。
  • 類似的瞭空,當導入一個 CommonJs/AMD/UMD 模塊時,TypeScript 視 import koa from 'koa'const koa = require('koa').default 等價疗我,但在大部分 CommonJs/AMD/UMD 模塊里咆畏,它們并沒有默認導出。

在 2.7 后的版本里吴裤,TypeScript提供了一個新的 esModuleInterop標記旧找,旨在解決上述問題。
當使用這個新的esModuleInterop標記時麦牺,可調(diào)用的CommonJS模塊必須被做為默認導入:

import express from "express";

let app = express();

我們將其加入tsconfig.json文件中

{
    "compilerOptions": {
        "outDir": "./dist/",
        "sourceMap": true,
        "noImplicitAny": true,
        "strictNullChecks": false,
        "module": "commonjs",
        "target": "ESNext",
        "jsx": "react",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "allowSyntheticDefaultImports": true, // 允許使用 ES2015 默認的 import 風格
        "esModuleInterop": true, // 可調(diào)用的CommonJS模塊必須被做為默認導入钮蛛,在已有的“老式”模塊模式之間保證最佳的互通性
        "moduleResolution": "node",
        "allowJs": true
    },
    "files": [
        "./node_modules/tsx-control-statements/index.d.tsx"
    ]
}

到了這里,我們的程序終于能完美的運行起來了剖膳。
我們不想再區(qū)分哪些需要使用import * as魏颓,哪些使用import,因此我們將格式統(tǒng)一為

import XX from 'XX'
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吱晒,一起剝皮案震驚了整個濱河市甸饱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌仑濒,老刑警劉巖叹话,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異躏精,居然都是意外死亡渣刷,警方通過查閱死者的電腦和手機鹦肿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門矗烛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事瞭吃÷掂郑” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵歪架,是天一觀的道長股冗。 經(jīng)常有香客問我,道長和蚪,這世上最難降的妖魔是什么止状? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮攒霹,結(jié)果婚禮上怯疤,老公的妹妹穿的比我還像新娘。我一直安慰自己催束,他們只是感情好集峦,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著抠刺,像睡著了一般塔淤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上速妖,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天高蜂,我揣著相機與錄音,去河邊找鬼罕容。 笑死妨马,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的杀赢。 我是一名探鬼主播烘跺,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼脂崔!你這毒婦竟也來了滤淳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤砌左,失蹤者是張志新(化名)和其女友劉穎脖咐,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體汇歹,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡屁擅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了产弹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片派歌。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出胶果,到底是詐尸還是另有隱情匾嘱,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布早抠,位于F島的核電站霎烙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蕊连。R本人自食惡果不足惜悬垃,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望甘苍。 院中可真熱鬧盗忱,春花似錦、人聲如沸羊赵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽昧捷。三九已至闲昭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間靡挥,已是汗流浹背序矩。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留跋破,地道東北人簸淀。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像毒返,于是被迫代替她去往敵國和親租幕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345