[关闭]
@gyyin 2019-11-24T17:06:29.000000Z 字数 12294 阅读 1162

React 语法入门(上)

慕课专栏


React 背景

React 最早是 Facebook 的内部项目,当时 Facebook 对市场上的 MVC 框架都不满意,于是自己做了一套新的框架。
在 React 诞生之前,Facebook 内部已经有了 React 的雏形项目。在2010年,Facebook 开源了 XHP 项目,支持在 PHP 中书写模板,这种模板语法和现在 React 中的 JSX 非常相似。

image_1dkb8tqnq5u71g8fmr9u599u49.png-51.3kB

2011年,jordwalke 开源了一个名为 FaxJS 的项目,支持组件化、服务端渲染等等,这是 React 最初的灵感之一。
现在 FaxJS 的 GitHub 上已经建议开发者去使用 React。

This is an old, experimental project: React is much better in every way and you should use that instead. This project will remain on Github for historical context.

2013年,Facebook 将 React 开源,虽然刚开始很多开发者认为这是一种倒退,但随着越来越多人使用,React 获得了越来越多的肯定,现在已经是最流行的前端框架之一。

React 的优势

相比传统的 jQuery 和原生 JS,React 带来了数据驱动、组件化的思想。

组件化

前面在讲解模块化的时候,有提到过组件这个概念。如果将应用看做一个玩具,那么组件就是组成这个玩具的一块块积木。

image_1dqcgheqlf23dqea0o1mc6r891g.png-37.2kB

在现代化的前端框架之前,也有过一些组件化的思想,比如 jQuery 丰富的插件。但这些大都不够彻底,都只是对逻辑层的封装,很多插件都还需要用户配合写要求格式的 HTML 结构。
举个简单的例子,比如有这么一个 Slider 插件,如果想要使用它,就必须按照插件规定的 HTML 格式来编写,不然它就无法在插件内部准确地获取到 DOM。

  1. <div class="slider">
  2. <div class="slider-item"></div>
  3. <div class="slider-item"></div>
  4. <div class="slider-item"></div>
  5. </div>
  6. $(".slider").start({
  7. showDot: true,
  8. animate: true
  9. })

React 中提出了 JSX,可以将 HTML 放到 JS 中编写,将 UI 和 逻辑都封装到了组件中,这样做到了更彻底的组件化。

数据驱动

在 jQuery 中,用户点击了某个 DOM-A 元素,来修改另一个 DOM-B 中相应的内容。
一般的做法是对这个 DOM-A 绑定事件,之后在事件里面用 $ 来查询获取到 DOM-B,然后修改 DOM-B 的 innerHTML 值。

  1. $("DOM-A").click(function() {
  2. $("DOM-B").html('xxx');
  3. })

只是这样的确比较简单,如果不仅仅要修改 DOM-B 呢?还要同时修改 DOM-C、DOM-D等等呢?

  1. $("DOM-A").click(function() {
  2. $("DOM-B").html('xxx');
  3. $("DOM-C").html('xxx');
  4. $("DOM-D").html('xxx');
  5. })

如果修改 DOM-B 的来源不仅仅是点击 DOM-A 呢?也可能是点击了 DOM-C 和 DOM-D,这就会让应用中的数据流动很不清晰。

image_1dqcg1g96hc49ku1no31tim1o5r13.png-30.8kB

除此之外,频繁的 DOM 操作会让性能变得比较低,尤其是涉及到 重排 的时候。
React 中则采用了数据驱动的思想,那就是我能不能把这个页面中渲染的数据放到某个地方来管理呢?
只要我修改了这个数据,就会引发页面的重新渲染,这样就不需要直接修改页面。
React 中提供了 state 来管理这些数据。可以参考下面这个 Toggle 组件:

  1. // 只要点击 div 就会修改 state.show 的值,从而触发重新渲染
  2. class Toggle extends React.Component {
  3. state = {
  4. show: false
  5. }
  6. toggle() {
  7. this.setState({
  8. show: !this.state.show
  9. })
  10. }
  11. render() {
  12. <div class="toggle">
  13. <div class="notice">click for toggle</div>
  14. <span>{this.state.show}</span>
  15. </div>
  16. }
  17. }

准备运行环境

讲了这么多,那么接下来开始进入正题。
在开始开发 React 之前,这里提供了几种方式来运行 React 应用。

  1. Babel REPL
    可以直接在 Babel REPL 中编写代码,实时预览 babel 编译后的结果。

  2. codesandbox
    codesandbox 是一个在线网站,可以直接创建 React/Vue/Typescript 等项目,不需要自己去配置 babel。

  3. 本地(推荐)
    由于 JSX 语法并非浏览器原生支持,因此我们除了引入 react 和 react-dom 两个库之外,还需要引入 babel 来将 JSX 编译为浏览器可以运行的 javascript。
    所以只需要在 html 文件中引入下面这三个 script 标签,并且将 script type 设置为 text/babel 就可以直接在本地编写 React 组件了。
  1. <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  2. <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
  3. <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
  4. <script type="text/babel">
  5. const Header = () => <h1>hello, world</h1>
  6. ReactDOM.render(
  7. <Header />,
  8. document.querySelector("#app")
  9. )
  10. </script>

ReactDOM.render

一般编写 React 的应用,除了需要引入 react 库之外,还需要引入 react-dom 这个库。react-dom 是 react剥离出的涉及 DOM 操作的部分。
react-dom 中最常用的 API 就是 render,在将项目的根组件插入到真实 DOM 节点中时常常要用到。这一步是必要的,不然你的 React 应用就无法挂载到浏览器的 DOM 中。

  1. // index.html
  2. <body>
  3. <div id="app"></div>
  4. </body>
  5. // App.jsx
  6. const App = () => <h1>hello, world</h1>
  7. ReactDOM.render(
  8. <App />,
  9. document.querySelector("#app")
  10. )

JSX

JSX 就是 JavaScript XML 的缩写,遵循 XML 的语法规则,语法格式很像模板,但是由 Javascript 实现的。
编写 React 组件的时候,文件名常常以 .jsx 结尾,首字母保持大写。这是为了和原生的 JS 做区分,让别人一眼就看出来这个文件是一个 React 组件。
当然也可以直接用 .js 结尾的文件,这里全部统一为以 .jsx 结尾且首字母大写的格式作为标准。

  1. + components
  2. - Header.jsx
  3. - Footer.jsx
  4. - Button.jsx
  5. - index.js

jsx语法

jsx 语法类似我们熟悉的 html,允许我们使用尖括号语法来描述组件。

  1. const App = () => {
  2. return (
  3. <div id="app">
  4. <Header />
  5. <div className="body">
  6. </div>
  7. <Footer />
  8. </div>
  9. )
  10. }

这里的 div 就是普通的 html 标签,Header 和 Footer 则是 React 组件。
在 React 中,使用一个组件的时候,可以使用自闭合,也可以使用标签闭合,这点儿和 html 依然类似。

  1. // 对于一个Header组件来说,怎样闭合都可以
  2. <Header />
  3. <Header></Header>

由于 jsx 本质上也是 js,所以保留字 class 和 for 无法在 jsx 直接使用,需要用 className 和 htmlFor 来代替。例如:

  1. const Header = () => <h1 className="header"></h1>
  2. const App = () => {
  3. return (
  4. <div>
  5. <input type="text" />
  6. <label htmlFor=""></label>
  7. </div>
  8. )
  9. }

在 jsx 中使用变量的时候,不管是在属性上面还是元素节点里面,都需要用花括号来包裹。如果不用花括号,就一律当做原始类型的值来处理。

  1. // good
  2. const App = () => {
  3. const text = 'hello, world'
  4. return (
  5. <h1>{ text }</h1> // 输出hello, world
  6. )
  7. }
  8. // bad
  9. const App = () => {
  10. const text = 'hello, world'
  11. return (
  12. <h1> text </h1> // 输出text
  13. )
  14. }

花括号中不仅可以存放变量,甚至还可以存放一段 js 表达式。

  1. // 根据number的值来渲染不同的文本
  2. const App = (props) => {
  3. const number = 100;
  4. return (
  5. <h1>{ number > 100 ? 'big' : 'small' }</h1>
  6. )
  7. }

在 jsx 中想要实现 if...else 和 for 循环也很简单,只要在花括号里面写入表达式就行了。
在 jsx 中可以使用三目运算符来代替 if...else。

  1. const App = () => {
  2. const isShowButton = false;
  3. return (
  4. <div id="app">
  5. {isShowButton ? <button></button> : null}
  6. </div>
  7. )
  8. }

如果想输出一个列表,那么可以在 jsx 中使用 map 函数。但要切记,当使用循环输出列表的时候,需要给子项设置 key。至于 key 的作用是什么,后面我们会讲到。

  1. const List = () => {
  2. const list = [1, 2, 3, 4, 5]
  3. return (
  4. <ul>
  5. {
  6. list.map((item, i) => {
  7. return <li key={i}>{item}</li>
  8. })
  9. }
  10. </ul>
  11. )
  12. }

如果你想直接设置元素的 style 属性,那么 jsx 要求传给 style 一个对象,这个对象里面的属性和原生 DOM 保持一致,规定为必须是驼峰式的。

  1. // 我们将对象styles传给了style属性,styles里面设置了背景颜色background-color
  2. const Button = (props) => {
  3. const styles = {
  4. backgroundColor: props.color
  5. }
  6. return (
  7. <button style={styles}>{ props.text }</button>
  8. )
  9. }

由于 jsx 本质上也是 js,因此在 jsx 里面也可以直接使用 js 的注释。但是要注意,这里一般只能用 /* */ 这种注释。

  1. const App = () => {
  2. return (
  3. <p>
  4. {/* 单行注释 */}
  5. {
  6. /*
  7. 多行注释符号
  8. 多行注释符号
  9. */
  10. }
  11. </p>
  12. )
  13. }

组件

如果你以前有用过 jQuery 的插件,那么一定对这种结构很清楚吧。

  1. function Employee ( name, position, salary, office ) {
  2. this.name = name;
  3. this.position = position;
  4. this.salary = salary;
  5. this._office = office;
  6. this.office = function () {
  7. return this._office;
  8. }
  9. };
  10. $('#example').DataTable( {
  11. data: [
  12. new Employee( "Tiger Nixon", "System Architect", "$3,120", "Edinburgh" ),
  13. new Employee( "Garrett Winters", "Director", "$5,300", "Edinburgh" )
  14. ],
  15. columns: [
  16. { data: 'name' },
  17. { data: 'salary' },
  18. { data: 'office()' },
  19. { data: 'position' }
  20. ]
  21. } );

这是 jQuery DataTable 官网的一个使用例子。DataTable 方法接收了一个对象,这个对象包括 data 和 columns 属性,这两个属性分别代表了这个表格要展示的数据和表格的头部。
这个 DataTable 就是一个组件,这个组件接收了一些数据,输出了对应的界面。

image_1dkq2tlreuhegvhe9bp9bihk9.png-32.1kB

组件化的作用是为了复用相同的界面。比如我们每个页面中都有一个 button 按钮,但每个按钮的颜色都不一样,如果我们在每个页面都编写一个按钮,那么就得不偿失。这时候就应该将按钮封装成一个 Button 组件,根据传入的颜色来展示不同的按钮。

组件语法

一般来说,React 中的组件有两种,一种是原生组件,即用原生 HTML 标签的组件,一种 React 自定义组件。
原生组件:

  1. const Header = () => <header>hello, world</header> // header就是原生组件

自定义组件:

  1. const App = () => <Header></Header> // 上面创建的 Header 组件就是 React 自定义组件

同时,React 的每个组件必须要有一个根节点,如果有多个根节点就会导致报错,这是因为 React 在创建虚拟 DOM 树的时候,需要有个根节点。
因此,经常会出现不得不在多个 div 外面再套一个 div 的情况,有时候就会使得样式错乱。
所幸的是,React16 之后可以用 React.Fragment 和 <></> 来代替。

  1. // bad
  2. const App = () => {
  3. return (
  4. <p></p>
  5. <p></p>
  6. )
  7. }
  8. // good
  9. const App = () => {
  10. return (
  11. <div>
  12. <p></p>
  13. <p></p>
  14. </div>
  15. )
  16. }
  17. // good
  18. const App = () => {
  19. return (
  20. <>
  21. <p></p>
  22. <p></p>
  23. </>
  24. )
  25. }
  26. // good
  27. const App = () => {
  28. return (
  29. <React.Fragment>
  30. <p></p>
  31. <p></p>
  32. </React.Fragment>
  33. )
  34. }

函数组件与类组件

在 React 中,组件的定义方式有两种。一种是纯函数组件,这种组件本质上是一个函数,它接受一个 props,返回一个 view,只是单纯负责展示的,像 Button 就属于这种组件。

  1. // Button组件接收了一个props,返回了对应的button按钮
  2. const Button = (props) => {
  3. return <button>{ props.text }</button>
  4. }

而另一种组件是类组件,在类组件中会提供更多的功能,比如 state 和生命周期,这种组件允许在内部控制状态,像轮播图、选项卡就属于这种组件。
定义函数组件的时候需要继承 React.Component 这个类。

  1. class Calculation extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. this.state = {
  5. count: 0
  6. }
  7. }
  8. handlePlus = () => {
  9. this.setState({
  10. count: this.state.count + 1
  11. })
  12. }
  13. handleMinus = () => {
  14. this.setState({
  15. count: this.state.count - 1
  16. })
  17. }
  18. render() {
  19. return (
  20. <div className="calculation">
  21. <button onClick={this.handlePlus}>+</button>
  22. <span>{this.state.count}</span>
  23. <button onClick={this.handleMinus}>-</button>
  24. </div>
  25. )
  26. }
  27. }

可以看下效果:
react入门.gif-86.9kB

我们来一步步去讲解这个例子。
首先,我们在 Calculation 的构造函数中,手动用 super 调用了 React.Component 这个构造函数并传入了 props,这一步是为了初始化组件中的 props。
其次,在构造函数中定义了 state,react 中的 state 是一个对象。在 render 的时候将 this.state.count 展示了出来。
这里对加减两个符号绑定了点击事件。首先要注意,这里的 handlePlushandleMinus 是用箭头函数定义的,这是为了避免 handlePlushandleMinus 中的 this 丢失。
当然,也可以不用箭头函数来定义 handlePlus 和 handleMinus,这里还有其他几种方法,比如使用 bind 函数。

  1. class Calculation extends React.Component {
  2. constructor(props) {
  3. this.handlePlus = this.handlePlus.bind(this)
  4. this.handleMinus = this.handleMinus.bind(this)
  5. }
  6. handlePlus() {
  7. }
  8. handleMinus() {
  9. }
  10. render() {
  11. }
  12. }

还可以使用双冒号的语法,双冒号相当于 bind,但无法传值。

  1. class Calculation extends React.Component {
  2. constructor(props) {
  3. }
  4. handlePlus() {
  5. }
  6. handleMinus() {
  7. }
  8. render() {
  9. return (
  10. <div className="calculation">
  11. <button onClick={::this.handlePlus}>+</button>
  12. <span>{this.state.count}</span>
  13. <button onClick={::this.handleMinus}>-</button>
  14. </div>
  15. )
  16. }
  17. }

其次,这里通过 this.setState 方法修改了 state 的值。在 React 中,state 的值无法直接修改,只能通过 setState 来修改。
setState 接收一个新的对象或者函数(函数也要返回一个新的对象),最终会合并到现在的 state 中,这一个过程类似 Object.assign(this.state, newState)
在我们每次修改 state 的时候,都会重新执行一次 render 方法,将组件进行重新渲染,因此我们看到每次展示出来的都是最新的 state。

props

上面我们举了 jQuery-DataTable 的例子来说明什么是组件。DataTable 接收了 data 和 columns 两个数据,返回了一个表格。
因此,在 React 的设计理念中,view = f(data)。我们给组件传入对应的数据,最后组件返回了我们想要展示出来的 view。
在 React 中,组件也是接收数据,返回一个 view,只不过这个数据叫 props。
我们来设计一个 Button 组件,这个组件可以根据传入的 props 来展示不同的文字和颜色。

  1. const Button = (props) => {
  2. const styles = {
  3. backgroundColor: props.bgColor
  4. }
  5. return (
  6. <button style={styles}>
  7. {props.text}
  8. </button>
  9. )
  10. }

那么我们在调用的时候可以直接给Button组件传值。

  1. const App = () => {
  2. return <Button bgColor="red" text="submit"></Button>
  3. }

在组件内部通过 props 来拿到外部传给这个组件的值,比如上面的 bgColor 值为 'red' 是 App 组件从外部传给 Button 组件的,Button 组件在内部通过 props.bgColor 又拿到了这个背景颜色。
props 除了可以传递值,也可以传递对象、数组甚至函数等等。
比如我们想给 Button 组件绑定点击事件,允许我们在外部调用,那就可以把事先写好的 onClick 函数传入。在点击 button 按钮的时候,会去执行这个 onClick 函数。

  1. const Button = (props) => {
  2. return (
  3. <button style={{backgroundColor: props.color}} onClick={props.onClick}>
  4. {props.children}
  5. </button>
  6. )
  7. }
  8. const App = () => {
  9. const handleClick = (event) => {
  10. console.log(event.target);
  11. }
  12. return <Button color="red" onClick={handleClick}>submit</Button>
  13. }

children

props 还提供了类似插槽的功能,我们可以在组件内部通过 props.children 获取到组件的子节点。这种渲染方式可以让我们设计出非常灵活的组件。

假设有个轮播图组件,你想它既能展示图片轮播,又能放入一个 div 展示文字,甚至你还想让它展示表格、视频等等,这个组件该怎么来设计呢?

考虑一下,如果只封装这个组件的各种切换状态,而里面的内容可以让使用者自行插入,这样是不是就很完美了?不管用户插入什么内容,我都会原封不动展示出来。

可以参照下方的这个 Slider:

  1. class Slider extends React.Component {
  2. render() {
  3. return (
  4. <div className="slider">{ this.props.children }</div>
  5. )
  6. }
  7. }
  8. // 调用方式
  9. <Slider>
  10. <img src="1.jpg" />
  11. <Video />
  12. <Table />
  13. </Slider>

最终,被 Slider 组件包裹着的三个组件最终会被渲染到 this.props.children 的位置。
最终渲染出来的结果如下:

  1. <div className="slider">
  2. <img src="1.jpg" />
  3. <Video />
  4. <Table />
  5. </div>

注意,props.children 有可能是 undefined(没有children),有可能是个对象(只有一个children),也有可能是个数组(有多个children)。这取决于传入的 children 数量。
因此,如果需要遍历 children 的时候,需要注意为另外两种值的可能性。直接遍历对象和 undefined 会导致报错。
React 中提供了 React.Children 这个 API,使用 React.Children.mapReact.Children.forEach 就可以直接遍历了。

  1. function App() {
  2. return (
  3. <Slider>
  4. <h1>hello</h1>
  5. <p>world</p>
  6. </Slider>
  7. );
  8. }
  9. function Slider(props) {
  10. if (!props.children) {
  11. return null;
  12. }
  13. return React.Children.map(props.children, child => {
  14. return React.cloneElement(child)
  15. })
  16. }

state

我们在前面讲解类组件的时候已经提到了 state,state 类似一个状态机,可以由一种状态转变为另一种状态。
关于状态机,最形象的理解就是马路上的红绿灯,每隔一段时间可以从红灯切换到绿灯,从绿灯再切换到黄灯,这就是一个典型的状态机。
如果想要深入理解状态机在 JS 中的应用,可以参照一下这篇文章:JavaScript与有限状态机
在 React 中,也是由于 state 的改变,从而引发了组件视图的重新渲染。

setState

在 React 中,state 的改变只能通过 setState 方法,setState 方法可以接收一个对象或者函数。
当 setState 接收一个对象的时候,那么最后会将当前组件内的 state 和这个对象做合并,返回的新对象作为当前组件内的 state。
以最开始的这个 Toggle 组件为例子。在每次点击执行 toggle 方法的时候,会调用 setState 方法。setState 接收一个对象,这个对象会和最开始的 this.state 对象做一个合并,这个合并类似 Object.assign
如果不考虑 PureComponent 和 shouldComponentUpdate,那么每次执行 setState 都会引发组件的重新渲染,即重新执行一遍 render 函数。

  1. // 只要点击 div 就会修改 state.show 的值,从而触发重新渲染
  2. class Toggle extends React.Component {
  3. state = {
  4. show: false
  5. }
  6. toggle() {
  7. this.setState({
  8. show: !this.state.show
  9. })
  10. }
  11. render() {
  12. <div class="toggle">
  13. <div class="notice">click for toggle</div>
  14. <span>{this.state.show}</span>
  15. </div>
  16. }
  17. }

当 setState 接收一个函数的时候,这个函数会返回一个参数,这个参数就是前一次的 state,最终函数会返回一个新的对象,也会将这个新对象和组件内的 state 做合并。

  1. this.setState(function(prevState) {
  2. return {
  3. ...prevState,
  4. count: prevState.count + 1
  5. }
  6. })

由于 setState 的设计是“异步”的,所以如果想立刻拿到更新后的值,最好是使用传入函数的形式。

关于 setState 的“异步”特性,在后面的文章中会进行深入的讲解。

生命周期

在 React 类组件中提供了丰富的生命周期,允许你在组件渲染、更新和卸载的时候执行某些操作。
这张图是 React16 之前的生命周期图,一共是三个阶段,分别是首次 mounting 阶段、updation 更新阶段、unmouting 卸载阶段。

image_1dkqrtcjp2hc10t51u0v10fn19j59.png-205.6kB

constructor

constructor 函数接收 props 和 context 两个参数并在组件中进行初始化。如果你想要在构造函数中使用 props 和 context,那么必须调用 super 并传入 props 和 context。
由于 React 组件也是个类,所以在渲染阶段会使用 new 操作符来实例化,这一步是在 React 中做的,我们不需要关心。

  1. class App extends React.Component {
  2. constructor(props, context) {
  3. super(props, context);
  4. console.log('props', this.props);
  5. console.log('context', this.context);
  6. }
  7. }

那有时候我们调用 super,什么都不传,在组件中依然能够拿到 this.props,这是为什么呢?
这是因为 React 在实例化组件的时候会重新设置一遍 props。

  1. const instance = new App(props);
  2. instance.props = props;

componentWillMount

在组件第一次渲染之前调用,如果在此时调用setState,将不会引发多次渲染。

componentDidMount

在组件渲染成功(插入到dom树中)调用,不是在组件 render 后就调用,而是当所有子组件都触发 render 之后才会被调用。
依赖 DOM 的操作都应该放在这里,如果需要通过网络请求获取数据,也应当放到这里。

  1. componentDidMount() {
  2. fetch('/getUserList').then(function(res) {
  3. return res.json();
  4. })
  5. }

componentWillReceiveProps

componentWillReceiveProps 会在组件接收新的 props 之前被调用(一般是更新阶段),参数中返回了新的 props。如果这个时候你有需要比较前后两次 props 后再决定更新 state 的操作,那么就可以在这里。

  1. componentWillReceiveProps(nextProps) {
  2. if (nextProps.count !== this.props.count) {
  3. this.setState({
  4. count: nextProps.count
  5. })
  6. }
  7. }

shouldComponentUpdate

这个函数是在组件更新之前调用的,接收新的 props 和新的 state,最终需要返回一个布尔类型的值,会根据返回的值来判断当前组件是否需要更新。
如果需要进行性能优化(防止无关组件渲染),那么就可以在此处进行处理。

  1. shouldComponentUpdate(nextProps, nextState) {
  2. // 如果返回true,那就是需要更新。如果返回了false,则组件不会进行更新。
  3. }

componentWillUpdate

这个函数是在组件将要更新之前调用,此时 shouldComponentUpdate 已经返回了true。
切记,在这里不能调用 setState,因为 setState 会造成组件更新,最终将造成死循环。

componentDidUpdate

这个函数是在组件更新之后调用,这个时候组件已经执行过了render 方法。
切记,在这里不能调用 setState,因为 setState 会造成组件更新,最终将造成死循环。

componentWillUnmount

componentWillUnmount 是在组件将要卸载时执行的,如果在 componentDidMount 中绑定了原生事件,那么就需要在这里进行解绑。

  1. componentDidMount() {
  2. window.addEventListener('scroll', this.scroll)
  3. }
  4. componentWillUnmount() {
  5. window.removeEventListener('scroll', this.scroll)
  6. }
  7. scroll() {}

在React 16之后,由于现有的 fiber 架构带来的异步渲染,导致了原有的部分生命周期不再适用,componentWillReceiveProps、componentWillMount、componentWillUpdate 三个生命周期将在 React17 移除。
关于新的生命周期,由于篇幅有限,将会放到下篇文章中进行讲解。

实战例子

总结

作为当前最火的前端框架之一,React 有着很多优秀的理念和设计。在掌握了基本语法之后,如何设计好的组件更需要我们去不断探索。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注