Fork me on GitHub

TypeScript+React

搭建 React+webpack+typescript

参考:https://typescript.bootcss.com/tutorials/React-&-webpack.html

使用 React+webpack+ts 搭建项目

  1. 新建项目
1
2
3
mkdir React-webpack-ts
cd React-webpack-ts
npm init

工程目录

1
2
3
4
5
6
React-webpack-ts/
├─ dist/
└─ src/
└─ components/
└─ index.tsx
└─ index.html

dist目录打包时自动生成

  1. 安装依赖
1
2
npm install --save React React-dom @types/React @types/React-dom
npm install --save-dev typescript awesome-typescript-loader source-map-loader
  1. 添加 ts 配置文件

根目录新建 tsconfig.json

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": true,
"module": "commonjs",
"target": "es5",
"jsx": "React"
},
"include": [
"./src/**/*"
]
}
  1. 代码
  • src/components/Hello.tsx
1
2
3
4
5
import * as React from "React";

export interface HelloProps { compiler: string; framework: string; }

export const Hello = (props: HelloProps) => <h1>Hello from {props.compiler} and {props.framework}!</h1>;
  • src/index.tsx
1
2
3
4
5
6
7
8
9
import * as React from "React";
import * as ReactDOM from "React-dom";

import { Hello } from "./components/Hello";

ReactDOM.render(
<Hello compiler="TypeScript" framework="React" />,
document.getElementById("example")
);
  • src/index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
</head>
<body>
<div id="example"></div>

<!-- Dependencies(打包时排除了这些固定依赖文件,所有需要手动引入) -->
<script src="./node_modules/React/umd/React.development.js"></script>
<script src="./node_modules/React-dom/umd/React-dom.development.js"></script>

<!-- Main -->
<script src="./dist/bundle.js"></script>
</body>
</html>
  1. 配置 webpack
    根目录新建 webpack.config.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
module.exports = {
entry: "./src/index.tsx",
output: {
filename: "bundle.js",
path: __dirname + "/dist"
},

// Enable sourcemaps for debugging webpack's output.
devtool: "source-map",

resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: [".ts", ".tsx", ".js", ".json"]
},

module: {
rules: [
// All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
{ test: /\.tsx?$/, loader: "awesome-typescript-loader" },

// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{ enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
]
},

// When importing a module whose path matches one of the following, just
// assume a corresponding global variable exists and use that instead.
// This is important because it allows us to avoid bundling all of our
// dependencies, which allows browsers to cache those libraries between builds.
externals: { //不打包
"React": "React",
"React-dom": "ReactDOM"
},
};
  1. 打包
1
npx webpack

使用 create-React-app 新建 ts 项目

1
2
3
npx create-React-app React-ts --typescript
# or
yarn create React-app React-ts --typescript

兼容 ie11

  1. package.json
    “browserslist”: {
    “production”: [
    “>0.2%”,
    “not dead”,
    “not op_mini all”
    ],
    “development”: [
    “ie 11”,
    “last 1 chrome version”,
    “last 1 firefox version”,
    “last 1 safari version”
    ]
    }

  2. 删除 node_modules 里面的 .cache i

  3. 重新运行项目
    如果系统解析其他 ES6 等模块在 src/index.js 第一行
1
2
import "React-app-polyfill/ie11";
import "React-app-polyfill/stable";

暴露脚手架配置

1
yarn eject

注意:如果之前全局安装过 create-React-app(sudo npm install -g create-React-app),请使用sudo npm uninstall -g create-React-app卸载,以确保使用最新版的脚手架
项目会自动安装 ts 和 node、React、React-dom、jest 的声明文件依赖

与原来的 React 项目的不同

  1. 文件扩展名:.js变成.ts.jsx变成了.tsx
  2. .ts 是普通 ts 文件,.d.ts 是声明文件
  3. 全局变量或者自定义的 window 对象属性,统一在项目根目录的 global.d.ts 中进行定义声明
  4. 对于项目中常用到的接口数据对象,在 types/目录下定义好其结构化类型声明
  5. 根目录的 tsconfig.json 可以配置 TypeScript 的编译选项
    jsx 工作模式匹配
  • preserve:生成代码保留 JSX,输出文件是.jsx
  • React:直接编译成React.createElement
  • React-native:相当于 preserve,它也保留了所有的 JSX,但是输出文件的扩展名是.js
模式 输入 输出 输出文件扩展名
preserve <div/> <div/> .jsx
React <div/> React.createElement('div') .js
React-native <div/> <div/> .js
  1. @types/xxx
    xxx依赖的声明文件,统一放在node_modules/@types文件夹下,TypeScript 会自动从这里获取模块内相关类型定义
    搜索依赖对应文件声明库
  2. as 运算符
    类型断言。默认是

    1
    var foo1=<foo>bar

    与 jsx 语法冲突,可以使用

    1
    var foo1=bar as foo

各种类型的定义

类的定义

泛型参数定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, { Component } from "React";
interface IProps {
name: string;
}
interface IState {
color: "red" | "blue";
}
class AddTodo extends Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
color: "red"
};
}
}

函数组件定义

1
2
3
4
5
6
7
8
9
10
interface Props {
tasks: Task[];
onDelete: (task: Task) => void;
}
export const TaskListItem: FunctionComponent<Props> = ({ task, onDelete }) => {
return ();
};
export const TaskListItem: React.RF<Props> = ({ task, onDelete }) => {
return ();
};

input 事件

1
private handleTaskChange = (event: React.ChangeEvent<HTMLInputElement>) => {};

点击事件

1
2
3
private handleClick = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
};

https://juejin.im/post/5bed5f03e51d453c9515e69b

hooks

1
2
3
4
5
6
7
8
9
10
const Counter: FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => {
const [clicks, setClicks] = useState(initial);
return (
<div>
<p>Clicks:{clicks}</p>
<button onClick={() => setClicks(clicks + 1)}>+</button>
<button onClick={() => setClicks(clicks - 1)}>-</button>
</div>
);
};

条件渲染

1
2
3
const MyArrayComponent = () =>
(Array(5).fill(<div>11</div>) as any) as JSX.Element;
const el2 = <MyArrayComponent />; // throws an error

https://github.com/piotrwitek/React-redux-typescript-guide#Reactfcprops--Reactfunctioncomponentprops
https://github.com/typescript-cheatsheets/React-typescript-cheatsheet

-------------本文结束感谢阅读-------------