1. React 介绍

1.png

React 是一个用于构建用户界面的 JavaScript 库,它只负责应用的视图层,帮助开发人员构建快速且交互式的 web 应用程序。

React 使用组件的方式构建用户界面。

2. JSX 语法

在 React 中使用 JSX 语法描述用户界面,它是一种 JavaScript 语法扩展。

在 React 代码执行之前,Babel 会将 JSX 语法转换为标准的 JavaScript API。

JSX 语法就是一种语法糖,让开发人员使用更加舒服的代码构建用户界面。

2.1 在 JSX 中使用表达式

  1. const user = {
  2. firstName: 'Harper',
  3. lastName: 'Perez'
  4. }
  5. function formatName(user) {
  6. return user.firstName + ' ' + user.lastName;
  7. }
  8. const element = <h1>Hello, {formatName(user)}!</h1>;

JSX 本身其实也是一种表达式,将它赋值给变量,当作参数传入,作为返回值都可以。

  1. function getGreeting(user) {
  2. if (user) {
  3. return <h1>Hello, {formatName(user)}!</h1>;
  4. }
  5. return <h1>Hello, Stranger.</h1>;
  6. }

2.2 属性

如果属性值为字符串类型,需要加引号,属性名称推荐采用驼峰式命名法。

  1. const element = <div greeting="hello"></div>;

如果属性值为JavaScript表达式,属性值外面加大括号。

  1. const element = <img src={user.avatarUrl} />;
  2. // 注意大括号外面不能加引号,JSX 会将引号当中的内容识别为字符串而不是表达式

2.3 JSX 单标记必须闭合

如果 JSX 是单标记,必须闭合,否则报错。

  1. const element = <img src={user.avatarUrl} />
  2. const element = <input type="text"/>

2.4 className

为 JSX 标记添加类名需要使用 className,而不是class。

  1. const element = <img src={user.avatarUrl} className="rounded"/>;

2.5 JSX 自动展开数组

  1. const ary = [<p>哈哈</p>, <p>呵呵</p>, <p>嘿嘿</p>];
  2. const element = (
  3. <div>{ary}</div>
  4. );
  5. // 解析后
  6. /*
  7. <div>
  8. <p>哈哈</p>
  9. <p>呵呵</p>
  10. <p>嘿嘿</p>
  11. </div>
  12. */

2.6 三元运算

  1. { boolean ? <div>Hello React</div> : null }
  2. { boolean && <div>Hello React</div> }

2.7 循环

  1. const persons = [{
  2. id: 1,
  3. name: '张三',
  4. age: 20
  5. }, {
  6. id: 2,
  7. name: '李四',
  8. age: 15
  9. }, {
  10. id: 3,
  11. name: '王五',
  12. age: 22
  13. }]
  1. <ul>
  2. { persons.map(person => <li key={person.id}> {person.name} {person.age} </li>) }
  3. </ul>

2.8 事件

  1. {/* 第一个参数即是事件对象 不需传递 */}
  2. <button onClick={this.eventHandler}>按钮</button>
  3. {/* 需要传递事件对象 */}
  4. <button onClick={e=>this.eventHandler('arg',e)}>按钮</button>
  5. {/* 最后一个参数即是事件对象 不需传递 */}
  6. <button onClick={this.eventHandler.bind(null, 'arg')}>按钮</button>
  1. constructor () {
  2. this.eventHandler = this.eventHandler.bind(this)
  3. }
  4. eventHandler () {}
  5. <button onClick={this.eventHandler}>按钮</button>

2.9 样式

2.9.1 行内样式
  1. class App extends Component {
  2. render() {
  3. const style = {width: 200, height: 200, backgroundColor: 'red'};
  4. return <div style={style}></div>
  5. }
  6. }

2.9.2 外链样式
  1. // Button.js
  2. import styles from './Button.module.css';
  3. class Button extends Component {
  4. render() {
  5. return <button className={styles.error}>Error Button</button>;
  6. }
  7. }

2.9.3 全局样式
  1. import './styles.css'

2.10 ref 属性

2.10.1 createRef
  1. class Input extends Component {
  2. constructor() {
  3. super()
  4. this.inputRef = React.createRef()
  5. }
  6. render() {
  7. return (
  8. <div>
  9. <input type="text" ref={this.inputRef} />
  10. <button onClick={() => console.log(this.inputRef.current)}> button </button>
  11. </div>
  12. )
  13. }
  14. }

2.10.2 函数参数
  1. class Input extends Component {
  2. render() {
  3. return (
  4. <div>
  5. <input type="text" ref={input => (this.input = input)} />
  6. <button onClick={() => console.log(this.input)}>button</button>
  7. </div>
  8. )
  9. }
  10. }

2.10.3 ref 字符串

不推荐使用,在严格模式下报错。

  1. class Input extends Component {
  2. render() {
  3. return (
  4. <div>
  5. <input type="text" ref="username" />
  6. <button onClick={() => console.log(this.refs.username)}>button</button>
  7. </div>
  8. )
  9. }
  10. }

2.10.4 获取组件实例

点击按钮让 input 文本框获取焦点。

input 文本框以及让文本框获取焦点的方法定义在 Input 组件中,在 App 组件中引入 Input 组件,按钮定义在 App 组件中。

  1. // Input.js
  2. class Input extends Component {
  3. constructor() {
  4. super()
  5. this.inputRef = React.createRef()
  6. this.focusInput = this.focusInput.bind(this)
  7. }
  8. focusInput() {
  9. this.inputRef.current.focus()
  10. }
  11. render() {
  12. return (
  13. <div>
  14. <input type="text" ref={this.inputRef} />
  15. </div>
  16. )
  17. }
  18. }
  1. // App.js
  2. class App extends Component {
  3. constructor() {
  4. super()
  5. this.InputComponentRef = React.createRef()
  6. }
  7. render() {
  8. return (
  9. <div className="App">
  10. <Input ref={this.InputComponentRef} />
  11. <button onClick={() => this.InputComponentRef.current.focusInput()}>button</button>
  12. </div>
  13. )
  14. }

2.png

3. 组件

3.1 什么是组件

React 是基于组件的方式进行用户界面开发的. 组件可以理解为对页面中某一块区域的封装。

3.png

3.2 创建组件

3.2.1 创建类组件
  1. import React, { Component } from 'react';
  2. class App extends Component {
  3. render () {
  4. return <div>Hello, 我是类组件</div>
  5. }
  6. }

3.2.2 创建函数组件
  1. const Person = () => {
  2. return <div>Hello, 我是函数型组件</div>;
  3. }

注意事项

  1. 组件名称首字母必须大写,用以区分组件和普通标签。
  2. jsx语法外层必须有一个根元素

3.3 组件 props

3.3.1 props 传递数据

在调用组件时可以向组件内部传递数据,在组件中可以通过 props 对象获取外部传递进来的数据。

  1. <Person name="乔治" age="20"/>
  2. <Person name="玛丽" age="10"/>
  1. // 类组件
  2. class Person extends Component {
  3. render() {
  4. return (
  5. <div>
  6. <h3>姓名:{this.props.name}</h3>
  7. <h4>年龄:{this.props.age}</h4>
  8. </div>
  9. );
  10. }
  11. }
  1. // 函数组件
  2. const Person = props => {
  3. return (
  4. <div>
  5. <h3>姓名:{props.name}</h3>
  6. <h4>年龄:{props.age}</h4>
  7. </div>
  8. );
  9. }

注意:

  1. props 对象中存储的数据是只读的,不能在组件内部被修改。
  2. 当 props 数据源中的数据被修改后,组件中的接收到的 props 数据会被同步更新。( 数据驱动DOM )

3.3.2 设置 props 默认值
  1. class App extends Component {
  2. static defaultProps = {}
  3. }
  1. function ThemedButton(props) {
  2. }
  3. ThemedButton.defaultProps = {
  4. theme: "secondary",
  5. label: "Button Text"
  6. };

3.3.3 组件 children

通过 props.children 属性可以获取到在调用组件时填充到组件标签内部的内容。

  1. <Person>组件内部的内容</Person>
  1. const Person = (props) => {
  2. return (
  3. <div>{props.children}</div>
  4. );
  5. }

3.3.4 单向数据流
  1. 在React中, 关于数据流动有一条原则, 就是单向数据流动, 自顶向下, 从父组件到子组件.
  2. 单向数据流特性要求我们共享数据要放置在上层组件中.
  3. 子组件通过调用父组件传递过来的方法更改数据.
  4. 当数据发生更改时, React会重新渲染组件树.
  5. 单向数据流使组件之间的数据流动变得可预测. 使得定位程序错误变得简单.

react基础回顾 - 图4

3.4 类组件状态 state

3.4.1 定义组件状态

类组件除了能够从外部 (props) 接收状态数据以外还可以拥有自己的状态 (state),此状态在组件内部可以被更新,状态更新 DOM 更新。

组件内部的状态数据被存储在组件类中的 state 属性中,state 属性值为对象类型,属性名称固定不可更改。

  1. class App extends Component {
  2. constructor () {
  3. super()
  4. this.state = {
  5. person: { name: '张三', age: 20 },
  6. }
  7. }
  8. render () {
  9. return (
  10. <div>
  11. {this.state.person.name}
  12. {this.state.person.age}
  13. </div>
  14. );
  15. }
  16. }

3.4.2 更改组件状态

state 状态对象中的数据不可直接更改,如果直接更改 DOM 不会被更新,要更改 state 状态数据需要使用 setState方法。

  1. class App extends Component {
  2. constructor () {
  3. this.state = {
  4. person: { name: '张三', age: 20 },
  5. }
  6. this.changePerson = this.changePerson.bind(this)
  7. }
  8. changePerson () {
  9. this.setState({
  10. person: {
  11. name: '李四',
  12. age: 15
  13. }
  14. })
  15. }
  16. render() {
  17. return (
  18. <div>
  19. {this.state.person.name}
  20. {this.state.person.age}
  21. <button onClick={this.changePerson}>按钮</button>
  22. </div>
  23. );
  24. }
  25. }

3.4.3 双向数据绑定

双向数据绑定是指,组件类中更新了状态,DOM 状态同步更新,DOM 更改了状态,组件类中同步更新。组件 <=> 视图。

要实现双向数据绑定需要用到表单元素和 state 状态对象。

  1. class App extends Component {
  2. constructor () {
  3. this.state = {
  4. name: "张三"
  5. }
  6. this.nameChanged = this.nameChanged.bind(this)
  7. }
  8. nameChanged (event) {
  9. this.setState({name: event.target.value});
  10. }
  11. render() {
  12. return (
  13. <div>
  14. <div>{this.state.name}</div>
  15. <Person name={this.state.name} changed={this.nameChanged}/>
  16. </div>
  17. )
  18. }
  19. }
  1. const Person = props => {
  2. return <input type="text" value={props.name} onChange={props.changed}/>;
  3. }

3.5 类组件生命周期函数

react基础回顾 - 图5

在组件完成更新之前需要做某种逻辑或者计算,就需要用到快照

  1. componentDidUpdate(prevProps, prevState, snapshot) {}

getSnapshotBeforeUpdate 方法会在组件完成更新之前执行,用于执行某种逻辑或计算,返回值可以在 componentDidUpdate 方法中的第三个参数中获取,就是说在组件更新之后可以拿到这个值再去做其他事情。

  1. getSnapshotBeforeUpdate(prevProps, prevState) {
  2. return 'snapshot'
  3. }

3.6 Context

通过 Context 可以跨层级传递数据

react基础回顾 - 图6

  1. // userContext.js
  2. import React from "react"
  3. const userContext = React.createContext("default value")
  4. const UserProvider = userContext.Provider
  5. const UserConsumer = userContext.Consumer
  6. export { UserProvider, UserConsumer }
  1. // App.js
  2. import { UserProvider } from "./userContext"
  3. class App extends Component {
  4. render() {
  5. return (
  6. <UserProvider value="Hello React Context">
  7. <A />
  8. </UserProvider>
  9. )
  10. }
  11. }
  1. // C.js
  2. import { UserConsumer } from "./userContext"
  3. export class C extends Component {
  4. render() {
  5. return (
  6. <div>
  7. <UserConsumer>
  8. {username => {
  9. return <div>{username}</div>
  10. }}
  11. </UserConsumer>
  12. </div>
  13. )
  14. }
  15. }

context 的另一种用法

  1. // userContext.js
  2. export default userContext
  1. // C.js
  2. import userContext from "./userContext"
  3. export class C extends Component {
  4. static contextType = userContext
  5. render() {
  6. return (
  7. <div>
  8. {this.context}
  9. </div>
  10. )
  11. }
  12. }

4. 表单

4.1 受控表单

表单控件中的值由组件的 state 对象来管理,state对象中存储的值和表单控件中的值时同步状态的

  1. class App extends Component {
  2. constructor () {
  3. this.state = { username: "" }
  4. this.nameChanged = this.nameChanged.bind(this)
  5. }
  6. nameChanged (e) {
  7. this.setState({username: e.target.value})
  8. }
  9. render() {
  10. return (
  11. <form>
  12. <p>{this.state.username}</p>
  13. <input type="text" value={this.state.username} onChange={this.nameChanged}/>
  14. </form>
  15. )
  16. }
  17. }

4.2 非受控表单

表单元素的值由 DOM 元素本身管理。

  1. class App extends Component {
  2. constructor () {
  3. this.onSubmit = this.onSubmit.bind(this)
  4. }
  5. onSubmit(e) {
  6. console.log(this.username.value)
  7. e.preventDefault();
  8. }
  9. render(
  10. <form onSubmit={this.onSubmit}>
  11. <input type="text" ref={username => this.username = username}/>
  12. </form>
  13. )
  14. }

5. 路由

url地址与组件之间的对应关系,访问不同的url地址显示不同的组件。

下载:npm install react-router-dom

5.1.1 路由基本使用

  1. // App.js
  2. import React from 'react';
  3. import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
  4. function Index() {
  5. return <div>首页</div>;
  6. }
  7. function News() {
  8. return <div>新闻</div>;
  9. }
  10. function App() {
  11. return (
  12. <Router>
  13. <div>
  14. <Link to="/index">首页</Link>
  15. <Link to="/news">新闻</Link>
  16. </div>
  17. <div>
  18. <Route path="/index" component={Index}/>
  19. <Route path="/news" component={News}/>
  20. </div>
  21. </Router>
  22. );
  23. }

5.1.2 路由嵌套

  1. function News(props) {
  2. return (
  3. <div>
  4. <div>
  5. <Link to={`${props.match.url}/company`}>公司新闻</Link>
  6. <Link to={`${props.match.url}/industry`}>行业新闻</Link>
  7. </div>
  8. <div>
  9. <Route path={`${props.match.path}/company`} component={CompanyNews} />
  10. <Route path={`${props.match.path}/industry`} component={IndustryNews}/>
  11. </div>
  12. </div>
  13. );
  14. }
  15. function CompanyNews() {
  16. return <div>公司新闻</div>
  17. }
  18. function IndustryNews() {
  19. return <div>行业新闻</div>
  20. }

5.1.3 路由传参

  1. import url from 'url';
  2. class News extends Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. list: [{
  7. id: 1,
  8. title: '新闻1'
  9. }, {
  10. id: 2,
  11. title: '新闻2'
  12. }]
  13. }
  14. }
  15. render() {
  16. return (
  17. <div>
  18. <div>新闻列表组件</div>
  19. <ul>
  20. this.state.list.map((item, index) => {
  21. return (
  22. <li key={index}>
  23. <Link to={`/detail?id=${item.id}`}>{item.title}</Link>
  24. </li>
  25. );
  26. })
  27. </ul>
  28. </div>
  29. );
  30. }
  31. }
  32. class Detail extends Component {
  33. constructor(props) {
  34. super(props);
  35. }
  36. const { query } = url.parse(this.props.location.search, true);
  37. console.log(query); // {id: 1}
  38. render() {
  39. return <div>新闻详情</div>
  40. }
  41. }

5.1.4 路由重定向

  1. import { Redirect } from 'react-router-dom';
  2. class Login extends Component {
  3. render() {
  4. if (this.state.isLogin) {
  5. return <Redirect to="/"/>
  6. }
  7. }
  8. }