CRA 构建 React 与 TypeScript 开发环境

发布于 2023-05-04 20:07:48 字数 5517 浏览 63 评论 0

Create-React-App(CRA) 是 Facebook 推出的脚手架工具用于构建 React 开发环境,快捷简单零配置推荐使用这个脚手架来构建 React 应用(至于我搭的 Yorha Boilerplate 只是代码文件不像 CRA 这样可选择构建生成)。如果 CSS 预处理器选择 stylus 但不知道如何加到 CRA 里可以看我在 Github 发的 CRA 个人用搭建步骤

TypeScript(TS) 是 JavaScript(JS) 的超集,在 JS 原有的基础上添加了扩展语法并容易掌握,JS 最令人诟病的弱类型语言特性在 TS 中得到了解决,代码完全 OOP 开发,这也可能是后端工程师直接选择学习 TS 而不是 JS 的原因,不必多去了解原型链且 OOP 封装继承多态概念在 TS 中存在。仍需注意的是目前还有许多第三方 JS 类库不支持 TS。如果感兴趣想开始入门学习 TS 可直接看官网文档

有这样一个问题:JS 是解释型语言还是编译型语言?《你不知道的JS》上卷中第一章就有了很好的解释(这里只是简单的描述,更详细内容还是推荐去看书)。事实上 JS 是一门编译语言,但相比编译过程有三个步骤(分词/语法分析 → 解析/语法分析 → 代码生成)的语言的编译器,JS 要复杂很多,实际上 JS 代码执行前,就已经被「JS 浏览器引擎」或者是「特定的编译环境」例如 NodeJS 编译过。

首先,JS 引擎不会有大量的(像其他语言编译器那么多的)时间用来进行优化,因为与其他语言不同,JS 的编译过程不是发生在构建之前的。对于 JS 来说,大部分情况下编译发生在代码执行前的几微秒(甚至更短!)的时间内。

在 JS 作用域背后,JS 引擎用尽了各种办法(比如 JIT,可以延迟编译甚至实施重编译)来保证性能最佳。简单地说,任何 JS 代码片段在执行前都要进行编译(通常就在执行前)。

简单了解 JS 编译过程后再看看 TS,当运行一段 TS 时,代码首先是 TypeScirpt 编辑器进行的编译阶段,然后才是 JS 引擎的编译阶段,确保代码运行之前是按照预期方式所写的,得益于 TS 编译器代码更易于调试和发现错误。所以会有这样一个段子:一名工程师写了上千行 TS 代码运行后一个报错信息都没有兴奋的打电话给女朋友讲述这件事,女朋友冷漠的回了个字:哦。

CRA 环境搭建

这里开始正题内容,我的 CRA 是安装在全局,希望您和我一样。

npm install -g create-react-app

安装成功后,执行下面命令,初始化项目并带上 scripts-version 参数为 react-scripts-ts。

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

react-scripts-ts 参数告诉 CRA 使用 TypeScript 并添加相对应 tools。成功后会生成以下的文件结构:

ts-app/
├─ .gitignore
├─ node_modules/
├─ public/
├─ src/
 | — — index.tsx 
 | — — registerServiceWorker.ts
 | — — logo.svg
 | — — App.tsx
 | — — App.test.tsx
 | — — App.css
 | — — index.css
 | — — assets/
├─ package.json
├─ tsconfig.json
├─ tsconfig.test.json
└─ tslint.json

以下是目录解释:

  • tsconfig.json TS 配置声明文件,一般在根目录
  • tslint.json TSLint 配置文件
  • public 静态资源文件夹用于保存 HTML 与 manifest 文件
  • src APP 代码目录,包括 TS 组件与 CSS 样式,入口文件 index.js 已被 index.tsx 替换

除了在 TS 中加入配置声明,引入 React 库也需要相对应的声明文件(declaration files)。声明文件可以看做是 TS 与 JS 之间的一个接口。这些声明文件使用@types前缀。在 ts-app 项目的 package.json 可以看到安装了以下四个声明文件依赖:

  • @types/jest
  • @types/node
  • @types/react
  • @types/react-dom

在开始用 React 配合 TS 写代码时,需要注意两点,第一是将 .jsx 文件后缀改为 .tsx,关于更多 JSX 介绍可以查阅官网文档 JSX。第二是代码开头引入 React 库时的语法:

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

无状态功能组件

在写 props 和 state 代码时一旦理解 interface 的使用(初学还是建议多看文档),那么会比默认的写法好很多。在 React 每个组件里都会定义一个 props interface,这个声明就像对象和类型相关的属性值。这里我创建了一个新的函数组件为 Header,接受一个 name 属性。name? 中的 ? 代表 name 属性的类型为 string 或者 undefined。React.SFC 表示的是无状态功能组件,这个不是必须要写但可以允许使用 defaultProps。

import * as React from 'react';

interface Props {
  name?: string;
}

const Header: React.SFC<Props> = (props: Props) => (
  <h1>
    Hello, {props.name}! Welcome to React and TypeScript.
  </h1>
);

Header.defaultProps = {
  name: 'world',
};

export default Header;

然后在 App.tsx 中引入并替换 h1 标签为 Header 组件,就完成了第一个无状态功能组件。

Class 组件

为了展示 Class 组件是如何写,这里就将 App.tsx 中的 p 标签内容替换为我新创建的组件 Description.tsx。Class 组件与函数组件区别第一在于 Class 组件可以创建维护自己的 state,第二在于创建生命周期方法。写 React 的都知道在这里不多提。这个组件有个可选的 prop 为 countBy,下面是 Description.tsx 组件代码:

import * as React from 'react';

interface Props {
  countBy?: number;
}

interface State {
  count: number;
}

class Description extends React.Component<Props, State> {
  public static defaultProps: Partial<Props> = {
    countBy: 1,
  };

  state: State = {
    count: 0,
  };

  increase = () => {
    const countBy: number = this.props.countBy!;
    const count = this.state.count + countBy;
    this.setState({ count });
  }

  render() {
    return (
      <div>
        <p>
          My favorite number is {this.state.count}
        </p>
        <button onClick={this.increase}>
          Increase
        </button>
      </div>
    );
  }
}

export default Description;

具体代码实现逻辑很简单点击按钮后增加 count 值这里不多描述,只是为了展示组件写法,运行命令npm run start后启动本地预览。

React Types Cheatsheet

// `React.StatelessComponent<P> or React.SFC<P>`
const MyComponent: React.SFC<MyComponentProps> = ...

// `React.Component<P, S>`
class MyComponent extends React.Component<MyComponentProps, State> { ... };

// React.ReactElement<P> or JSX.Element
const elementOnly: React.ReactElement = <div /> || <MyComponent />;

// React.ReactNode
const elementOrPrimitive: React.ReactNode = 'string' || 0 || false || null || undefined || <div /> || <MyComponent />;
const Component = ({ children: React.ReactNode }) => { ... };

// React.CSSProperties
const styles: React.CSSProperties = { flexDirection: 'row', color: 'red' };
const element = <div style={styles}></div>

// React.ReactEventHandler<E>

const handleChange: React.ReactEventHandler<HTMLInputElement> = (ev) => { ... };
const element = <input onChange={handleChange} />

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

文章
评论
26 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文