写在前面
Typescript是JavaScript的一个超集,主要提供了类型系统和对es6的支持。本人使用ts编写react将近3个月的时间,中间踩了不少坑,从刚开始的觉得ts没有必要到现在觉得ts真香。在这里对使用ts编写react的心得做一下总结。
本文将从以下几部分进行总结:
Typescript的优势
TS结合React使用
总结
参考链接及扩展阅读
Typescript的优势
1.帮助更好地重构代码
一个好的代码习惯是常常对自己写的代码进行小的重构,使得代码可维护性更强。但是对于很多线上运行的代码,代码测试覆盖率往往不是很高,有时候哪怕一个变量名的改动,都会牵一发而动全身。而对于使用ts编写的项目就不会有这种担心。ts的静态检查特性会帮助找出代码中有错误的部分。
2.vscode等IDE的提示更加智能
js是一门动态弱类型解释语言,变量声明后可以改变类型,而且类型需要在运行时才能确定。而ts的报错提示是在编译时,不是在运行时。所以使用ts带来的静态类型检查等特性将使得IDE的提示更加完善。
3.类型声明本身就是非常好的文档
当你接手一个有历史包袱的项目时,肯定会头疼于文档和代码注释的缺失,而对于ts来说,是可以做到代码即文档的,通过声明文件可以知道哪些字段的含义以及哪些字段是必填和选填的。举个简单例子,当封装一个button的组件时:
export interface ButtonProps {
style?: React.CSSProperties
className?: string
label?: React.ReactNode
type?: 'primary' | 'default' | 'search'
size?: 'sm' | 'md' | 'lg' | 'mini'
disabled?: boolean
title?: string
onClick?: ((e: React.MouseEvent<HTMLButtonElement>) => void)
}
通过这些声明文件可以知道,当使用这个button文件时,style是一个可选值,表示一个可以自定义样式的style字段。type也是一个可选值,表示按钮的颜色类型,可以选择'primary','default','mini'其中的一种。disabled也是一个可选值,传入的值必须是boolean类型。所以就可以看出类型声明本身就是非常好的文档。
TS结合React使用
类组件的使用
以下是官网的一个例子,创建Props和State接口,Props接口接受name和enthusiasmLevel参数,State接口接受currentEnthusiasm参数。
import * as React from "react";
export interface Props {
name: string;
enthusiasmLevel?: number;
}
interface State {
currentEnthusiasm: number;
}
class Hello extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { currentEnthusiasm: props.enthusiasmLevel || 1 };
}
onIncrement = () => this.updateEnthusiasm(this.state.currentEnthusiasm + 1);
onDecrement = () => this.updateEnthusiasm(this.state.currentEnthusiasm - 1);
render() {
const { name } = this.props;
if (this.state.currentEnthusiasm <= 0) {
throw new Error('You could be a little more enthusiastic. :D');
}
return (
<div className="hello">
<div className="greeting">
Hello {name + getExclamationMarks(this.state.currentEnthusiasm)}
</div>
<button onClick={this.onDecrement}>-</button>
<button onClick={this.onIncrement}>+</button>
</div>
);
}
updateEnthusiasm(currentEnthusiasm: number) {
this.setState({ currentEnthusiasm });
}
}
export default Hello;
function getExclamationMarks(numChars: number) {
return Array(numChars + 1).join('!');
}
无状态组件的使用
无状态组件也称为傻瓜组件,如果一个组件内部没有自身的state,那么组件就可以称为无状态组件。在@types/react已经定义了一个类型type SFC<P = {}> = StatelessComponent
。我们写无状态组件的时候,能指定我们的组件为SFC或StatelessComponent。它已经预定义了children,displayName等。以button为例:
import React from 'react'
const Button = ({ onClick: handleClick, children }) => (
<button onClick={handleClick}>{children}</button>
)
如果采用ts来编写出来的无状态组件是这样的:
import React, { MouseEvent, SFC } from 'react';
type Props = { onClick(e: MouseEvent<HTMLElement>): void };
const Button: SFC<Props> = ({ onClick: handleClick, children }) => (
<button onClick={handleClick}>{children}</button>
);
readonly
react规定不能通过this.props.xxx和this.state.xxx直接进行修改,所以可以将State和Props标记为不可变数据:
interface Props {
readonly number: number;
}
interface State {
readonly color: string;
}
export class Hello extends React.Component<Props, State> {
someMethod() {
this.props.number = 123; // Error: props 是不可变的
this.state.color = 'red'; // Error: 你应该使用 this.setState()
}
}
处理Event对象
在工作中,可能经常会使用Event对象,change事件可以使用React.ChangeEvent, click事件可以使用React.ChangeEvent。
onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
// do something
}
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// do something
}
可渲染的接口
React 可以渲染一些像 JSX 或者是 string 的内容,这些被合并到类型 React.ReactNode 中,因此,当你接收可渲染的内容时,你可以使用它:
type Props = {
header: React.ReactNode;
body: React.ReactNode;
};
class MyComonent extends React.Component<Props, {}> {
render() {
return (
<div>
{this.props.header}
{this.props.body}
</div>
);
}
}
<MyComponent header={<h1>Header</h1>} body={<i>body</i>} />
总结
在大中型前端项目中,由于js的动态弱类型特性,导致很多错误在运行时才发现。ts作为js的超集,为前端开发带来了编译时的检查,将很多的错误避免在了编译阶段。也为IDE带来了更强的智能提示。虽然学习ts会花一些时间,但这些时间是值得的。使用ts开发项目之后,明显发现项目的可维护性变强了,bug率降低了,查文档也更加方便,一看类型声明文件就明白了各个字段的含义。