1.react项目搭建
1)搭建react项目有两种:html中引入react和脚手架搭建react
2)react组件分为两种,函数式组件和类式组件
3)参考:React.js下载与使用
react JS文件CDN地址
React官网找到CDN连接 React 官方中文文档 – 用于构建用户界面的 JavaScript 库 或者bootcdn查找cdn地址
引入react可以直接指定cdn地址,也可以在浏览器输入下面地址下载保存为本地js然后html中引入。
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
注: bootcdn用来查找常见js库的cdn地址 BootCDN - Bootstrap 中文网开源项目免费 CDN 加速服务
2.react旧的生命周期
生命周期理解
- 组件从创建到死亡它会经历一些特定的阶段。
- React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。
- 我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。
1)render方法是将虚拟dom挂载(mount)到真实dom。
2)组件挂载时会执行一遍上图左侧方法。每个组件都会先进行一遍挂载mount,然后以后进行更新update。
- 挂载时数据一般都是空。比如axios还没有请求回数据。如果挂载时数据person.name为undefined,会显示空,不显示内容。但是如果person.name中person为undefined,会报错。因此一般state初始化 state={person:{name:''}} 这样就不会报错。
<span>person.name</span>
- 挂载时如果元素为null,react就不去渲染。如下为render里面代码,如果为null react就不去渲染。
name === undefined ?<Button>name</Button>:null
3)setState方法执行时,会执行setState--->shouldComponentUpdate--->componentWillUpdate---render--->componentDidUpdate; shouldComponentUpdate:是否要执行组件更新根据返回值boolean来判断是否进行更新
4)forceUpdate,会强制组件更新 forceUpdate--->componentWillUpdate---render--->componentDidUpdate
父组件render时,会执行右侧的方法
5)ReactDOM.unmountComponentAtNode(document.getElementById('demo'))会卸载组件。然后调用componentWillUnmount方法。
6)如下A组件为B组件的父组件。当父组件调用render方法时,子组件会调用对应方法。父组件render--->componentWillReceiveProps--->shouldComponentUpdate--->componentWillUpdate---render--->componentDidUpdate。
注意:componentWillReceiveProps当子组件收到的props更新时调用,第一次传递过来参数时不会调用,只有当第二次以后传递props才会调用。
7)所有方法分为mount、update两个时刻,即挂载组件和更新组件,分为will和did。render方法用来将虚拟dom转为真实dom;分为三个阶段,如下图:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1"> <title></title> <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script> <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> class A extends React.Component { state = { carName: "奔驰" } changeCar = () => { this.setState({ carName: "奥拓" }) } componentWillMount() { console.log("A---ComponentWillMount") } render() { return ( <div> <div>我是A组件</div> <button onClick={this.changeCar}>切换</button> <B carName={this.state.carName} /> </div> ) } } class B extends React.Component { componentWillMount() { console.log("B---ComponentWillMount") } componentWillReceiveProps() { console.log("B---componentWillRecieveProps()") } render() { return ( <div>B组件接收到的车还是{this.props.carName}</div> ) } } ReactDOM.render( <A />, document.getElementById('example') ); </script> </body> </html>
3.render生命周期 (react 新版本使用)
旧版本生命周期中的componentWillMount、componentWillUpdate、componentWillRecieveProps在新版本被重命名。18. 版本将无法使用旧版本的名字。详情见官网
新旧版本生命周期相比
1)废弃三个钩子componentWillMount、componentWillUpdate、componentWillRecieveProps
2)新增两个钩子getDerivedStateFromProps、getSnapsshowBeforeUpdate。这两个不常用
3)getDerivedFromProps 唯一的作用就是用于从props得到一个衍生的state。不常用,仅用在组件的state取决于pros的情况。如下示例
- getDerivedFromProps可以接收props和state两个参数。props为父组件调用子组件时传入的;state为子组件自身的state属性
无论是子组件调用了 setState(),、接收父组件的 props 发生了变化, 或是父组件的更新都会导致子组件上的 getDerivedStateFromProps被触发。
如果在getDerivedStateFromProps对本组件的state做了初始化,本组件一个setState就引起调用getDerivedStateFromProps,从而引起本组件state的初始化。如果不想初始化,必须加一个state属性,在getDerivedStateFromProps进行判断,来决定更不更新。如下所示,本组件每次调用setState时设置一下setStateOccurs值,从而可以实现setState引起的getDerivedFromProps调用时不执行state初始化。
static getDerivedStateFromProps(props, state) { const { surveySuspectInfo, isSubmit, propsUpdate } = props //允许接受更新 if (propsUpdate) { //案件名称 案件类型 交案单位 负责人 联系方式 立案时间 state = { ...state, surveySuspectInfo, selectedRelPersonIndex: 0, } return state; } //如果是提交 不要让父组件props更新传过来 if (state.setStateOccurs || isSubmit) { state = { ...state, setStateOccurs: false } return state } else { //案件名称 案件类型 交案单位 负责人 联系方式 立案时间 state = { ...state, surveySuspectInfo, selectedRelPersonIndex: 0, } return state; } }
changeTabButton = (activeKey) => { //activeKey和相关人列表的索引一样 this.setState({ selectedRelPersonIndex: +activeKey , setStateOccurs: true, propsUpdate_rel: true }, () => { this.setState({ propsUpdate_rel: false, setStateOccurs: true }) }) }
子组件调用setState,getDerivedStateFromProps(props,state)被触发,此时的state是更新后的值。
getDerivedFromProps(nextProps,preState) nextProps父组件传过来的props,preState为当前子组件的state值。有时需要进行一些判断来决定更不更新子组件state。如果不更新需要return null代表不更新
<script type="text/babel"> class A extends React.Component { state = {count:0} static getDerivedStateFromProps(props,state){ console.log(props + state ) return props } render() { return (<div>{this.state.count}</div>) } } ReactDOM.render( <A count={111} />, document.getElementById('example') ) </script>
4.总结
重要的勾子
- render:初始化渲染或更新渲染调用
- componentDidMount:开启监听, 发送ajax请求
- componentWillUnmount:做一些收尾工作, 如: 清理定时器
即将废弃的勾子
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。
5.虚拟DOM Diff算法
理解:react中每个标签都有一个key。react会将新旧虚拟dom相同key的标签进行比较,如果内容变了就将新虚拟dom替换掉旧虚拟dom,并更新到真实dom 。这样可以有效减少更新真实dom操作。比如:一个ul列表,几千条。可能每次前边的数据都没变化,就不再需要更新真实dom。
数组的index作为标签的key的问题
1)persons = [{name:'fang',id:1}] <Demo persons={persons}>
2)persons=[{name:'yang',id:2},...persons] <Demo persons={persons}> 此时会出现问题,因为Demo中ul的li标签使用index作为key。fang的key为1,yang的key也是1,那么key就重复了,也就回报错。所以,此处使用数据自身携带的唯一的id作为key值最好。
import React, { Component } from 'react' export default class Demo extends Component { render() { const {persons} = this.state return ( <ul> persons.map((person,index)=>{ return (<li key={index}> person.name</li>) }) </ul> ) } }
6.父子组件生命周期执行顺序
父组件A,子组件B ,A组件的render里面有多个B。
A组件render时,遇到B组件就先去执行B组件的render方法,B生命周期结束,然后继续A组件render,再次遇见B就去执行B的生命周期。直至A组件生命周期结束。
如何实时获取子组件的变量值?
父组件A,子组件B ,A组件的render里面有多个B。
A通过传递一个回调函数给B,让B的变量发生变化时,调用A传递的回调函数,从而实现将变量传给A,A此时可以通过state或者成员变量存储传递回来的值。最后在componentDidUpdate获取到所有B组件的变量值,做一些逻辑处理。
A组件render里面包含的每个B组件都是一个B组件建立的新对象。B组件彼此之间互不影响。A组件只有第一次render时新建B的组件对象,以后render只是传递值给B组件对象。
组件对象也有自己的成员变量,可以进行赋值。
export default class PersonInfo extends Component { //成员变量 用来存储所有子组件的值 addAreaDataList_suspect = [] componentDidUpdate() { const { addAreaDataList_suspect } = this //axios接口 完成数据提交 setSurveySuspectInfo(addAreaDataList_suspect) //清空addAreaDataList this.addAreaDataList_suspect = [] } //子组件调用 增加需要提交的多媒体信息 嫌疑人:type=61身份证 71生活照 ; realTimeUpdateSuspect = (fileList, fileType) => { let { addAreaDataList_suspect } = this addAreaDataList_suspect = [...addAreaDataList_suspect, ...fileList] this.addAreaDataList_suspect = addAreaDataList_suspect } render() { return ( <div> <div style={{ display: 'flex', flexDirection: 'column', width: '120%', marginLeft: '-20%' }}> <ImageUpload relTimeUpdata={this.realTimeUpdateSuspect} /> <span className='tag'>身份证照</span> </div> <div style={{ display: 'flex', flexDirection: 'column', width: '120%', marginLeft: '-20%' }}> <ImageUpload relTimeUpdata={this.realTimeUpdateSuspect} descType='6' /> <span className='tag'>生活照</span> </div> </div> } }
import React, { Component } from 'react' export default class ImageUpLoade extends Component { state = { imgs: [] } addImgs= () => { const { relTimeUpdata } = this.props const { imgs} = this.state imgs= [...imgs,{file_name:'图1',id:'1221212'}] relTimeUpdata(count,'img') this.setState({ imgs}) } render() { return ( <div> <Button onClick={this.addImgs}>增加图片</Button> <Button onClick={this.addAudio}>增加语音</Button> </div> ) } }