目錄
前言
typescript(以下簡稱ts) 官推是腳手架 create-react-app 的ts版本,可自行查閱。但是我沒有用它,原因有2:
- Webpack 版本是3.x,在組內用過4.x重構腳手架之後之後不想再回頭配3.x。
- 作為學習專案以及教程,方便貫穿整個專案構建流程。
接下來將分別從一些用到的tsconfig、webpack配置展開來講解
ts配置
編譯器安裝
ts 作為 js 的方言要編譯成 js 需要編譯器安裝(相關參考5分鐘上手TypeScript)
npm install -g typescript複製程式碼
編譯程式碼當然也就是:
tsc 檔名.ts複製程式碼
最終預設會在當前目錄下生產一個js檔案,就是編譯後的程式碼了。
既然有預設那就有自定義配置,如何做呢?
Ts自定義配置
自定義配置有兩種方式:
-
在命令列後加相應配置引數例如你不想將以下程式碼編譯成es5的函式形式(配置參考連結:compiler-options)
const fuc = ()=>
{
console.log(1);
}fuc();
複製程式碼你可以
tsc 檔名.ts --target es6複製程式碼
如果需要多個配置,繼續往後寫即可,這裡就不詳述了。
-
第一種方式如果引數多了看上去很難受,這裡我推薦第二種方式:在專案跟目錄新建tsconfig.json檔案,我推薦在方法1文件上找到一個配置引數**–init**初始化tsconfig.json(參考連結:tsconfig.json)。
tsc --init複製程式碼
這裡我列舉幾個用到的屬性
{
"compilerOptions": {
"target": "es5", // 你最終編譯成js模型 "lib":[ "es2017", "dom" ],// 你使用的一些庫,你可以理解成ts的一些polyfill "module": "ESNext", // 你編譯後的程式碼的模式,amd umd esmodule...等等下面詳述 "jsx": "react", // jsx 語法糖用哪個 "allowJs": true, // 是否允許引入js "checkJs": true, // 是否檢測js檔案型別 "paths": {
"@components/*": ["./src/components/*"], "@utils/*": ["./src/utils/*"], "@view/*": ["./src/view/*"], "@styles/*": ["./src/styles/*"], "@api/*": ["./src/api/*"], "@store/*": ["./src/store/*"], "@decorators/*": ["./src/decorators/*"], "@assets/*": ["./src/assets/*"],
},// 別名 "strict": true, //嚴格模式 "moduleResolution": "node", // 直接看這個吧https://www.tslang.cn/docs/handbook/module-resolution.html "baseUrl": ".", // 配合paths,當符合 paths 規則的檔案引入,會採用baseUrl+相應陣列列表下查詢的方式去找相應檔案 // "esModuleInterop": true, // 作用是讓commonjs/esmodule兩種模組模式正常通訊(具體看下一節),作用同下,如果使用了es7相關polyfill不可用會報錯(不確定,個人經驗)。 "allowSyntheticDefaultImports": true, // 往下看模組機制 "experimentalDecorators": true, // 使用裝飾器 "rootDir": "./src", // 執行tsc命令時去編譯哪個目錄下的檔案配合webpack可以不設定 "outDir": "./dist" // 同樣,其實這個也可以不設定,但是如果不設定你要給js檔案寫d.ts(後文會講到)這裡會報一個overWrite的錯,作為強迫症就設定一下吧。
}, "include": [ "src/**/*" ], // tsc 會編譯在->
rootDir<
-內哪些檔案 "exclude": [ "node_modules", "dist", "build" ] // tsc 不會編譯->
rootDir<
-內的哪些檔案
}複製程式碼Ps:檔案新建好了之後如果要編譯當前子資料夾,配置是無效的:
tsc ./sub/檔名.ts # 配置無效還是預設配置複製程式碼
模組轉化
通過編譯後的幾種模組模式來幫助理解module
和esModuleInterop
、allowSyntheticDefaultImports
配置項;
// 原始程式碼const fuc = ()=>
{
console.log(1);
}export default fuc();
// commonjs"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var fuc = function () {
console.log(1);
};
exports.default = fuc();
// amddefine(["require", "exports"], function (require, exports) { "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var fuc = function () {
console.log(1);
};
exports.default = fuc();
});
複製程式碼
原始程式碼在 commonjs
和 amd
匯出的資料格式其實是這樣子的(如果不知道defineProperty
可以查下mdn):
{
__esModule: true, default:fuc()
}複製程式碼
當在某處遇到一行程式碼引用了
import fuc from '這個地址';
console.log(fuc);
複製程式碼
當我們加上esModuleInterop
或allowSyntheticDefaultImports
它會被編譯成
"use strict";
var __importDefault = (this &
&
this.__importDefault) || function (mod) {
return (mod &
&
mod.__esModule) ? mod : {
"default": mod
};
};
Object.defineProperty(exports, "__esModule", {
value: true
});
var complie_1 = __importDefault(require("./complie"));
console.log(complie_1.default);
複製程式碼
require("./complie")
這玩意兒就是上面說的那個物件
然後來分析 __importDefault
,首先會識別__esModule
變數,如果為true
,直接把當前模組作為匯出,否則匯出一個物件,物件的default是匯出的模組。
當我們不加上面那兩個選項的時候編譯成
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var complie_1 = require("./complie");
console.log(complie_1.default);
複製程式碼
小結一下:import variable from 'xx'
variable 會被轉成 variable.default;如果配置了esModuleInterop
或allowSyntheticDefaultImports
,如果import
的是esmodule
直接採用當前模組,否則把當前模組放到一個含default
的物件中去,default的值就是當前模組。
OK,接下來我們來解釋這兩個屬性的意義,引入@types/react/index.d.ts
一段程式碼(d.ts是啥之後再探討)
export = React;
export as namespace React;
複製程式碼
我們發現沒有預設值匯出值,但是我們想import React from 'react'
這種操作就會報錯,OK結論就是這兩句話,但是過程。。很曲折。
再看下另外一種引入和匯出方式會如何轉換
// module xexport const a = 1;
export const b = 1;
export const c = 1;
export const d = 1;
export default 10;
// module import module ximport * as all from 'x';
console.log(all)// 轉化過後"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.a = 1;
exports.b = 1;
exports.c = 1;
exports.d = 1;
exports.default = 10;
//----------------------------------var __importStar = (this &
&
this.__importStar) || function (mod) {
if (mod &
&
mod.__esModule) return mod;
var result = {
};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
var all = __importStar(require("x"));
console.log(all);
複製程式碼
同樣esModule原樣匯出,否則取當前物件上的屬性匯出(剔除原型鏈上的屬性);
這裡引入的變數不會被處理成all.default
;
webpack配置
我眼中的webpack
他是一個資源整合工具,經過:資源->
入口->
loader + plugin->
output這樣一個過程,進行策略整合。本小結不詳細講解配置過程,只是描述下配置思路,具體配置項可點我檢視。
首先我們給自己定一個小目標賺他一個億,別走錯片場了,我們的專案需要:
- 能解析 ts|tsx 檔案;
- 能用scss/less檔案;
- 能跟根據不同命令,打包/執行不同執行環境下的程式碼;
- 能熱更新;
- 移動端線真機除錯需要vconsole,但也受命令控制是否引入;
- import(‘xx’)實現模組切割,非同步載入模組;
- 公用庫的只想構建一次;
- 構建後的程式碼,樣式我想
autoprefixer
並額外匯出,js我想壓縮; - 我想引入antd-mobile並能按需引入模組;
接下來我們一個一個實現這些小目標:
- 官推兩個loader:ts-loader/aweasome-typescript-loader,他們都會根據你專案根目錄下的tsconfig.json進行解析。
- 配置scss-loader/less-loader
- 在package.json中的scripts項分別新增相應引數的命令,可以通過yargs這個庫去拿相應的引數通過DefinePlugin去修改方法體上的程式碼。
- 那就是配置devServer配置項嘛,當然別忘了加入入HotModuleWebpackPlugin。react的熱更新需要react-hot-loader除了基礎配置以外還需要一個ForkTsCheckerWebpackPlugin外掛。
- vconsole-webpack-plugin
- 啥也不用幹,ts已經處理好了
- dll??我要更簡單一點,用AutoDllPlugin。
- postcss 配下這個外掛
- ts-import-plugin
具體過程我就不詳述瞭如果你想看demo就點我吧