写在前面,以下内容仅为笔者初学React时的学习资料整理以及总结等
1 初步了解
1.1 React诞生
- 原作者: Jordan Walke
- 2011年部署于Facebook的newsfeed
- 2012年部署于Instagram
- 2013年5月在JSconf US开源
- 开发者:
- 社区 [由React开发者\使用者和相关人员组成的一个开放性集体]
1.2 React概述
1.2.1 React是什么?
- 视图层框架
- 组件系统
- 单向数据流
- 前后端分离的单页面应用
1.2.2 React相关时间?
- 创作时间: 2012年
- 开源时间: 2013年
1.2.3 为什么要创造React
MVC无法满足Facebook的扩展需求,当系统中有很多模型和相应的视图时,其复杂度会级数增长,非常难以理解与调试,特别是模型和视图可能存在双向数据流动.
1.2 React特点
-
Virtual DOM
-
Diff算法
React 16版本之后发布了一个新的算法: React Filber算法.
React Filber把一个耗时长的任务分成很多小片,每一个小片的运行时间很短,虽然总时间依然很长,但是在每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不会被独占.
-
组件系统 [ Component ]
React最核心的思想是将页面中的任何一个区域或者元素都可以看做一个组件.
-
单向数据流
React的核心内容就是数据绑定,所谓数据绑定指的是只要将一些服务端的数据和前端页面绑定好,开发者只关注业务.
-
JSX语法
React采用了JSX语法,更加便利的模拟DOM结构,React可以通过脚手架 [ webpack构建 ] 来对JSX进行编译.
-
函数式编程
JS的最大特点就是函数式编程,在React中,函数式编程可谓是无处不见
2. 构建项目
React主要通过脚手架构建项目,常用有CRA \ DVA \ UMI,这里不对项目构建做说明,可自行查阅相关文档.
3.开始学习
3.1 React基础
3.1.1 React元素
- 元素是构成React应用的最小砖块
- 元素描述了你在屏幕上想看到的内容
- 组件是由元素构成的
- ! 这里的React元素不是真实DOM元素,是模拟DOM的普通对象
3.1.2 React组件
- 组件是由元素构成的一个集合
- React组件的书写方式
/* 函数组件 */ funciton Gems(props){ return <h1>Hello,{props.name}</h1> } //or const Fang = props=><h1>Hello Fang</h1> /* 类组件 */ import React,{Component} from 'react' class GemsFang extends Component{ render(){ <div> My name is Gems. </div> } }
3.1.3 组件的嵌套与组合
- 嵌套
/* 类组件嵌套 */ //父组件 import React,{Component} from 'react' export default class Father extends Component{ render(){ return( <div> this is Father <Son/>//调用子组件 </div> ) } } //子组件 class Son extends Component{ render(){ return( <div> this is Son </div> ) } }/* 函数组件嵌套 */ import React from 'react' //父组件 const Father=()=>{ return( <div> this is Faher <Son/> </div> ) } //子组件 const Son=()=>{ return( <div> this is Son </div> ) } export default Father - 组合
/* 类组件实现组合 */ import React,{Component} from 'react' class Home extends Component{ const {children} = this.props render(){ return( <div> this is Home {children} {/*在Home中使用children属性*/} </div> ) } } class Title extends Component{ render(){ return( <div> this is Title </div> ) } } class Content extends Component{ render(){ return( <div> this is Content </div> ) } } class App extends Component{ render(){ return( <Home> {/*将子组件放在Home组件中*/} <Title/> <Content/> </Home> ) } }import React from 'react' const Title = props=>{ return( <div>this is title<div/> ) } const Cotent = props=>{ return( <div>this is content<div/> ) } cosnt Home = props=>{ return( <div>this is home<div/> {this.props.children} ) } const App = props=>{ return( <Home> <Title/> <Content/> </Home> ) }
3.1.4 组件的样式
-
行内样式
import React,{Component} from 'react' class Gems extends Component{ style={ width:'100px', height:'100px', background:'red' } render(){ return( <div> <p style={this.style}> 行内样式</p> </div> ) } } -
外部引用
import React,{Component} from 'react' import './index.css' import './index.scss' import './index.less' class Gems extends Component{ render(){ return( <div> <p className="text">外部引用</p> <div/> ) } } -
使用classname第三方模块
先安装
classnameyarn add classnamenpm install classname --saveimport React,{Component} from 'react' import classname from 'classname' class Gems extends Component{ render(){ return( <div> <p className={classname({ size:true, bg:false })}>classname模块</p> </div> ) } } -
样式组件
先安装
styled-componentyarn add styled-componentnpm install styled-component --saveimport React,{Component} from 'react' import styled from 'styled-component' const Box = styled.div` width:100px; height:100px; background:red; ` class Gems extends Component{ render(){ return( <Box> 样式组件 </Box> ) } }
3.1.5 组件的数据形式
-
props [属性]
- 外部传入 [父组件传给子组件]
在父组件中将数据作为子组件的属性传递
<Son name="Gems"></Son>,可以在子组件的this.props[类组件]或者形参props[函数组件]中取到name值"Gems"- 内部设置
static default Props = {name:"Gems"}[类组件中]- props验证
安装
yarn add prop-typesnpm install prop-types --save使用
import React,{ Component } from 'react' import PropTypes from 'prop-types' class Father extends Component{ render () { return ( <div> <Son name = "Gems"/> </div> ) } } class Son extends Component{ const { name } = this.props render () { return ( <div> { name } </div> ) } } Gems.propTypes = { name:PropTypes.string } -
state [状态]
-
状态是组件描述某种显示情况的数据,通常属性为不变值,需要改变的数据放在状态中,状态只能由组件自己设置和更改.
-
state的定义形式有两种
- 直接在类内定义
import React,{Component} from 'react' class Gems extends Component{ state={ name:'Gems', } render(){ const { name }=this.state; return { <div> name:{name} </div> } } }- 在类的构造器函数之中定义
import React,{Component} from 'react' class Gems extends Component{ constructor(props){ super(props) this.state={ age:21 } } render(){ const { age }=this.state; return { <div> age:{age} </div> } } } -
setState [修改状态]
state不能直接修改由setState方法来修改 它有两个参数,第一个参数可以是对象也可以是方法[return一个对象]/* 参数是对象 */ this.setState({ flag:!this.state.flag }) /* 参数是方法 */ this.setState((prevState,props)=>{ //prevState是先前的状态 return{ flag:!prevState.flag } })setState是异步的,所以想要获取到最新的state,需要使用第二个参数这是一个可选的回调函数this.setState((prevState,props)=>{ return { flag:!prevState.flag } },()=>{ console.log('回调里的state',this.state.flag); }) console.log('setState外部的',this.state.flag)
-
3.1.6 组件的数据渲染
-
条件渲染
{ condition?'取消':'收藏' } {flag&&'取消'||'收藏'} //也可以定义函数通过分支语句return DOM结构 -
列表渲染
//数据 cosnt people = [{ id:1, name:'Gems', age:21 },{ id:2, name:'Insane', age:21 },{ id:3, name:'Saint', age:22 }] { people.map(people=>{ return ( <li key={people.id}> <span>name:{people.name}</span> <span>age:{people.age}</span> </li> ) }) } -
dangerouslySetInnerHTML出于安全,React当中所有表达式的内容会被转义,如果直接输入,标签会被当成文本,需要该属性动态设置
innerHTML.this.state={ text:"<p>this is innerHTML</p>" } render(){ const { content }=this.state const html={ __html:content } //注意__html是两个下划线 return ( <div dangerouslySetInnerHTML={ html }> </div> ) }
3.1.7 组件的事件
- 绑定事件
采用on+事件名的方式来绑定一个事件,但原生事件全是小写的
onclick,React里的事件是小驼峰onClick,React事件不是原生事件是合成事件. - 事件handler的写法
这里只提两种推荐写法,如需其它写法可自行查阅相关文档.
- 在组件内使用箭头函数定义一个方法.
- 直接在组件内定义一个非箭头函数的方法,然后在
constructor里bind(this)
- ref绑定
- 普通绑定
<input type="text" ref="usernm"> - 函数形式 [推荐]
<input type="text" ref={el=>this.usernm=el}>
不要过量使用
ref,会浪费性能 - 普通绑定
3.1.8 状态提升
- 作用
当多个组件需要反映相同的变化数据时,建议将共享状态提升到最近的共同父组件中去.
- 使用
import React,{Component} from 'react' export default class Father extends Component { constructor(props){ super(props); this.changeMoney=this.changeMoney.bind(this); this.state={ money:0 } } changeMoney(e){ this.setState({ money:e.target.value }); } render(){ const { money } = this.state; return ( <h3>Enter your money</h3> <input value={moeny} onChange={this.changeMoney} /> <Son money={money} /> {/* Son和input共用money */} ) } }
3.1.9 组件通信
-
父子组件通信
父组件将自己的状态
state传递给子组件,子组件当做属性props来接收,当父组件更改自己的状态的时候,子组件接收到的属性就会发生改变.父组件利用
ref对子组件做标记,通过调用子组件的方法以更改子组件的状态,也可以调用子组件的方法./* Father */ render(){ const { money } = this.state; return ( <div> <Son money={ money } /> </div> ) } /* Son */ render(){ const { money } = this.props; return ( <div> <p>Father give me :{ money }</p> </div> ) } -
子父组件通信
父组件将自己的某个方法传递给子组件,在方法里可以做任意操作,如修改状态,子组件通过
this.props接收到父组件的方法后调用./* 以下为类组件中的主要代码,并非完整,需自行补全类组件的创建 */ /* Father */ this.state={ money:0 } changeMoney = val => { this.setState({ money:val }) } render(){ const { money } = this.state; return ( <div> <Son changeMoney={ this.changeMoney } /> <p>get { money } from my son</p> </div> ) } /* Son */ constructor(props){ super(props); this.state = { hongbao:10000 } } render(){ const { changeMoney } = this.props; const { hongbao } = this.state; return ( <div> <button onClick = {()=>{changeMoney( hongbao )}}>send hongbao</button> </div> ) } -
非父子组件通信
兄弟组件之间通信需要借助共同的父组件才能完成,原理结合子父通信/父子通信,主要难点为
ref绑定/* 仅单独对ref绑定做演示 */ /* 父组件 */ /* 已略去文件引入部分 */ export default class Father extend Component{ cry = () => { this.brother.cry(); } render(){ return() <div> <Sister cry = {this.cry}/> <Brother ref = { el => this.brother = el }/> </div> } } -
跨组件通信
当出现多维度父子关系需要通信时,
react提供了context api来实现跨组件通信//创建一个context const ThemeContext = React.createContext('light'); //一级元素 class App extends Component { render() { //使用一个provider来将当前的theme传递给以下的组件树,包起来,这里将"dark"作为值传递下去 return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); } } //二级元素 function Toolbar(props) { return ( <div> <ThemedButton /> </div> ); } //三级元素 class ThemedButton extends Component { //指定contextType读取当前的themecontext //React 会往上找到最近的 theme Provider,然后使用它的值。 //在这个例子中,当前的 theme 值为"dark". static contextType = ThemeContext; render() { return <Button theme={this.context} />; } } -
多组件状态共享
状态管理放在后面讲解,可自行查阅文档
3.1.10 组件的生命周期
仅针对16.3后版本的生命周期
- 挂载阶段
- constructor
- 通过
super来完成继承,并将props赋值给this.props. - 做一些初始化动作:比如:
state的定义和初始化赋值,比如非箭头函数的this绑定.
- 通过
- static getDerivedStateFromProps
- 必须要求有返回值,返回值是一个新的状态或者
null,这里可以更新一次state. static静态方法这里无法使用this.
- 必须要求有返回值,返回值是一个新的状态或者
- render
- 通过
React.createElement将jsx结构渲染为vdom对象模型. - 可以进行
this.props和this.state的计算.
- 通过
- componentDidMount
- 组件挂载结束.
- 此时可以做数据请求,然后将请求结果给我们的
state. - 这个阶段已经生成真实DOM,可以进行DOM操作.
- 我们可以做第三方类库实例化.
- constructor
- 更新阶段
- static getDerivedStateFromProps(nextProps)
- 初始化执行一次 代替了
componentWillMount. props改变可以触发 代替了componentWillReceiveProps.state改变可以触发 代替了componentWillUpdate.
- 初始化执行一次 代替了
- shouldComponentUpdate(nextProps,nextState)
- 组件是否应该更新,这个钩子函数可以决定组件是否要更新,是性能优化的方案.
- 必须要有返回值,返回值是布尔值.
true执行更新,false不执行更新.- 如果组件继承
Component,是深对比. - 如果组件继承
PureComponent,不写此钩子,是浅对比.
- render
- 同初始化阶段,数据更新,重新渲染生成vdom树.
- getSnapshotBeforeUpdate(prevProps,prevState)
- 这个钩子的存在意义就是通过返回值给
componentDidUpdate传递一个数据.
- 这个钩子的存在意义就是通过返回值给
- ComponentDidUpdate
- 组件更新结束
- 真实DOM渲染出来了,可以操作DOM.
- 第三方库实例化 - 动态数据的实例化.
- static getDerivedStateFromProps(nextProps)
- 卸载阶段
- ComponentWillUnmount
- 表示组件卸载时触发.
- 作用:善后 - 完成清理工作.
- ComponentWillUnmount
- 错误处理阶段
- static getDerivedStateFromError
- 监听子组件的错误,然后更新
state.
- 监听子组件的错误,然后更新
- componentDidCatch ( err,info )
- 记录错误栈信息.
- static getDerivedStateFromError
生命周期与相关钩子函数非常重要,此处仅提炼出部分,详情可查阅官方文档.
至此,React学习旅程第一部分内容全部结束.
下期更新:组件库,高阶组件,路由,状态管理等