React笔记

背景

挖坑看了vue后准备看一下react,是国外最火的前端框架.

介绍

截止目前,国际上使用量最大的前端框架.
2010年Facebook开源了PHP的语法扩展XHP,支持直接写html代码
2011年,员工Jordan Walke创建了FaxJS,是React的早期原型.
2011年用于newsfeed,2012年用于Instagram,2013年开源.

特点:

  1. 虚拟DOM的使用使其性能好
  2. 专注与使用JSX实现UI层面的功能,因此小而美.其他功能(路由,网络请求等)不在范围内.
  3. 概念简单,没有约定的根组件,没有指令,没有过滤器,数据绑定也是单向的,没有强调事件接口,非常自然.
  4. 逻辑简单,官方倡导使用不可变性简化开发,同时也提出了一套开发哲学,这是其他框架没有的.

初看感受

  1. 借助JSX来写html代码,一切特性我都已经知道,这才是真正的亲切
    1. 语法靠近html,同时修改了html的不合理之处
    2. 借助原生的js写法,可以顺利完成其他框架借助指令才能完成的功能.
    3. 让人怀念使用anko layout来写安卓界面的日子,用同一种语言就能做所有事情,而不必专门为xml配置一套IDE配置
  2. 使用不可变性质来完成数据的变更检测,历史回朔,想法挺好.
    以前觉得数据的回朔是个问题,什么时候变更了什么地方,
    现在在react中,检索代码可能会更加方便.
  3. 但为什么不支持typescript呢,明明挺好的.还有rxjs呢.
    1. 可能是嫌弃rxjs的写法太不通俗
  4. react的官方教程简单易懂,信息文字比大.
    其哲学更是给人一种王侯将相宁有种乎,没有天然约定的上下级关系的感觉.
    这些都比vue不知道好到那里去了.
    可惜在浅尝则止,许多想知道的用法并没有说.就像看了一段 利维坦.
    也让人摸不着头脑,接下来该看些什么.

hello-world

需要先安装 create-react-app 包.
然后使用命令

1
2
3
4
5
6
7
8
npx create-react-app my-app
cd my-app

# 运行
npm start # 使用既有的方式来实现功能,熟悉的才叫亲切的,vue算个什么

# 打包
npm run build # 在build文件夹下生成部署用的代码

生成的文件结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html # 空白的首页
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── README.md
├── src
│ ├── App.css # 对应的css
│ ├── App.js # 组件1
│ ├── App.test.js # 测试用
│ ├── index.css
│ ├── index.js # 引导用
│ ├── logo.svg
│ ├── reportWebVitals.js
│ └── setupTests.js
└── yarn.lock # fackbook的项目偏爱yarn来管理

组件和引导

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// react的基础单元依然是组件.而且使用了ES6的class来定义.
// 引用时只需要和其他框架一样引用即可.<Welcome name="Sara" />
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
otherMethod(){} // 可以有其他属于组件的方法
}

// 如果一个组件太过简单,只有render函数,则可以简化成function的形式存在
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}

// 基础的引导方法就是让App组件(当然也可以叫别的)
// 渲染到index.html上的root元素上
ReactDOM.render(
<App />,
document.getElementById('root')
);

而index.html可以如下

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html lang="en">
<head>
<title>React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

组件基础

定义和使用

定义用class,使用直接用类名作selector,不知道比vue规范多少.

使用class定义

1
2
3
4
5
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}

或者使用function形式

1
2
3
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}

使用

1
const element = <Welcome name="Sara" />;

render函数定义模板

就像上面说的那样,通常一个组件的render函数只会执行一次.
之所以数据能够更新,是因为使用了下面的state.

props来传递数据和方法

事实上从function形式的组件就能看出来,props是外部调用组件时指定的非常重要的参数.
相比其他框架,react的props的特点有:

  1. 简单,直接使用 this.props.xxx 即可,不需要专门声明为 @Inputprops:
  2. 只读,能变化的那个叫state

另外react还提供了一个哲学:
当一个组件接收的外来数据过多,说明难以复用,需要考虑拆分成多个组件了.

state来保存组件中的状态

如果不想从外部传入变量,则需要在内部的一个地方保存,
本来直接以类属性存放是可以的,
但为了保持丰富的响应式特性,专门放在约定的 this.state 当中.
state本身是一个对象,里面会存放许多东西.
使用时只需要使用 this.state.xxx, 比vue那种 .value 时而可省时而不可的规定省心多了.
不知道比vue规范多少.

  1. state的更新

    如果强行更新数据,可以,但不会重新渲染组件

    1
    this.state.comment = "hello";

    需要使用 setState 来触发组件更新.

    1
    this.setState({comment: "hello"});
  2. 更新时考虑异步

    不要依赖state的值来更新state,因为state的更新可能是异步的,导致现在拿到的state不是真正想要拿到的state

    1
    2
    3
    this.setState({
    counter: this.state.counter + this.props.increment,
    });

    非要实现这个功能的话,可以使用函数作为 setState 的参数.

    1
    2
    3
    this.setState((state, props) => ({
    counter: state.counter + props.increment
    }));
  3. state更新时可以只更新一部分

    如果state里面有两个字段,更新时可以只更新一个,另外一个会不变的.
    这样也会比较合理,防止产生太多的重复代码.
    这种做法称为 浅合并

  4. 与props的关系

    组件对外交流全用props,
    这样在外部组件看来,该组件是无状态的,逻辑上更简单明确,控制起来更方便.

    如果两个组件需要交流信息,通常一个保持响应性的方法是

    1
    子组件props <= 父组件state

    这样子组件的样子就会根据父组件的state改变而改变.

constructor用于处理props和声明state

参数和function形式的组件一样,是props.
里面必须使用 super(props).
然后可以定义一些state的内容.

生命周期函数

目前已知的生命周期函数,数量稀少,非常简单

  • componentDidMount 加载后
  • componentWillUnmount 卸载前

尽管在命名上,可能 mounted, onDistroy, preUnmount 等更直白一些,
但React这样的命名更加规范,整齐.

用户自定义函数

放在class里就好了

完整例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Clock extends React.Component {
// 类里的这些函数竟然不需要使用function来声明
constructor(props) {
super(props);
this.state = {date: new Date()};
}

componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}

componentWillUnmount() {
clearInterval(this.timerID);
}

tick() {
this.setState({
date: new Date()
});
}

render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}

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

JSX与模板基础

JSX基础

js的一种语法扩展.

  1. 语法像html,一定程度上做了兼容以让用户习惯.另外也修改了html里面许多不人性化的地方.变得更好
    (class => className,tabindex => tabIndex)

  2. 本质上还是js,可以直接给变量赋值为JSX元素.

    1
    const element = <h1>Hello World</h1>;
  3. 天然的防注入攻击,比html好许多.vue似乎提都没提.

有一些规定

  1. 标签没有内容可以随时用 “/>” 关闭标签
  2. 多行时最用用()括起来,以免受自动加分号的影响.

原理上:
最终会翻译成 React.createElement 函数.

元素渲染

  1. JSX最终会转换成 createElement 函数,不使用JSX则需要使用较为麻烦的API.

  2. 元素一经创建,不可更改.如果要更改页面上某个元素的UI,
    唯一办法只有创建一个全新的元素,并用 ReactDOM.render() 来重新绘制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function tick() {
    const element = (
    <div>
    <h1>Hello, world!</h1>
    <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
    );
    ReactDOM.render(element, document.getElementById('root')); // 需要不断用render来重新绘制
    }

    setInterval(tick, 1000);
  3. 在更新数据时,react会设法计算出修改的最小限度,尽量不修改整个页面.

变量绑定

字面量用引号,变量用大括号,没有话里胡哨的例外,不知道比vue规范多少

1
2
const element = <div tabIndex="0"></div>;
const element = <img src={user.avatarUrl}></img>;

大括号中也可以使用各种表达式,xxx,2+2,f(x).

事件处理

  1. 基本使用

    JSX仿照了html的原生方法,依然使用onclick等,
    不同点在于

    1. 对html语法做了改进,能够使用 onClick 等有大小写区分的方法.
    2. 不能通过返回false来阻止默认行为,因为这样语意不明显.需要手动调用 preventDefault
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function ActionLink() {
    function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
    }

    return (
    <a href="#" onClick={handleClick}>
    Click me
    </a>
    );
    }

    如代码所示,通常处理事件的函数会命名成 handleXXX.但没有这种硬性规定.
    由于是完全在js里写内容,处理事件时不需要考虑

    1. 传的是字面量还是变量
    2. 如果是变量该如何绑定,使用什么指令,圆括号还是方括号.
  2. class中this的问题

    另外一个常用的场景: class形式的组件中,通常会遇到 this 的问题,
    通常有两种解决方法

    1. 使用bind

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      class Toggle extends React.Component {
      constructor(props) {
      super(props);
      this.state = {isToggleOn: true};

      // 首先绑定一下
      this.handleClick = this.handleClick.bind(this);
      }

      handleClick() {
      this.setState(state => ({ // 这里的this才能有意义
      isToggleOn: !state.isToggleOn
      }));
      }

      render() {
      return (
      <button onClick={this.handleClick}> // 正常的使用,bind和不bind都不影响这里写成神什么样子
      {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
      );
      }
      }
    2. 将函数定义为类的public成员(而不是一个单独定义的function),当然这需要使用箭头函数.
      这样也产生了两种做法

      1. 比较先进,使用时也优雅,但要求开启public class fields语法,create-react-app默认会启用

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        class LoggingButton extends React.Component {
        handleClick = () => { // handleClick是一个箭头函数,而不是function定义的函数
        console.log('this is:', this);
        }

        render() {
        return (
        <button onClick={this.handleClick}> // 使用时不受任何影响,就正常使用
        Click me
        </button>
        );
        }
        }
      2. 如果没有public类成员,只能变通一下

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        class LoggingButton extends React.Component {
        handleClick() {
        console.log('this is:', this);
        }

        render() {
        return (
        <button onClick={() => this.handleClick()}> // 虽然影响到了使用,但还是可以将就一下
        Click me
        </button>
        );
        }
        }
  3. 传递参数

    由于解决this上有两种方案,传递参数时也有了两种方案

    1
    2
    <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
    <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

    值得注意的是事件参数 e 是一个合成事件,不必担心跨浏览器和跨平台的问题.

if

利用简单的if表达式可以写出众多逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}


if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}

如果嫌麻烦,还可以利用与运算符的特性

  1. 如果前面true,一定会返回后面的值
  2. 如果前面是false,后面会跳过.但这造成渲染的内容不正确,需要小小处理一下
1
2
3
4
5
6
7
8
render() {
const count = 0;
return (
<div>
{ count && <h1>Messages: {count}</h1>}
</div>
);
}

这里的代码相当于,如果count不为0,则渲染成h1元素.
但如果count为0则会渲染一个朴素的0.
这种情况下可以使用条件表达式

1
2
3
4
5
6
7
8
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
}

条件表达式算得上是一个比较折衷的办法,也能代替上面的if

1
2
3
4
5
6
7
8
9
10
11
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn
? <LogoutButton onClick={this.handleLogoutClick} />
: <LoginButton onClick={this.handleLoginClick} />
}
</div>
);
}

一个很常见的需求是,如果 msg.warn 为空,则不渲染这个div.
那么也可以通过null来告诉react不要渲染

1
2
3
4
5
6
7
8
9
10
11
function WarningBanner(props) {
if (!props.warn) {
return null;
}

return (
<div className="warning">
Warning!
</div>
);
}

循环

循环就不能用for来多次return了,可以用map.
这非常js也非常html,总之比 *ngForv-for 都要自然.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);

有循环就要有key.用来指定排序依据.
map可以使用的也不仅仅有值,还可以有index.
于是就能在循环中使用index作为key.

1
2
3
4
5
const todoItems = todos.map((todo, index) =>
<li key={index}>
{todo.text}
</li>
);

当然用index作为key很不好.而且是作为最后才用的一种手段.
通常都会用一些id什么的,最次也是toString.

1
2
3
4
5
6
7
8
9
10
11
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);

const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);

最后注意key是传递给react的而没有传递给组件
意味着下面的Post组件,无法使用 this.props.key 获取内容.

1
2
3
4
5
const content = posts.map((post) =>
<Post
key={post.id}
title={post.title} />
);

如果想要获取,需要手动以其他名义传一次

1
2
3
4
5
6
const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title} />
);

表单

react提供了一个基本的表单,概念上非常简单:
以state为唯一数据源

  1. 自定义函数来响应用户的输入
  2. 响应用户输入时需要更新state
  3. 以state内容更新显示内容
  4. 由于以state为唯一内容,也推荐直接以定值作为表单的内容(输入定值会导致无法编辑)

以这种方式建立的组件也称为 受控组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};

this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}

handleChange(event) { // 通常只用来更新数据
this.setState({value: event.target.value});
}

handleSubmit(event) { // 可以在这里校验数据
alert('提交的名字: ' + this.state.value);
event.preventDefault();
}

render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
名字:
<input type="text"
value={this.state.value} // state只用于显示
onChange={this.handleChange} /> // 管理输入使用几乎原生的事件
</label>
<input type="submit" value="提交" />
</form>
);
}
}

但是这显然无法应对大量的表单元素,因为这意味着数量巨大的event handler.
因此也有着破例的非受控组件,以后再说.

另外JSX对于html的语法做了一些改进,让不同的元素,在使用时能够接近普通的input

  1. textarea

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 定义
    this.state = {
    value: '请撰写一篇关于你喜欢的 DOM 元素的文章.'
    };

    // 使用
    <textarea value={this.state.value} onChange={this.handleChange} />

    // 更新,后略
    handleChange(event) {
    this.setState({value: event.target.value});
    }

    这样textarea能够看起来像个input

  2. select

    原本的html里面,select是没有值的,获取被选取的值也不方便,靠一个selected标记

    1
    2
    3
    4
    5
    6
    <select>
    <option value="grapefruit">葡萄柚</option>
    <option value="lime">酸橙</option>
    <option selected value="coconut">椰子</option>
    <option value="mango">芒果</option>
    </select>

    但JSX做了修改,selected直接用value保存被选项的值即可

    1
    2
    3
    4
    5
    6
    <select value={this.state.value} onChange={this.handleChange}>
    <option value="grapefruit">葡萄柚</option>
    <option value="lime">酸橙</option>
    <option value="coconut">椰子</option>
    <option value="mango">芒果</option>
    </select>

    如果value为数组,则表示还可以多选

    1
    <select multiple={true} value={['B', 'C']} />
  3. 文件input

    对于html的文件input

    1
    <input type="file" />

    react暂时没有办法以受控组件的形式使用,以后再说

  4. 多个输入

    当有多个输入时,为了让onChange能够更好地区分,可以使用 name 属性标记,
    然后使用 event.target.name 获取.
    这样的一个好处在于,可以一定程度上减少重复写onChange的处理函数.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    class Reservation extends React.Component {
    // ...

    handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name; // 获取name

    this.setState({
    [name]: value // 便于批量设置响应函数
    });
    }

    render() {
    return (
    <form>
    <label>
    参与:
    <input name="isGoing" 略 onChange={this.handleInputChange} /> // 使用name打标记
    </label>
    <br />
    <label>
    来宾人数:
    <input name="numberOfGuests" 略 onChange={this.handleInputChange} />
    </label>
    </form>
    );
    }
    }

组合而非继承

设想已有一个Dialog组件,想建立一个显示warning的Dialog窗口,可以有两种方法

  1. 新的WarningDialog组件继承Dialog组件,然后重写render函数,为了方便可以把具体渲染过程拆分到用户自定义的函数中
  2. 组合,使用组件时再具体定义其外观,然后插入到模板当中.相当于外框和子元素组合成了新的元素.

React偏好第二种方式,可能的原因是js没有特别规范的继承理念.
具体方法是:

  1. 在render函数中使用 props.children 表示组件使用时的子元素
  2. 在使用组件时直接在组件的子元素位置写内容
1
2
3
4
5
6
7
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}

使用时

1
2
3
4
5
6
7
8
9
10
11
12
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}

虽然使用了类似slot的概念,但即使没有 props.children,
但react依然可以使用 props.xxx 来完成内容的注入和组合.
尤其是当希望使用多个slot的时候,就可以使用这种原始一些的方式来做.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left} // 使用普通的props,而不是特殊的props.children
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}

function App() {
return (
<SplitPane
left={ // 使用props而不是子元素指定内容
<Contacts />
}
right={ // 使用props而不是子元素指定内容
<Chat />
} />
);
}

这种尽量不制造特殊变量(比如 this.slot)的方式深得我心.

方法论和哲学

状态提升

开发中通常会形成形式上的父子组件结构.
有时为了两个子组件的通信,或者是对子组件的state进行统一管理.
就需要将数据从子组件的state拿到父组件的state当中,
而react希望props是只读的,因此原本在子组件之中操作数据的方法,也需要一起移动到父组件之中.
一个显而易见的结果是,子组件中不再使用 this.state.xxx 而使用 this.props.xxx.
即使换成了 this.props.xxx, 子组件依然有响应性.

子组件可以如下定义

1
<input value={this.props.temprature} onChange={this.props.onTemperatureChange} />

调用时可以使用

1
2
3
<TemperatureInput
temperature={this.state.temp}
onTemperatureChange={this.handleFahrenheitChange} />

此时可以看到,父子组件约定了一个 onTemperatureChange 的自定义事件.
而比较方便的是,不需要再声明为 @Output.

一般方法论

react提供了一种建立项目的方法论,称之为哲学.具体的步骤是

  1. 划分组件层级

    1. 有时可以按照美工的图层来直接划分组件
    2. 单一功能原则, 一个组件只提供一个功能
    3. UI结构可以和数据结构一一对应.

    比如一个显示搜索结果的可以分成5部分

    • 应用整体
      • 接受用户输入
      • 展示数据内容,负责数据过滤
        • 展示标题
        • 展示具体数据
  2. 创建一个没有数据的静态版本

    1. 简单软件通常使用自上而下
    2. 复杂软件通常需要自下而上(还要对下层的做好足够的测试)
  3. 确定state内容

    1. 也叫state的最小完整表示
    2. 需要参考 DRY原则 (Don’t Repeat Yourself)
    3. 确定数据是state还是props,通常的判断方法是
      1. 如果从外部传递而来,不是state
      2. 如果可以随着时间推移而不变,则不是state
      3. 如果可以由其他数据计算得出,则不是state,比如有整体数据,就不要再另外获得一次过滤后数据了,自行计算即可
  4. 确定state的存放位置

    1. 先找到一个使用该state数据的所有组件
    2. 这些所有组件如果有一个共同的父级,则state由父级保存
    3. 如果实在找不到,可以直接建立一个父级组件用来保存数据
  5. 添加反向数据流

    1. 即为没能直接操作state的组件,建立获取父级处理逻辑的通道.
    2. 同时也让下级的组件暴露一些函数接口,然父级方便将处理逻辑下放

一些其他问题

  1. 表单的验证是如何做的
    需要使用外部库,有许多东西

    1. Formik github 27k 官方教程似乎也推荐了一下,怕是给了广告费啊,明明和react哲学不接近
    2. react-hook-form 看起来更接近原生 20.2k

    功能上大同小异,写法上萝卜青菜,不过我关心哪一个在哲学上和react更接近,
    也不知道谁能回答这个问题

  2. http请求是如何做的
    官方推荐了好多

    1. Axios
    2. jQuery AJAX 看着就老
    3. window.fetch 不支持取消请求,查看进度…

    Axios虽然是基于promise的,但如果喜欢,可以使用rxjs进行封装,毕竟rxjs的作用不仅仅是进行网络请求.
    目前默认就是rxjs风格的请求库恐怕只有angular自带的库了.

    那么有人做axios的rxjs包装工作吗

  3. 如何进行路由,路由守卫靠什么?
    好在react的路由库只有一个,react-router

  4. 如何测试
    通常是使用Jest

  5. 如何快速搭建UI
    material UI依然可用,但就是不知道怎么用

为什么源码没有用TypeScript

参照知乎的说法,

  1. 曾经是超前的,只不过现在不是主流,何必要移动过来
  2. 就算不如主流的好,移动过来也有风险
  3. TypeScript会全部读到内存处理,而Fackbook的代码又多到不能全部读取到内存中.

参考

  1. 官方文档
  2. 框架对比
  3. 源码没有用TypeScript