一、首先是React的核心概念
React的核心概念大概就是两个,一个是虚拟DOM另一个是diff算法。正式因为有了这两个核心概念,使得React的性能大大的提升。
首先虚拟DOM:
虚拟DOM就是对于DOM的抽象,虚拟DOM就是一个对象,用这个对象来描述DOM,每次通过对比DOM的变化差异,而选择对变化的部分重新渲染,并不是直接的在DOM上进行操作,从而提升的渲染的效率。
接下来是diff算法:
在某一时刻调用render()函数渲染了一棵元素树,在下一次state或者props更新时,又会生成一棵元素树,而diff算法就是对比这两棵树的差异点,从而只改变变化的部分。
对于根节点元素类型不同时,会拆掉原有的树并且会建一棵新的树。就相当于拆卸掉原有树上的所有节点然后重建。拆卸时会触发componentWillUnmount()函数,而重建会触发新建流程,从componentWillMount()函数,紧接着是componentDidMount()函数。
对于同一类型的元素,React 会保留 DOM 节点,仅比对及更新有改变的属性。
对于同类型组件元素,组件更新时,对于state不变,而props改变时调用componentWillReceiveProps()函数和componentWillUpdate()函数对元素进行改变。
对于在子节点后面添加节点,react会匹配旧结点,然后完成新节点的插入
对于在子节点前面插入节点,React会提供key属性,用key属性来匹配旧结点,对于有key属性的节点仅仅移动位置,对于新的节点再添加。
二、React的使用
首先导入react;然后创建DOM;接着渲染。
// 1. 导入 react import React from 'react' import ReactDOM from 'react-dom' // 2. 创建 虚拟DOM // 参数1:元素名称 参数2:元素属性对象(null表示无) 参数3:当前元素的子元素string||createElement() 的返回值 const divVD = React.createElement('div', { title: 'hello react' }, 'Hello React!!!') // 3. 渲染 // 参数1:虚拟dom对象 参数2:dom对象表示渲染到哪个元素内 参数3:回调函数 ReactDOM.render(divVD, document.getElementById('app'))
三、React创建组件两种方式
第一种通过函数来创建,通过JS函数来创建,也叫无状态组件,这种创建仅仅是为了展示数据,不需要对于数据进行改变。
function Welcome(props) { return ( <h1>Hello {props.name}</h1> ) } ReactDOM.render( <Welcome name="jack" />, document.getElementById('app') )
第二种通过class来创建,也叫有状态组件,可以操作数据而不是简单的展示数据。
class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>现在是 {this.props.date.toLocaleTimeString()}.</h2> </div> ); } } function tick() { ReactDOM.render( <Clock date={new Date()} />, document.getElementById('example') ); }
四、props和state
props:用于父子组件中传递数据,只读不可修改添加,props.children获取组件的内容。
function Welcome(props) { // 返回的 react元素中必须只有一个根元素 return <div>hello, {props.name}</div> } class Welcome extends React.Component { constructor(props) { super(props) } render() { return <h1>Hello, {this.props.name}</h1> } }
state:数据,用于组件内部数据传递,也叫状态,只有class创建才有state,sate是私有的,由组件来控制。
class Hello extends React.Component { constructor() { // es6继承必须用super调用父类的constructor super() this.state = { gender: 'male' } } render() { return ( <div>性别:{ this.state.gender }</div> ) } }
五、组件生命周期
1、创建:constructor() 、componentWillMount() 、render() 、componentDidMount()
2、运行交互:componentWillReceiveProps() 、shouldComponentUpdate() 、componentWillUpdate() 、render() 、componentDidUpdate()
3、卸载:componentWillUnmount()
创建阶段:该阶段函数只执行一次。
constructor():获取props、设置state。
class Greeting extends React.Component { constructor(props) { // 获取 props super(props) // 初始化 state this.state = { count: props.initCount } } } // 初始化 props // 语法:通过静态属性 defaultProps 来初始化props Greeting.defaultProps = { initCount: 0 };
componentWillMount() :
- 说明:组件被挂载到页面之前调用,其在render()之前被调用,因此在这方法里
同步
地设置状态将不会触发重渲染 - 注意:无法获取页面中的DOM对象
- 注意:可以调用
setState()
方法来改变状态值 - 用途:发送ajax请求获取数据
componentWillMount() { console.warn(document.getElementById('btn')) // null this.setState({ count: this.state.count + 1 }) }
render() :
- 作用:渲染组件到页面中,无法获取页面中的DOM对象
-
注意:不要在render方法中调用
setState()
方法,否则会递归渲染- 原因说明:状态改变会重新调用
render()
,render()
又重新改变状态
- 原因说明:状态改变会重新调用
render() { console.warn(document.getElementById('btn')) // null return ( <div> <button id="btn" onClick={this.handleAdd}>打豆豆一次</button> { this.state.count === 4 ? null : <CounterChild initCount={this.state.count}></CounterChild> } </div> ) }
componentDidMount():
- 1 组件已经挂载到页面中
- 2 可以进行DOM操作,比如:获取到组件内部的DOM对象
- 3 可以发送请求获取数据
- 4 可以通过
setState()
修改状态的值 - 注意:在这里修改状态会重新渲染
componentDidMount() { // 此时,就可以获取到组件内部的DOM对象 console.warn('componentDidMount', document.getElementById('btn')) }
接下来是运行交互阶段:
- 特点:该阶段的函数执行多次
- 说明:每当组件的
props
或者state
改变的时候,都会触发运行阶段的函数
componentWillReceiveProps() :
- 说明:组件接受到新的
props
前触发这个方法 - 参数:当前组件
props
值 - 可以通过
this.props
获取到上一次的值 - 使用:若你需要响应属性的改变,可以通过对比
this.props
和nextProps
并在该方法中使用this.setState()
处理状态改变 - 注意:修改
state
不会触发该方法
componentWillReceiveProps(nextProps) { console.warn('componentWillReceiveProps', nextProps) }
shouldComponentUpdate() :
- 作用:根据这个方法的返回值决定是否重新渲染组件,返回
true
重新渲染,否则不渲染 - 优势:通过某个条件渲染组件,降低组件渲染频率,提升组件性能
- 说明:如果返回值为
false
,那么,后续render()
方法不会被调用 - 注意:这个方法必须返回布尔值!!!
- 场景:根据随机数决定是否渲染组件
// - 参数: // - 第一个参数:最新属性对象 // - 第二个参数:最新状态对象 shouldComponentUpdate(nextProps, nextState) { console.warn('shouldComponentUpdate', nextProps, nextState) return nextState.count % 2 === 0 }
componentWillUpdate() :
- 作用:组件将要更新
- 参数:最新的属性和状态对象
- 注意:不能修改状态 否则会循环渲染
componentWillUpdate(nextProps, nextState) { console.warn('componentWillUpdate', nextProps, nextState) }
render():
- 作用:重新渲染组件,与
Mounting
阶段的render
是同一个函数 - 注意:这个函数能够执行多次,只要组件的属性或状态改变了,这个方法就会重新执行
componentDidUpdate()
- 作用:组件已经被更新
- 参数:旧的属性和状态对象
componentDidUpdate(prevProps, prevState) { console.warn('componentDidUpdate', prevProps, prevState) }
卸载阶段:
- 组件销毁阶段:组件卸载期间,函数比较单一,只有一个函数,这个函数也有一个显著的特点:组件一辈子只能执行依次!
- 使用说明:只要组件不再被渲染到页面中,那么这个方法就会被调用( 渲染到页面中 -> 不再渲染到页面中 )
componentWillUnmount()
-
作用:在卸载组件的时候,执行清理工作,比如
- 1 清除定时器
- 2 清除
componentDidMount
创建的DOM对象
六、受控组件和非受控组件
受控组件:
在HTML当中,像input
,textarea
和select
这类表单元素会维持自身状态,并根据用户输入进行更新。
在React中,可变的状态通常保存在组件的state
中,并且只能用 setState()
方法进行更新.
React根据初始状态渲染表单组件,接受用户后续输入,改变表单组件内部的状态。
因此,将那些值由React控制的表单元素称为:受控组件。只能通过setState来设置受控组件的值。
import React from "react" class CommentBox extends React.Component(){ constructor(props){ super(props); this.state={ value:"" } this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(event){ this.setState({ value:event.target.value}); } handleSubmit(){ alert(this.state.value); event.preventDefault; } render(){ return( <form onSubmit={this.handleSubmit}> <input type = "text"value ={this.state.value} onClick={this.handleChange}>姓名</input> <input type = "submit">提交</input> </form> ); } }
非受控组件:
非受控组件数据由DOM操作。使用ref从DOM获得表单数据。
class NameForm extends React.Component { constructor(props) { super(props); this.handleSubmit = this.handleSubmit.bind(this); this.input = React.createRef(); } handleSubmit(event) { alert('A name was submitted: ' + this.input.current.value); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <label> Name: <input type="text" ref={this.input} /> </label> <input type="submit" value="Submit" /> </form> ); } }
七、单身数据流
- 数据流动方向:自上而下,也就是只能由父组件传递到子组件
- 数据都是由父组件提供的,子组件想要使用数据,都是从父组件中获取的
- 如果多个组件都要使用某个数据,最好将这部分共享的状态提升至他们最近的父组件当中进行管理
父子组件数据流传递:
class Child extends React.Component{ constructor(props){ super(props); this.state = {} } render(){ return ( <div> {this.props.text} <br /> <button onClick={this.props.refreshParent}> 更新父组件 </button> </div> ) } } class Parent extends React.Component{ constructor(props){ super(props); this.state = {} } refreshChild(){ return (e)=>{ this.setState({ childText: "父组件沟通子组件成功", }) } } refreshParent(){ this.setState({ parentText: "子组件沟通父组件成功", }) } render(){ return ( <div> <h1>父子组件沟通</h1> <button onClick={this.refreshChild()} > 更新子组件 </button> <Child text={this.state.childText || "子组件未更新"} refreshParent={this.refreshParent.bind(this)} /> {this.state.parentText || "父组件未更新"} </div> ) } }
兄弟组件数据传递:借助父组件
class Brother1 extends React.Component{ constructor(props){ super(props); this.state = {} } render(){ return ( <div> <button onClick={this.props.refresh}> 更新兄弟组件 </button> </div> ) } } class Brother2 extends React.Component{ constructor(props){ super(props); this.state = {} } render(){ return ( <div> {this.props.text || "兄弟组件未更新"} </div> ) } } class Parent extends React.Component{ constructor(props){ super(props); this.state = {} } refresh(){ return (e)=>{ this.setState({ text: "兄弟组件沟通成功", }) } } render(){ return ( <div> <h2>兄弟组件沟通</h2> <Brother1 refresh={this.refresh()}/> <Brother2 text={this.state.text}/> </div> ) } }
参考自:
https://segmentfault.com/a/1190000006831820
https://segmentfault.com/a/1190000012921279