目錄
- 起手式
- typescript
- react一些小姿勢
- react的狀態管理
前言
typescript(以下簡稱ts) 官推是腳手架 create-react-app 的ts版本,可自行查閱。但是我沒有用它,原因有2:
- Webpack 版本是3.x,在組內用過4.x重構腳手架之后之后不想再回頭配3.x。
- 作為學習項目以及教程,方便貫穿整個項目構建流程。
react項目?接下來將分別從一些用到的tsconfig、webpack配置展開來講解
ts配置
編譯器安裝
ts 作為 js 的方言要編譯成 js 需要編譯器安裝(相關參考5分鐘上手TypeScript)
npm install -g typescript
復制代碼
react練手項目、編譯代碼當然也就是:
tsc 文件名.ts
復制代碼
最終默認會在當前目錄下生產一個js文件,就是編譯后的代碼了。
react移動端項目?既然有默認那就有自定義配置,如何做呢?
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();
// amd
define(["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 x
export const a = 1;
export const b = 1;
export const c = 1;
export const d = 1;
export default 10;
// module import module x
import * 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就點我吧