React & TypeScript
之前看了一下 TypeScript 的知識,但是一直沒有上手,最近開始結合 React 和 TypeScript 一起嘗試了一下,感受還是很好的,所以寫一下筆記。
環境配置沒有參考其他東西,就是看了下 Webpack 和 TypeScript 的官方文件,使用 Webpack 進行構建還是比較簡單的。
環境構建
建立一個專案目錄,然後切換當前目錄到專案目錄下:
$ mkdir tsc && cd ./tsc
然後使用 npm 初始化專案:
$ npm init -y
然後建立一些專案檔案:
$ mkdir build src $ touch build/webpack.base.conf.js build/webpack.dev.conf.js build/webpack.prod.conf.js index.html src/index.tsx tsconfig.json
接下來,就可以安裝一些依賴了:
$ npm i webpack webpack-cli webpack-merge webpack-dev-server -D
$ npm i html-webpack-plugin clean-webpack-plugin typescript ts-loader style-loader css-loader @types/react @types/react-dom -D
$ npm i react react-dom -S
可以注意到我們沒有安裝 babel 轉譯器,如果我們只寫 .ts
或者 .tsx
檔案,可以不安裝 babel。如果要轉譯處理 .js
我們先寫基礎配置:
webpack.base.conf.js
****
const path = require('path'); const htmlWebpackPlugin = require('html-webpack-plugin'); const cleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: path.resolve(__dirname, '../src/index.tsx'), output: { filename: '[name].[hash].js' }, resolve: { extensions: ['*', '.js', '.json', '.ts', '.tsx'] }, module: { rules: [ { test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ }, { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] }, plugins: [ new htmlWebpackPlugin({ inject: true, template: path.resolve(__dirname, '../index.html') }), new cleanWebpackPlugin(['dist']) ] };
然後可以構造開發環境下的配置檔案:
webpack.dev.conf.js
****
const merge = require('webpack-merge');
const path = require('path');
const baseConfig = require('./webpack.base.conf');
module.exports = merge(baseConfig, {
mode: 'development',
devtool: 'source-map',
devServer: {
port: 9999,
open: true,
contentBase: path.resolve(__dirname, '../dist')
}
});
然後新增 npm 指令碼到 package.json
中:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --config ./build/webpack.dev.conf.js"
}
然後新增我們的 ts 配置到 tsconfig.json
:
{
"compilerOptions": {
"outDir": "./dist/", // 打包輸出目錄
"noImplicitAny": true, // 預設必須為變數指定型別
"module": "es6", // 使用 ESM 模組化方案
"target": "es5", // 程式碼編譯成 ES 5
"jsx": "react", // 開啟 JSX,使用 react 方式編譯,如果要使用 babel 編譯,那就將 jsx 設定為 ‘preserve’
"allowJs": true, // 允許編譯 js 程式碼
"sourceMap": true, // 編譯後同時產出 map 檔案
"removeComments": true // 移除註釋
}
}
更多的配置項解釋,參考:翻譯 | 開始使用 TypeScript 和 React。
寫完了以後我們就可以新增內容到我們的開發檔案中了:
index.html
****
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
src/index.tsx
****
import * as React from 'react';
import * as ReactDOM from 'react-dom';
ReactDOM.render(
<h1>Hello TSX!</h1>,
document.getElementById('root') as HTMLElement
);
可以注意到引入 React 和 ReactDOM 的方式和之前有一些不同。
另外由於 TypeScript 的強制轉換符 <>
和 JSX 的元素相沖突,所以使用 as
作為強制轉換符。
執行 npm run dev
,可以檢視效果。
使用 TypeScript 以後,專案配置要稍微簡單一點。配置好開發環境以後,就可以寫程式碼啦!
第一個元件
我以一個 Header 元件為例,效果如下:
新建一個 Header.tsx
檔案和一個 Header.css
檔案到 src/components
下。
由於頭部欄的標題文字應該是可以修改的,然後右邊的 menu 應該是可以自定義的,所以這些資料應該都可以通過 props 傳入我們的 Header 元件。
寫我們的 Header 元件:
// Header.tsx
// 引入 React
import * as React from 'react';
// 引入我們的元件樣式
import './Header.css';
// 定義的介面,用於規範 Header 元件的 props,向外界公開,
// 便於在其他元件中引用時實現這個介面,減少錯誤
export interface HeaderProps {
title: string; // 必須給定 title,一個 string 型別的值
menus?: MenuItemProps[]; // menus 是可選屬性,是一個符合 MenuItemProps 介面規範的物件的陣列
height?: string; // 問號都代表可選項
bgColor?: string;
}
// 這個介面定義了 MenuItem 元件的 props 規範,同時也定義了
// HeaderProps 中 menus 陣列的元素的規範
interface MenuItemProps {
name: string; // 給定 menu 的名稱
href: string; // 給定 menu 要跳轉的連結
}
// 定義了 Header 元件的 state 的規範
interface HeaderState {
isVisible: boolean // 代表 Header 元件是否可見
}
// Header 元件
// 注意 React.Component 後面的泛型,就是我們上方定義的介面,它們分別制定了元件的 props 和 state 的規範
class Header extends React.Component<HeaderProps, HeaderState> {
// 指定元件例項的 state,必須符合 HeaderState 的規範
state = {
isVisible: true
}
render() {
const {
title,
menus = [],
height = '50px',
bgColor = 'lightblue'
} = this.props; // 從 props 獲取值,其中可選項都有預設值
const style = {
height,
backgroundColor: bgColor
}; // 構造 Header 內聯樣式
return this.state.isVisible ? <div style={style} className="header">
<span>{title}</span>
<div className="header-menus">
{
menus.map(item => <MenuItem {...item}/>)
}
</div>
</div> : null;
}
}
// MenuItem 元件
// state 的規範是一個 object,未指定具體介面型別
class MenuItem extends React.Component<MenuItemProps, object> {
render() {
const { name, href } = this.props;
return <a className="header-menu-item" href={href} key={href}>
{name}
</a>
}
}
// 最後向外部暴露 Header 元件
export default Header;
可以看到 TypeScript 結合 React 其實很好用,尤其在規範 props 的時候很好用,能夠避免很多程式設計時候的錯誤。而 IDE 的提示能夠更加地方便我們開發。寫起來何止舒服,簡直舒服啊~
然後在 Header.css
寫一下我們的樣式:
html,
body,
div {
margin: 0;
padding: 0;
font-size: 16px;
}
.header {
font-size: 1.5rem;
line-height: 1rem;
padding: 1rem;
box-sizing: border-box;
position: relative;
}
.header-menus {
position: absolute;
right: 1rem;
top: 50%;
transform: translateY(-50%);
}
.header-menu-item {
margin: 0 .5rem;
}
這裡僅僅是做個示例,排版沒有在意太多的通用性,大家看看就好~
然後我們就可以在 index.tsx
中使用我們的 Header 元件了:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import Header, { HeaderProps } from './components/Header'; // 引入介面規範和元件
// 構造 Header 元件的 props,必須符合 HeaderProps 介面規範。
// 在寫的過程中 IDE 也能給我們很多的提示,方便了開發
const headerProps: HeaderProps = {
title: 'Hello TSX!',
menus: [{
name: 'menu1',
href: 'https://www.zhongdeming.fun'
}, {
name: 'menu2',
href: 'https://www.baidu.com'
}],
bgColor: 'lightyellow'
};
ReactDOM.render(
<Header {...headerProps}/>,
document.getElementById('root') as HTMLElement
);
這樣一個元件就寫完了,可以感受到 TypeScript 確實能夠加速我們的開發,減少開發中的錯誤。
下面是一些利用 TypeScript 開發的時候 IDE 給出的提示的截圖: