一、简介

为什么需要React框架

和其他主流的前端框架一样,React框架也是为了构建大型的前端应用而生的。使用React框架开发应用可以避免操作大量dom操作,使用组件化方式管理应用,提升开发效率和代码可维护性。

React做了哪些工作

React提供组件来抽象界面交互单元,React会管理开发者开发的组件,根据组件中的数据计算实际要渲染的视图,并且当数据变化时候计算最小的改动,自动操作dom完成界面的变化。

当我们使用React开发web应用时候,我们在写什么

当我们使用React开发时候,我们就是在写React组件,组件的state即数据,组件的render方法返回的是视图,组件的声明周期钩子和事件的处理,是数据的变化逻辑。更准确地说是一颗组件树,根组件引用子组件,子组件可能还会引用子组件,形成一个组件树。组件树的结构和视图的盒子嵌套结构类似。

我们怎样使用React开发应用

  1. 将界面划分为组件

  2. 声明视图和数据的关系

  3. 实现数据的变化逻辑,即组件的实现

JSX


  • JSX是一个 JavaScript 的语法扩展,用来描述视图结构
  • JSX中混合了标记语言和js语法

    • 大括号中是js表达式

    • js表达式的值也可以是一个元素

  • JSX并不是必须,但是是React的最佳实践

二、框架基本使用

1. 脚手架的使用

https://create-react-app.dev/docs/getting-started/

  1. npx create-react-app my-app
  2. cd my-app
  3. npm start

src/index.js中,将组件渲染到指定的元素(”#root”)上

  1. import React from 'react';
  2. import ReactDOM from 'react-dom';
  3. import App from './App';
  4. ReactDOM.render(
  5. <React.StrictMode>
  6. <App />
  7. </React.StrictMode>,
  8. document.getElementById('root')
  9. );

在初始化文件src/index中可以看到引用了react和react-dom两个模块,

react模块负责react核心逻辑,react-dom负责dom操作,ReactDOM.render方法将组件挂在到指定的元素上。

src/App.js中是根组件。

2. 简单示例

  1. import React from 'react';
  2. import './App.css';
  3. class App extends React.Component {
  4. state = {
  5. digit: 1
  6. };
  7. onCounterClick = () => {
  8. this.setState({
  9. digit: Math.random()
  10. });
  11. };
  12. render() {
  13. return (
  14. <div onClick={this.onCounterClick}>digit:{this.state.digit}</div>
  15. );
  16. }
  17. }
  18. export default App;

声明一个React类组件,继承React.Component

该组件包含一个render方法,返回JSX(定义视图)

组件有个属性state,保存组件用到的数据

视图中标签上onClick属性注册点击回调,取值是类的一个成员方法。标签上的事件和原生的事件类似,注意是驼峰的。如onClick、onChange、onInput等

点击后通过”setState”改变状态,文本就相应地改变了

3. 视图

文本

在大括号中使用js表达式渲染文本

事件、class、style

  1. import React from 'react';
  2. import './App.css';
  3. class App extends React.Component {
  4. state = {
  5. digit: '1'
  6. };
  7. onButtonClick = e => {
  8. this.setState({
  9. digit: e.target.dataset.digit
  10. });
  11. };
  12. render() {
  13. return (
  14. <>
  15. <div>{this.state.digit}</div>
  16. <button
  17. className="btn-2"
  18. data-digit="1"
  19. onClick={this.onButtonClick}
  20. style={{fontSize: '16px', 'margin-right': 20}}
  21. >
  22. 点击显示 1
  23. </button>
  24. <button
  25. className="btn-2"
  26. data-digit="2"
  27. onClick={this.onButtonClick}
  28. >
  29. 点击显示 2
  30. </button>
  31. </>
  32. );
  33. }
  34. }
  35. export default App;

事件回调的参数是e,这里的e并不是原生的e,而是经过React包装过的e,但是和原生的e类似,大部分场景都可以按照原生的使用方法访问这个对象

元素属性和原生类似

为了不和关键字class混淆,JSX中给元素的类名复制要用”className”

style的值是一个对象,key是css属性,value是该属性的取值,注意,key可以是驼峰的(fontSize)也可以是原生形式的(’font-size’),如果是非法格式的对象key,需要加引号;取值可以是字符串(”16px”)也可以是数字,如果是数字,默认单位是px

条件渲染

  1. import React from 'react';
  2. import './App.css';
  3. class App extends React.Component {
  4. state = {
  5. digit: '1'
  6. };
  7. onButtonClick = e => {
  8. this.setState({
  9. digit: e.target.dataset.digit
  10. });
  11. };
  12. render() {
  13. const button = this.state.digit === '1'
  14. ? (
  15. <button
  16. data-digit="2"
  17. onClick={this.onButtonClick}
  18. >
  19. 点击显示 2
  20. </button>
  21. )
  22. : (
  23. <button
  24. data-digit="1"
  25. onClick={this.onButtonClick}
  26. >
  27. 点击显示 1
  28. </button>
  29. )
  30. return (
  31. <div>
  32. <div>{this.state.digit}</div>
  33. {button}
  34. </div>
  35. );
  36. }
  37. }
  38. export default App;

因为JSX是js的语法扩展,因此当需要根据条件决定是否渲染某个元素,或者根据条件决定渲染哪个元素时候,只需要使用if条件语句或者条件运算符创建元素即可。示例中根据state的digit数据判断显示按钮1还是按钮2。

也可以使用条件运算符

  1. import React from 'react';
  2. import './App.css';
  3. class App extends React.Component {
  4. state = {
  5. isShow: false
  6. };
  7. onButtonClick = () => {
  8. this.setState({
  9. isShow: !this.state.isShow
  10. });
  11. };
  12. render() {
  13. return (
  14. <div>
  15. <div>
  16. {
  17. this.state.isShow && <span>hello, world</span>
  18. }
  19. </div>
  20. <button
  21. onClick={this.onButtonClick}
  22. >
  23. {
  24. this.state.isShow ? '点击隐藏文本' : '点击显示文本'
  25. }
  26. </button>
  27. </div>
  28. );
  29. }
  30. }
  31. export default App;

如果表达式返回false或者null,则React不会渲染任何元素

列表渲染

  1. import React from 'react';
  2. import './App.css';
  3. class App extends React.Component {
  4. state = {
  5. userList: [
  6. {id: '1', name: 'Sam'},
  7. {id: '2', name: 'Ann'}
  8. ]
  9. };
  10. render() {
  11. return (
  12. <ul>
  13. {
  14. this.state.userList.map(({id, name}) => (
  15. <li key={id}>{name}</li>
  16. ))
  17. }
  18. </ul>
  19. );
  20. }
  21. }
  22. export default App;
  1. 使用数组的map方法,返回一个itemJSX的数组,从而渲染这个列表,注意key属性的唯一性

表单和受控组件

背景:当使用需要用户输入内容的表单,例如input、radio等的时候,开发者需要

  1. 输入的内容同步到组件的state中

  2. 可以手动改变元素的内容。

受控组件的概念:这通过受控组件实现。所谓受控组件就是受开发者控制的表单元素。即开发者可以控制表单的取值。

具体实现:实现有两步:1. 将元素的value绑定到state,这样开发者控制state就可以控制表单的取值,因为用户输入内容后,开发者需要将其取值同步到state,因此还要绑定onChange事件,在onChange里面将改变后的value set到state中。当表单的value发生改变,会触发onChange,从而改变state。

示例:示例中在state中添加text属性,并将text绑定到input的value属性上,然后监听onChange,setState设置text。这样,input变化时候同步到state.text,就可以实时显示出来,并且还可以通过改变state来控制input的值得显示

  1. import React from 'react';
  2. import './App.css';
  3. class App extends React.Component {
  4. state = {
  5. text: ''
  6. };
  7. onInputChange = e => {
  8. this.setState({text: e.target.value});
  9. };
  10. onClearButtonClick = () => {
  11. this.setState({text: ''});
  12. };
  13. render() {
  14. return (
  15. <div>
  16. <div>{this.state.text}</div>
  17. <input value={this.state.text} onChange={this.onInputChange} />
  18. <button onClick={this.onClearButtonClick}>清空</button>
  19. </div>
  20. );
  21. }
  22. }
  23. export default App;

4. 组件

React项目是由一个一个的组件组成的。

我们可以使用一个类继承React.Component实现一个组件,也可以使用函数式组件。

一个React组件包含几个部分:

state,props,方法(类方法和箭头函数方法),render属性,生命周期属性

子组件标签要大写,小写的会被解析成原生标签

1. 父子组件通信

父子组件通信主要有3种方式

props传递属性、props传递回调、父组件通过ref属性获取子组件的引用

  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. class Child extends React.Component {
  4. name = 'child';
  5. static propTypes = {
  6. digit: PropTypes.number.isRequired
  7. };
  8. render() {
  9. <div>
  10. <span>digit:{this.props.digit}</span>
  11. <button onClick={this.props.onButtonClick}>点击</button>
  12. </div>
  13. }
  14. }
  15. class App extends React.Component {
  16. state = {
  17. digit: 0
  18. };
  19. childNode = React.createRef();
  20. onButtonClick = () => {
  21. this.state({
  22. digit: Math.random()
  23. });
  24. console.log(this.childNode.current.name);
  25. };
  26. render() {
  27. return (
  28. <div>
  29. <Child
  30. ref={this.childNode}
  31. digit={this.state.digit}
  32. onButtonClick={this.onButtonClick}
  33. />
  34. </div>
  35. );
  36. }
  37. }
  38. export default App;

props 父组件给子组件传递,子组件接收,类型校验

  1. 父组件在JSX元素上通过属性传递给子组件,子组件通过this.props来访问父组件传递的属性

  2. 类型校验:propTypes属性和defaultProps属性

回调

回调其实是父组件通过props传递给子组件的一个函数,子组件可以在组件内部调用这个函数

ref 一般用在父组件直接调用子组件的方法的场景

父组件通过ref获取子组件的实例的引用,使用步骤如下:

  1. react.createRef
  2. 子组件元素设置ref属性
  3. 使用时候用current访问

2. 生命周期

生命周期钩子函数用来在组件从创建到销毁的某个阶段做一些逻辑。

常见的生命周期钩子函数有ComponentDidMount、ComponentWillUnmount等

通常在componentDidMount中处理开启定时器、注册监听器。在componentWillUnmount钩子中清除定时器、注销监听器。