Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CRA 构建 React 与 TypeScript 开发环境 #2

Open
bouquetrender opened this issue May 9, 2018 · 0 comments
Open

CRA 构建 React 与 TypeScript 开发环境 #2

bouquetrender opened this issue May 9, 2018 · 0 comments
Labels

Comments

@bouquetrender
Copy link
Owner

bouquetrender commented May 9, 2018

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} />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant