这篇文章主要是用来记录在 create-react-app 中使用装饰器 Decorator 和 typeScript,使用装饰器 Decorator 的前提是已经使用 create-react-app 完成了项目的创建。如果你还不知道怎样使用 create-react-app 创建 react 项目,可以点击这里查看详细内容
。
1. 装饰器 Decorator
装饰器(Decorator)是ES6的新特性,是一种与类(class)相关的语法,用来注释或修改类和类方法。Decorator 提案经过了大幅修改,目前还没有定案,但是在开发项目中,依旧是可以使用的。
使用装饰器,要用 Babel 来进行转换,用到的插件是 @babel/plugin-proposal-decorators
1.1 安装:
在终端中执行下面的命令,安装 @babel/plugin-proposal-decorators
yarn add @babel/plugin-proposal-decorators -D
1.2 修改webpack配置文件
如果你还不知道怎么在 create-react-app 中修改 webpack 配置项,点击这里查看第3章内容 create-react-app 高级配置
在项目中 webpack 配置的文件 config-overrides.js 增加下面的内容:
const {override,addBabelPlugin} = require('customize-cra');
const path = require('path')
module.exports = override(
addBabelPlugin( [
//引入装饰器Decorator
"@babel/plugin-proposal-decorators", { "legacy": true }
])
)
1.3 语法
装饰器是一种函数,写成@ + 函数名
。它可以放在类
和类方法
的定义前面
。
例如:@fn class Box{}
- fn 为
函数名
,也就是装饰器
,在该函数中可以修改类的行为,比如给类添加静态属性或者实例属性; - Box 是定义的一个类;
- fn 中的参数是后面的
类本身
; - 装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,装饰器能在编译阶段运行代码。也就是说,装饰器本质就是
编译时执行
的函数; 装饰器
只能用于类
和类的方法
,不能
用于函数
,因为存在函数提升。
1.4 使用
给 Box 类添加静态属性 title,和实例属性 article:
import React from 'react';
import ReactDOM from 'react-dom';
function Foo(target){
target.title="Decorator"
target.prototype.article="decorator是一种语法,用来注释或修改类和类方法"
}
@Foo
class Box extends React.Component{
render(){
return(
<div>
<h1>{Box.title}</h1>
<div>{this.article}</div>
</div>
)
}
}
ReactDOM.render(
<Box />,
document.getElementById('root')
);
如果在调用装饰器函数的时候,需要往里面传入参数,则在函数中需要 return 一个函数,return 返回的函数的参数为类本身。
import React from 'react';
import ReactDOM from 'react-dom';
function Foo(params,target){
//params为下面传过来的参数,这里是2020
//target为要装饰的类本身,这里是Box
return function(target){
target.title="Decorator"
target.prototype.article="decorator是一种语法,用来注释或修改类和类方法"
}
}
@Foo("2020")
class Box extends React.Component{
render(){
return(
<div>
<h1>{Box.title}</h1>
<div>{this.article}</div>
</div>
)
}
}
ReactDOM.render(
<Box />,
document.getElementById('root')
);
2. typeScript
typeScript 是 javaScript 的一个超集,它的优点是对 ES6 支持的比较好,可以增加代码的可读性和可维护性。
2.1 React 项目中添加 typeScript
创建新项目:
要使用 TypeScript 启动新的 Create React App 项目,你可以在终端中执行下面的命令,在当前目录下创建基于 ts 的 react 项目:
npx create-react-app ./ --typescript
#或者
yarn create react-app ./ --typescript
创建成功后,会看到在根目录下多了一个 tsconfig.json 配置文件,执行npm start
启动服务。
更多关于 tsconfig.json 的配置项,可以查看官网介绍
。
如果是添加 TypeScript 到 已有的 Create React App 项目中:
在终端中执行下面的命令安装 TypeScript:
npm install --save typescript @types/node @types/react @types/react-dom @types/jest
# 或者
yarn add typescript @types/node @types/react @types/react-dom @types/jest
安装完成后,将任何文件重命名为 TypeScript 文件(例如 src/index.js 重命名为 src/index.tsx )并重新启动服务器。
2.2 ts函数组件
import React from 'react'
interface IProps { //interface定义数据类型
name:string; //确定属性,必须要传
age?:number //?表示该属性为可选择属性,可以不传
}
function Hello(props:IProps){
return (
<h1>name:{props.name}-age:{props.age}</h1>
)
}
export default Hello;
2.3 ts类组件
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
interface IProps {
count:number; //确定属性
age?:string; //?表示该属性为可选择属性,可以不传
[propName:string]:any //任意属性,确定属性和可选属性必须是任意属性的子集
}
interface IState {
count:number,
username?:string
}
class Counter extends Component<IProps,IState> {
constructor(props:IProps) {
super(props);
this.state ={
count:props.count
}
}
updateCount(count:number){
this.setState({
count
})
}
onIncrement=()=>this.updateCount(this.state.count+1);
onDcrement=()=>this.updateCount(this.state.count-1);
render() {
return (
<div>
<button onClick={this.onDcrement}>-</button>
{ this.state.count }
<button onClick={this.onIncrement}>+</button>
</div>
)
}
}
ReactDOM.render(
<Counter count={2}/>,
document.getElementById('root')
);
2.4 ts高阶组件
组件的类型是 React.ComponentType
import React, { Component,ComponentType } from 'react'
import ReactDOM from 'react-dom'
class App extends Component {
render() {
return (
<div> react.js 是一个构件用户界面的库 </div>
)
}
}
const withCopyrigth = (Comp:ComponentType) => {
return class NewComp extends Component {
render() {
return (
<>
<Comp></Comp>
<div>版权所有:2020-0430</div>
</>
)
}
}
}
const Apps = withCopyrigth(App);
ReactDOM.render(
<Apps />,
document.getElementById('root')
);
2.5 ts事件处理
import React, { MouseEvent } from 'react'
type Props = {
children: React.ReactNode,
buttonClick(e: MouseEvent): void,
touchMove(e: React.TouchEvent): void
}
const Button = ({ children, buttonClick, touchMove }: Props) => {
return (
<>
<button onClick={buttonClick}>{children}</button>
<button onTouchMove={touchMove}>{children}</button>
</>
)
}
class App extends React.Component {
handleClick = (event: MouseEvent) => {
console.log(event.clientX)
}
handleTouch = (event: React.TouchEvent) => {
console.log(event.touches)
}
render() {
return (
<Button touchMove={this.handleTouch} buttonClick={this.handleClick}>确定</Button>
)
}
}
export default App;
2.6 ts Promise
interface IResponse<T> {
message: string,
code: number,
result: T
}
type Params = {
url: string,
type: 'post' | 'get',
data?: any
}
function MyRequest(data: Params): Promise<IResponse<number[]>> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
code: 1,
message: "获取数据成功",
result:[1,2,3]
})
}, 1000)
})
}