TypeScript 在 React 项目中的应用

本文介绍如何使用TypeScript创建React项目,详细解释tsconfig.json配置,演示组件开发过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、创建支持 TypeScriptReact 项目

执行下面的命令新建一个名字为 demoReact 项目:

npx create-react-app demo --template typescript

根据 typescript 官网文档的说明,还可以使用下面的命令:

npx create-react-app demo --scripts-version=react-scripts-ts

react-script-ts是一个在采用了标准的 create-react-app 项目流程 的基础上,混合了 TypeScript 的功能的集合。

我想,原来是采用的第二种方式,后来就整理为了第一种方式,这里采用第一种方式。

创建的项目文件结构:

demo/
  |─   node_modules/
  |─   public/
  |      └─ favicon.ico
  |      └─ index.html
  |      └─ manifest.json
  |      └─ ...
  |─   src/
  |      └─ ...
  |─   .gitignore
  |─   package.json
  |─   package-lock.json
  |─   README.md
  └─   tsconfig.json

执行:

npm start

运行项目,默认服务将运行在 localhost:3000

2、tsconfig.json 配置文件详解

如果一个目录下存在一个 tsconfig.json 文件,那么它意味着这个目录是 TypeScript 项目的根目录,tsconfig.json 文件中指定了用来编译这个项目的根文件和编译选项。

一个项目可以通过以下方式之一来编译:

  • 不带任何输入文件的情况下调用 tsc 命令,编译器会从当前目录开始去查找 tsconfig.json 文件,逐级向上搜索父目录。
  • 不带任何输入文件的情况下调用 tsc 命令,且使用命令行参数 --project(或 -p )指定一个包含 tsconfig.json 文件的目录。

当命令行上指定了输入文件时,tsconfig.json 文件会被忽略。

一个 tsconfig.json 文件主要有以下配置项:

{
  "compilerOptions": {},
  "files": [],
  "include": [],
  "exclude": [],
  "extends": "",
  "compileOnSave": false,
  "typeAcquisition": {}
}

compilerOptions

compilerOptions:对象类型,用来设置编译选项,若不设置则默认使用上节介绍的默认配置。

下面是一份梳理的常用 compilerOptions 属性配置:

{
  "compilerOptions": {
    "target": "esnext", /* 指定编译之后的版本目标: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
    "module": "esnext", /* 指定要使用的模块标准: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    "noImplicitAny": false, /* 是否默认禁用 any */
    "removeComments": true, /* 是否移除注释 */
    "declaration": true, /* 是否自动创建类型声明文件 */
    "strict": true, /* 启动所有类型检查 */
    "jsx": "preserve", /* 指定jsx代码用于的开发环境 */
    "importHelpers": true, /* 引入tslib里的辅助工具函数*/
    "moduleResolution": "node", /* 选择模块解析策略,有'node'和'classic'两种类型 */
    "experimentalDecorators": true, /* 启用实验性的装饰器特性 */
    "esModuleInterop": true,  /* 通过为导入内容创建命名空间,实现CommonJS和ES模块之间的互操作性 */
    "allowSyntheticDefaultImports": true, /* 允许从没有默认导出的模块中默认导入 */
    "sourceMap": true, /* 是否生成map文件 */
    "baseUrl": ".", /* 工作根目录 */
    "types": [ /* 指定引入的类型声明文件,默认是自动引入所有声明文件,一旦指定该选项,则会禁用自动引入,改为只引入指定的类型声明文件,如果指定空数组[]则不引用任何文件 */
      "webpack-env",
      "jest"
    ],
    "paths": { /* 指定模块的路径,和 baseUrl有关联,和 webpack 中 resolve.alias 配置一样 */
      "@/*": [
        "src/*"
      ]
    },
    "lib": [ /* 译过程中需要引入的库文件的列表 */
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  }
}

filesincludeexclude

  • iles 是一个数组列表,写入待编译文件的相对或绝对路径,不支持 glob 匹配模式。
  • include 是一个数组列表,写入待编译文件的路径,支持 glob 匹配模式。
  • exclude 也是一个数组列表,写入排除某些文件路径,这些文件排除于待编译列表,支持 glob 匹配模式。

glob 通配符有:

  • * 匹配 0 或多个字符(不包括目录分隔符)
  • ? 匹配一个任意字符(不包括目录分隔符)
  • **/ 递归匹配任意子目录

如果 "files""include" 都没有被指定,编译器默认包含当前目录和子目录下所有的 TypeScript 文件(.ts, .d.ts.tsx),排除在"exclude" 里指定的文件。

如果开启了 allowJs 选项,那 .js.jsx 文件也属于编译器包含范围。

{
  "files": [
  "core.ts",
  "index.ts",
  "types.ts"
  ],
  "exclude": [
    "node_modules", 
    "lib", 
    "**/*.test.ts"
  ],
  "include": [
    "src/**/*"
  ],
}

如果没有特殊指定,"exclude" 默认情况下会排除 node_modules,bower_components,jspm_packages<outDir> 目录。

任何被 "files""include" 指定的文件所引用的文件也会被包含进来。

优先级:命令行配置 > files > exclude > include

extends

extends:字符串类型,指向另一个要继承文件的路径。例如:

{
  "extends": "config/base.json"
}

这个配置项的意思就是我们可以借助 "extends" 属性引入路径为 "config/base.json" 的配置文件中的配置选项。

configs/base.json:

{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true
  }
}

需要注意:

  • 如果有同名配置,继承文件里的配置会覆盖源文件里的配置

compileOnSave

compileOnSave 是一个布尔类型的属性,当值为 true 时,设置 compileOnSave 属性到 IDE,以便 tsconfig.ts 文件在保存时能够重新生成文件。

typeAcquisition

typeAcquisition:对象类型,用以设置自动引入库类型定义文件(.d.ts),该属性下面有3个子属性:

  • enable: 布尔类型,用以设置是否开启自动引入库类型定义文件
  • include: 数组类型,允许自动引入的库名列表,如 ["jquery", "kendo-ui"]
  • exclude: 数组类型,排除的库名列表

@typestypeRootstypes

默认情况下,node_modules/@types 文件夹下以及它们子文件夹下的所有包都会在编译过程中被包含进来。

但是如果指定了 typeRoots,则只有 typeRoots 路径下的包才会被包含进来:

{
  "compilerOptions": {
    "typeRoots" : ["./typings"]
  }
}

这个配置文件会包含所有 ./typings 下面的包,而不包含 ./node_modules/@types 里面的包。

如果指定了 types,只有被列出来的包才会被包含进来。比如:

{
  "compilerOptions": {
    "types": ["node", "lodash", "express"]
  }
}

如果 types 设置为空数组,则禁止自动引入 @types 包:

{
  "compilerOptions": {
    "types": []
  }
}

注意,自动引入只在你使用了全局的声明(相反于模块)时是重要的。如果你使用 import "foo" 语句,TypeScript 仍然会查找 node_modulesnode_modules/@types 文件夹来获取 foo 包。

3、创建第一个 Hello.tsx 组件

在创建好的项目中,简化 src 目录下的内容,删除 src/index.tsx 之外的所有文件,将 src/index.tsx 文件的内容做精简,示例代码如下:

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
  <div>
  </div>
,document.getElementById('root'));

创建 src/Hello.tsx 文件,示例代码如下:

import React from 'react'

interface IProps {
    message: string
}

const Hello = (props:IProps) => {
    return <h2>{props.message}</h2>
}

export default Hello

Hello.tsx 组件引入到 index.tsx 入口文件中,示例代码如下:

import React from 'react';
import ReactDOM from 'react-dom';
import Hello from './Hello';

ReactDOM.render(
  <div>
    <Hello message="hello world" />
  </div>
,document.getElementById('root'));

在终端执行 npm start 启动项目。

对上面代码还可以继续做优化。修改 src/Hello.tsx 组件,示例代码如下:

import React from 'react'

interface IProps {
    message?: string
}

const Hello:React.FunctionComponent<IProps> = (props) => {
    return <h2>{props.message}</h2>
}

Hello.defaultProps = {
    message: 'hello world'
}

export default Hello

在上面代码中,声明函数组件时使用 React.FunctionComponent 接口,该接口接受一个泛型,使用代码中定义的 IProps 接口作为泛型,此时的 Hello 组件中就可以接收 props.children 的属性,用于接收组件实例中传入的子节点。同时,还可以使用 defaultPropsprops 对象中的属性设置初始化值。

在组件中引入 React.FunctionComponent 单词有点长,可以使用简写的方式引入,修改后的代码如下:

const Hello:React.FC<IProps> = (props) => {
    return <h2>{props.message}</h2>
}

在上面代码中,React.FC 就是 React.FunctionComponent 的简写。

4、封装一个 Button 组件

为了方便的拼接 className 的值,本案例中使用了 classnames 工具。
安装 classnames 工具:

$ npm install classnames --save
$ npm install @types/classnames --save

创建 src/components/Button/button.tsx 组件,示例代码如下:

import React from 'react';
import classNames from 'classnames';
import './button.css';

// 声明按钮尺寸枚举
export enum ButtonSize {
    Large = 'lg',
    Small = 'sm'
}

// 声明按钮样式枚举
export enum ButtonType {
    Primary = 'primary',
    Default = 'default',
    Danger = 'danger',
    Link = 'link'
}

// 声明按钮组件的 props 接口
interface BaseButtonProps {
    className?: string;
    disabled?: boolean;
    size?: ButtonSize;
    btnType?: ButtonType;
    children?: React.ReactNode;
    href?: string
}

// 声明按钮与超链接标签的原生事件
type NativeButtonProps = BaseButtonProps & React.ButtonHTMLAttributes<HTMLElement>
type AnchorButtonProps = BaseButtonProps & React.AnchorHTMLAttributes<HTMLElement>
export type ButtonProps = Partial<NativeButtonProps & AnchorButtonProps>
    
const Button:React.FC<ButtonProps> = (props) => {
    const {
        className,
        btnType,
        disabled,
        size,
        children,
        href,
        ...restProps // 解构按钮与超链接的原生事件属性
    } = props

    // 使用 classnames 工具拼接样式的 class 值
    let classes = classNames('btn',className,{
        [`btn-${btnType}`]: btnType,
        [`btn-${size}`]:size
    })

    if(btnType === ButtonType.Link && href) {
        return (
            <a
               className={classes}
               href={href}
               {...restProps}
            >
                   {children}
            </a>
        )
    } else {
        return (
            <button
                className={classes}
                disabled={disabled}
                {...restProps}
            >
                {children}
            </button>
        )
    }
}

// 定义 props 的默认值
Button.defaultProps = {
    disabled: false,
    btnType: ButtonType.Default,
    size: ButtonSize.Small
}

export default Button

创建 src/components/Button/button.css 样式文件,示例代码如下:

.btn {
    width: 80px;
    height: 30px;
    font-size: 16px;
    color: #666;
}

.btn-danger {
    color: #f56;
}

.btn-primary {
    color: #37f;
}

.btn-lg {
    width: 150px;
    height: 50px;
    font-size: 22px;
}

.btn-sm {
    width: 60px;
    height: 20px;
    font-size: 14px;
}

以上 css的值仅供参考

src/App.tsx 组件中引入 Button 组件,示例代码如下:

import React from 'react'
import Button, {ButtonType,ButtonSize} from './components/Button/button.tsx';
import './app.css'

const App:React.FC = () => {
  return (
    <>
      <Button disabled>Hello</Button>
      <Button btnType={ButtonType.Primary} size={ButtonSize.Large}>Hello</Button>
      <Button btnType={ButtonType.Danger} size={ButtonSize.Small}>Hello</Button>
      <Button btnType={ButtonType.Link} href="http://www.baidu.com">百度一下</Button>
      <Button className='my-btn'>hello</Button>
    </>
  )
}

export default App

创建 src/app.css 样式文件,示例代码如下:

.my-btn{
    width: 300px;
    height: 80px;
    color: 'pink';
    font-size: 15px;
}

以上 css 的值仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柯晓楠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值