基於 TypeScript + Babel + Rollup 搭建 ts 開發環境

前言
本篇文章講述了基於 TypeScript + Babel + Rollup 搭建 ts 開發環境的解決方案,先贅述了此方案的優劣勢,后按步驟具體講解各個插件的配置方式,以幫助大家了解 TS 項目的編譯原理。
以下是該文章示例代碼所依賴的基礎環境,如有不符,配置引導可能無法正常運行。
TypeScript + Babel 方案的特點
什么是 Babel-TypeScript
大家也許在 Babel 官網中看到過一個 preset,叫 @babel/preset-typescript
,沒錯,Babel 不僅可以編譯 ES6 的語法糖、對瀏覽器兼容性進行 polyfill 以外,還擴展了對 TypeScript 的支持,但它與官方的 TypeScript 有一些差異,比較特殊。
下面,由我來給大家一一道來。
開發體驗特點
使用 TypeScript + Babel 方案搭建的項目有什么特點?
-
開發編譯過程,babel 會直接去除 typescript 類型標記,輸出編譯結果
- 速度很快,但沒有類型檢查
-
通過 eslint 做語法檢查和類型檢查
- 並且檢查時機是在 commit 時(由 husky 添加的鈎子)
- 當然,vscode 對 typescript 的類型檢查也有一定程度的支持
-
為了降低語法檢查帶來的麻煩,通過配置 prettier 來格式化代碼
- 保存文件時(需要配置編輯器)觸發 prettier
- 執行 git commit 時(由 husky 實現)觸發 prettier
Babel 編譯的劣勢
萬事開頭難,先說說 Babel 編譯的劣勢
- Babel 編譯 TypeScript 不會做類型檢查,所有的類型聲明都會被 Babel 拋棄掉
- 不支持編譯部分語法,比如:import xx = ‘xx’、namespace(無關緊要,這些語法都已棄用了)
Typescript 編譯器完全沒有上述的問題,那為什么還要多此一舉使用 Babel 編譯 Typescript 呢?
Babel 編譯的優勢
先苦后甜,再來說說 Babel 的優勢
靈活性
- Babel 支持根據瀏覽器兼容性要求按需編譯,這個 TypeScript 是不支持的,且官方也聲明過不在考慮范圍內Polyfill
- Babel 支持根據瀏覽器兼容性要求按需添加 PolyfillPlugins/Presets
- Babel 支持超級多的 Plugins,而且通過預設 Presets 可以免去復雜的 Plugins 配置,這點 TypeScript 也不能滿足
好,贅述完了,我們來看一下實戰演練,動手做個 demo。
懶人請執行以下命令,后面的就不用動手了(先查看上面的基礎環境要求,以確保你的環境可以正常運行以下命令)
git clone https://github.com/lianer/test-babel-typescript.git
創建一個空的 demo 項目
在命令行直接執行這些命令,先搭一個基礎的倉庫腳手架
mkdir test cd test npm init mkdir src cd src # 下面多行內容一整段復制執行 cat <<EOF > index.ts const sum = function (a: number, b: number): number { return a + b; }; console.log(sum(1, 2)); EOF
目錄結構如下
| src | index.ts | package.json
配置 typescript
配置核心要求的 typescript,雖然僅僅會在 lint 的時候用到它
1、安裝 typescript 依賴
yarn add -D typescript
2、添加 tsconfig 配置文件
因為是 demo 項目,沒有太多要求,因此這里保持默認的配置就足夠了
./node_modules/.bin/tsc --init
配置 rollup、babel 套件
支持對 ts 文件的編譯,產出 js 文件
相關資料
配置引導
1、安裝相關依賴
# 安裝 rollup 套件 yarn add -D rollup rollup-plugin-babel@latest @rollup/plugin-node-resolve # 安裝 babel 套件 yarn add -D @babel/core @babel/preset-env @babel/preset-typescript
2、添加 .babelrc 配置文件
{
"presets": [ "@babel/preset-env", "@babel/preset-typescript" ] }
3、添加 rollup.config.js 配置文件
const path = require('path'); const babel = require('rollup-plugin-babel'); const nodeResolve = require('@rollup/plugin-node-resolve'); const pkg = require('./package.json'); const extensions = ['.js', '.ts']; const resolve = function(...args) { return path.resolve(__dirname, ...args); }; module.exports = { input: resolve('./src/index.ts'), output: { file: resolve('./', pkg.main), // 為了項目的統一性,這里讀取 package.json 中的配置項 format: 'esm', }, plugins: [ nodeResolve({ extensions, modulesOnly: true, }), babel({ exclude: 'node_modules/**', extensions, }), ], };
4、配置 npm scripts
如果先前項目里還沒有 package.json
文件,可以先通過 npm init
命令初始化一個
"scripts": { "build": "rollup -c" },
5、在 rollup.config.js 中,用到了 package.json - main 配置項,因此別忘了修改一下 package.json
"main": "lib/index.js"
5、測試一下
npm run build
打包成功,輸出結果
> test-typescript-babel@1.0.0 build /dev/test /dev/test/src/index.ts → lib/index.js... > rollup -c created lib/index.js in 460ms
目錄結構
| lib | index.js | src | index.ts | .babelrc | package.json | rollup.config.js | tsconfig.json | yarn.lock
編譯出的 lib/index.js 文件內容
var sum = function sum(a, b) { return a + b; }; console.log(sum(1, 2));
認真的同學可能注意到了,Babel 僅僅只是把 TypeScript 類型移除
了而已。
注:如果你再這一步報錯了,提示 The "path" argument must be of type string. Received type undefined
,則檢查 package.json - main 配置是否有誤
配置 eslint、prettier
支持代碼的類型校驗、語法校驗,以及代碼格式化
相關資料
配置引導
1、安裝依賴
# 安裝 eslint 套件 yarn add -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin # 安裝 prettier yarn add -D prettier # 安裝 husky、lint-staged 套件 yanr add -D husky lint-staged
2、添加配置文件 .eslintrc.js
.eslintrc.js 文件描述 eslint 語法檢查和 ts 類型檢查的規則
const path = require('path'); module.exports = { parser: '@typescript-eslint/parser', extends: [ 'plugin:@typescript-eslint/recommended' // Uses the recommended rules from the @typescript-eslint/eslint-plugin ], parserOptions: { project: path.resolve(__dirname, './tsconfig.json'), tsconfigRootDir: __dirname, ecmaVersion: 2019, // Allows for the parsing of modern ECMAScript features sourceType: 'module', // Allows for the use of imports }, rules: { // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs // e.g. "@typescript-eslint/explicit-function-return-type": "off", } };
3、添加配置文件 .prettierrc
.prettierrc 文件描述代碼格式化的規則
{
"semi": true, "trailingComma": "all", "singleQuote": true, "printWidth": 120, "tabWidth": 2 }
4、修改 package.json,配置 husky 和 lint-staged
一個非常龐大的項目,eslint 完整檢查可能需要花費幾分鍾的時間。
而 husky + lint-staged 可以實現只對提交的文件進行檢查,從而提升開發效率。
這樣即使你的項目再大,也僅僅是檢查本次提交的文件,只需幾秒鍾。
"scripts": { "lint": "eslint 'src/**/*.{js,ts}'" }, "husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "*./src/**/*.{js,ts,json,css,less,md}": [ "prettier --write", "yarn lint" ] }
上述配置可以實現在執行 git commit
時調用 prettier
格式化代碼,並使用 eslint
做類型和語法的檢查。
5、如果想要保存文件時自動格式化代碼,則需要安裝 vscode 插件
prettier
,並做如下配置
"editor.formatOnSave": false, // 全局默認關閉不做格式化,僅針對 js 和 ts 格式化 "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, }, "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, },
進一步完善打包機制
進一步完善 rollup 的打包機制,支持多任務打包,打包出不同項目所需的資源文件。
引入 rimraf, npm-run-all, rollup-plugin-uglify, lodash.merge
相關資料
- rimraf - 文件刪除工具,用於每次編譯前清空 lib 目錄
- npm-run-all - npm 命令並行執行工具
- rollup-plugin-uglify - uglify js 壓縮工具(rollup 版)
- lodash.merge - 配置合並工具
配置引導
1、 安裝依賴
yarn add -D rimraf npm-run-all rollup-plugin-uglify lodash.merge
2、修改 rollup.config.js
const path = require('path'); const babel = require('rollup-plugin-babel'); const nodeResolve = require('@rollup/plugin-node-resolve'); const uglify = require('rollup-plugin-uglify').uglify; const merge = require('lodash.merge'); const pkg = require('./package.json'); const extensions = ['.js', '.ts']; const resolve = function(...args) { return path.resolve(__dirname, ...args); }; // 打包任務的個性化配置 const jobs = { esm: { output: { format: 'esm', file: resolve(pkg.module), }, }, umd: { output: { format: 'umd', file: resolve(pkg.main), name: 'rem', }, }, min: { output: { format: 'umd', file: resolve(pkg.main.replace(/(.\w+)$/, '.min$1')), name: 'rem', }, plugins: [uglify()], }, }; // 從環境變量獲取打包特征 const mergeConfig = jobs[process.env.FORMAT || 'esm']; module.exports = merge( { input: resolve('./src/index.ts'), output: {}, plugins: [ nodeResolve({ extensions, modulesOnly: true, }), babel({ exclude: 'node_modules/**', extensions, }), ], }, mergeConfig, );
3、修改 package.json - scripts
"main": "lib/index.umd.js", "module": "lib/index.esm.js", "scripts": { "lint": "eslint 'src/**/*.{js,ts}'", "dev": "rollup -w -c --environment FORMAT:esm", "build:esm": "rollup -c --environment FORMAT:esm", "build:umd": "rollup -c --environment FORMAT:umd", "build:min": "rollup -c --environment FORMAT:min", "build": "rimraf lib/* && run-p build:esm build:umd build:min" },
在這里,我們先通過 rimraf 工具清空 lib 目錄,然后再通過 npm-run-all 工具並行 3 個子編譯任務
這里的 3 個子編譯任務,分別是:
- build:esm - 編譯出符合 esm 規范的可執行文件,供 Vue、React 等采用 esmodule 規范進行模塊化打包的項目使用
- build:umd - 編譯出符合 umd 規范的可執行文件,供 jQuery、Vue、NodeJS 等項目使用
- build:min - 編譯出符合 umd 規范的壓縮的可執行文件
4、測試執行打包命令
npm run build
輸出結果文件
| lib | rem.esm.js | rem.umd.js | rem.umd.min.js
進一步完善工程
統一編輯器的行為,比如空格、縮進等,並添加 git 忽略列表
1、添加 .editorconfig 配置文件,編輯器安裝相應的 editorconfig 插件,使該項目統一應用相同的空格、縮進等編碼風格
root = true [*] charset = utf-8 indent_style = space indent_size = 2 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true
2、添加 .gitignore 配置文件,這里推薦一個 .gitignore 的生成工具 - gitignore.io
# Created by https://www.gitignore.io/api/vuejs,visualstudiocode # Edit at https://www.gitignore.io/?templates=vuejs,visualstudiocode ### VisualStudioCode ### .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json ### VisualStudioCode Patch ### # Ignore all local history of files .history ### Vuejs ### # Recommended template: Node.gitignore node_modules/ dist/ npm-debug.log yarn-error.log # End of https://www.gitignore.io/api/vuejs,visualstudiocode
結尾
至此,基於 Rollup + Babel + TypeScript + ESLint 的整套套件的搭建就完成了。
如果有不夠完善的地方,歡迎在文末進行點評[點贊]。