Webpack 轉譯 Typescript 現有方案

現有方案

1. awesome-typescript-loader

  • 這個 npm 包很久不更新了,並且類型檢查的時候會有遺漏,因此不推薦使用

2. ts-loader + babel-loader + fork-ts-checker-webpack-plugin

  • 這種方案,當 webpack 編譯的時候,ts-loader 會調用 typescript(因此本地項目須要安裝 typescript),而後 typescript 運行的時候會去讀取本地的 tsconfig.json 文件。
  • 默認狀況下,ts-loader 會進行 轉譯類型檢查,每當文件改動時,都會從新去 轉譯類型檢查,當文件不少的時候,就會特別慢,影響開發速度。因此須要使用 fork-ts-checker-webpack-plugin ,開闢一個單獨的線程去執行類型檢查的任務,這樣就不會影響 webpack 從新編譯的速度。
  • fork-ts-checker-webpack-plugin 這個插件要求最低 Node.js 6.11.5,webpack 4,TypeScript 2.1 和可選的 ESLint 6(其自己要求最低 Node.js 8.10.0)。

webpack 配置方法一

  • 使用並行化構建提高速度(thread-loader),對於 webpack 4+ 來講,速度提高好像不是很明顯

webpack.config.jsjavascript

const cpus = require('os').cpus().length;
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');

module.exports = {
    module: {
        rules: [
                // 單進程
                //{
                    // test: /\.tsx?$/, 
                    // 默認狀況下,ts-loader 會進行轉譯和類型檢查
                    // 由於是單進程,因此 webpack 能夠收集到錯誤信息,並經過 dev-server 反饋到瀏覽器
                    // 但這也致使了 webpack 構建速度極慢
                    // use:['ts-loader']
                //},

                // 多進程
                {
                    test/\.tsx?$/,
                    exclude/node_modules/,
                    use: [
                        {loader'cache-loader'},
                        {
                            loader'thread-loader',
                            options: {
                                workers: cpus - 1,
                            },
                        },
                        'babel-loader',
                        {
                            loader'ts-loader',
                            options: {
                                // 關閉類型檢查,即只進行轉譯
                                // 類型檢查交給 fork-ts-checker-webpack-plugin 在別的的線程中作
                                // transpileOnly: true,
                                // 若是設置了 happyPackMode 爲 true
                                // 會隱式的設置 transpileOnly: true
                                happyPackMode: true
                                // 若是是 vue 應用,須要配置下這個
                                // appendTsSuffixTo: [/\.vue$/]
                            }
                        }
                    ]
                },
                {
                    test/\.(js|jsx)$/,
                    use: ['happypack/loader?id=js'],
                    exclude: [/node_modules/, /(.|_)min\.js$/],
                    // include: [
                    //     path.resolve(__dirname, "src")
                    // ],
                }
        ],
 },
 plugins: [
        // fork 一個進程進行檢查
        new ForkTsCheckerWebpackPlugin({
           // async 爲 false,同步的將錯誤信息反饋給 webpack,若是報錯了,webpack 就會編譯失敗
           // async 默認爲 true,異步的將錯誤信息反饋給 webpack,若是報錯了,不影響 webpack 的編譯
           // async: false,
               // eslint: false,
           checkSyntacticErrors: true
        })
  ]
};
複製代碼

tsconfig.jsonhtml

{
  "compilerOptions": {
    //"module""commonjs",
    "target""es5",
    /* 'react' 模式下,ts 會將 tsx 編譯成 jsx 後再將 jsx 編譯成 js*/
    /* 'preserve' 模式下:TS 會將 tsx 編譯成 jsx 後,再也不將 jsx 編譯成 js,保留 jsx */
    /* 保留 jsx 時,就須要在 ts-loader 前面配置 babel-loader 去處理 jsx */
    /* 換句話說:只有想要用 babel-laoder 的時候,才須要設置這個值 */
    "jsx""preserve",
  },
}
複製代碼

webpack 配置方法二

  • 編譯 ts/tsx 文件時,不使用並行化構建(thread-loader)
const cpus = require('os').cpus().length;
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');

module.exports = {
    module: {
        rules: [
                {
                    test/\.tsx?$/,
                    exclude/node_modules/,
                    use: [
                       'babel-loader',
                        {
                            loader'ts-loader',
                            options: {
                                // 關閉類型檢查,即只進行轉譯
                                // 類型檢查交給 fork-ts-checker-webpack-plugin 在別的的線程中作
                                transpileOnly: true,
                            }
                        }
                    ]
                },
                {
                    test/\.(js|jsx)$/,
                    use: ['happypack/loader?id=js'],
                    exclude: [/node_modules/, /(.|_)min\.js$/],
                    // include: [
                    //     path.resolve(__dirname, "src")
                    // ],
                }
        ],
 },
 plugins: [
        // fork 一個進程進行檢查
        new ForkTsCheckerWebpackPlugin({
           // async 爲 false,同步的將錯誤信息反饋給 webpack,若是報錯了,webpack 就會編譯失敗
           // async 默認爲 true,異步的將錯誤信息反饋給 webpack,若是報錯了,不影響 webpack 的編譯
           // async: false,
               // eslint: false,
           checkSyntacticErrors: true
        })
  ]
};
複製代碼

tsconfig.jsonvue

{
  "compilerOptions": {
    //"module""commonjs",
    "target""es5",
    /* 'react' 模式下,ts 會將 tsx 編譯成 jsx 後再將 jsx 編譯成 js*/
    /* 'preserve' 模式下:TS 會將 tsx 編譯成 jsx 後,再也不將 jsx 編譯成 js,保留 jsx */
    /* 保留 jsx 時,就須要在 ts-loader 前面配置 babel-loader 去處理 jsx */
    /* 換句話說:只有想要用 babel-laoder 的時候,才須要設置這個值 */
    "jsx""preserve",
  },
}
複製代碼

3. babel-loader + @babel/preset-typescript

  • 這種方案,當 webpack 編譯的時候,babel-loader 會讀取 .babelrc 裏的配置,不會調用 typescript(因此本地項目無需安裝 typescript),不會去檢查類型
  • 可是 tsconfig.json 是須要配置的,由於須要在開發代碼時,讓 idea 提示錯誤信息

webpack.config,jsjava

rules: [
        {
          test:/\.(tsx?|jsx?)$/,
          // 默認會調用 @babel/core 
          use:'babel-loader'
        }
]
複製代碼

.babelrcnode

{
    "presets": [
         "@babel/preset-env"
            "@babel/preset-react",
            "@babel/preset-typescript"
    ]
}
複製代碼

常見問題

awesome-typescript-loader 與 ts-loader 的主要區別

  • awesome-typescript-loader 不須要安裝額外的插件,能夠經過內置的 CheckerPlugin 插件,把類型檢查放在獨立的進程中執行
  • 編譯時間對比:
  • 若是都是用默認配置的話,awesome-typescript-loader 的速度相對快一些
  • 若是都設置了禁止類型檢查的選項,ts-loader 的速度相對快一些
  • 若是都設置了禁止類型檢查的選項而且將類型檢查放到獨立的進程中執行,awesome-typescript-loader 相對快一些

並行構建再也不適合新版本的 webpack 了

It's possible to parallelise your builds. Historically this was useful from a performance perspective with webpack 2 / 3. With webpack 4+ there appears to be significantly less benefit and perhaps even cost.
But if that's what you want to do, there's two ways to achieve this: happypack and thread-loader. Both should be used in combination with fork-ts-checker-webpack-plugin for typechecking.)react

  • 並行化構建有兩種方式: happypackthread-loader
  • 並行化構建對於 webpack 2/3 的性能有明顯的提高,使用 webpack 4+時,速度提高的收益彷佛要少得多。

使用了 TypeScript,爲何還須要 Babel

  • 大部分已存項目依賴了 babel
  • 有些需求/功能須要 babel 的插件去實現(如:按需加載)
  • babel 有很是豐富的插件,它的生態發展得很好
  • babel 7 以前:須要前面兩種方案來轉譯 TS
  • babel 7 以後:babel 直接移除 TS,轉爲 JS,這使得它的編譯速度飛快

爲何用了 ts-loader 後,還要使用 babel-loader

  • ts-loader 是不會讀取 .babelrc 裏的配置,即沒法使用 babel 系列的插件,因此直接使用 ts-loader 將 ts/tsx 轉成 js ,就會出現墊片沒法按需加載、antd 沒法按需引入的問題。因此須要用 ts-loader 把 ts/tsx 轉成 js/jsx,而後再用 babel-loader 去調用 babel 系列插件,編譯成最終的 js。

如何選擇轉譯方案

若是在使用 babel-loader + @babel/preset-typescript 這種方案時,也想要類型檢查,該怎麼作

package.jsonwebpack

{
  "scripts": {
     // 再開一個 npm 腳本自動檢查類型
    "type-check""tsc --watch",
  },
    "devDependencies": {
      "@babel/cli""^7.4.4",
      "@babel/core""^7.4.5",
      "@babel/plugin-proposal-class-properties""^7.4.4",
      "@babel/plugin-proposal-object-rest-spread""^7.4.4",
      "@babel/preset-env""^7.4.5",
      "@babel/preset-typescript""^7.3.3",
      "typescript""^3.5.2"
    }
}
複製代碼

tsconfig.jsongit

{
  "compilerOptions": {
    // 不生成文件,只作類型檢查
         "noEmit"true,                      
  },
}
複製代碼

使用 @babel/preset-typescript 須要注意的地方

有四種語法在 babel 中是沒法編譯的github

  • namespace:不要再用了,已通過時了。改用標準的 ES6 module(import/export),在推薦的 tslint 規則中也建議不要使用 namesapce。
namespace Person{
    const name = 'abc';
}
複製代碼
  • 類型斷言:改用 as 來斷言類型(可是在 demo 中試了下,好像沒報錯,不知道是否是能夠正常編譯了)。
interface Person {
    name: string;
    age: number
}

let p1 = {age: 18} as Person;
console.log(p2.name);

let p2 = <Person>{age: 18};
console.log(p3.name);
複製代碼
  • 常量枚舉
const enum Sex {
    man,
    woman
}
複製代碼
  • 歷史遺留風格的 import/export 語法:import xxx= require(…)export = xxx

Typescript 官方轉向 ESLint 的緣由

  • TSLint 執行規則的方式存在一些架構問題,從而影響了性能,而修復這些問題會破壞現有規則;
  • ESLint 的性能更好而且使用者較多

使用了 TypeScript,爲何還須要 ESLint

  • TS 主要是用來作類型檢查和語言轉換的,順帶一小部分的語法檢查
  • ESLint 主要是用來檢查代碼風格和語法錯誤的

使用 `npx create-react-app xxx --typescript` 能夠快速建立 TS 項目

三種方案的配置 Demo

參考

[譯] TypeScript 和 Babel:一場美麗的婚姻web

使用@babel/preset-typescript取代awesome-typescript-loader和ts-loader

build-performance

https://segmentfault.com/q/1010000019545436

推薦閱讀

你真的瞭解 React 生命週期嗎

React Hooks 詳解 【近 1W 字】+ 項目實戰

React SSR 詳解【近 1W 字】+ 2個項目實戰

從 0 到 1 實現一款簡易版 Webpack

傻傻分不清之 Cookie、Session、Token、JWT

相關文章
相關標籤/搜索