1. 基础
1. 超基础API
-
创建react元素
React.createElement(参数1,参数2,参数3,…)
// 参数1元素名称
// 参数2元素属性
// 参数3以及以后的参数都表示元素子节点 -
渲染react元素
ReactDOM.render(参数1,参数2)
// 参数1要渲染的元素或组件
// 参数2要渲染的元素或组件的挂载点例:
const ele = React.createElement('h1',{id: 'kkk'},'Hello React') ReactDOM.render(ele, document.getElementById('root'))
2. react脚手架
使用官方的creat-react-app脚手架(集成了react+react-dom+react-router),一键搭建项目
3. jsx
jsx是javascript xml的简写,表示在javascript中写xml(html)格式的代码。
jsx是react的核心内容
-
属性名是驼峰命名法
class—>className 、for—>htmlFor//<label for="female">Female</label> <label htmlFor="female">Female</label> <input type="radio" name="sex" id="female" />
-
推荐使用小括号包裹jsx,避免js中自动插入分号的陷阱
const ele = (<h1>hello</h1>)
-
jsx中使用js表达式【函数调用、三目表达式等等】
语法:{js表达式}// 例1 const name = '迟娜' const ele = ( <h1> 你好,我叫:{name} </h1> ) ReactDOM.render(ele, document.getElementById('root')) //例2 const flag = false const meet = ()=>{ if(flag) { return (<h1>你好</h1>) } return (<h1>再见</h1>) } const ele = ( <div>{meet()}</div> ) ReactDOM.render(ele, document.getElementById('root')) //例3 const lists = [ {id: 1, title: 'AAA'}, {id: 2, title: 'BBB'}, {id: 3, title: 'CCC'} ] const ele = ( <ul> {lists.map((item)=>{ return ( <li key={item.id}> {item.title} </li> ) })} </ul> ) ReactDOM.render(ele, document.getElementById('root'))
jsx的样式处理
- 行内样式—style
<div style={{color:'red',backgroundColor:'yellow'}}> 你好 </div>
- 类名-----className
// css .kkkk { background-color: yellow; } <div className="kkkk"> 你好 </div>
4. 组件
组件是react的一等公民
1. 使用函数创建自定义组件
函数组件:使用js的函数创建的组件
-
函数名必须是大写字母开头
-
函数名直接作为组件标签名使用
-
函数组件必须有返回值,表示该组件的结构【return null表示不渲染任何东西,也必须写】
function Hello(){ return ( <div> 你好,react </div> ) } ReactDOM.render(<Hello/>, document.getElementById('root'))
2. class创建自定义组件
类组件:使用es6的class创建的组件
-
类名必须是大写字母开头
-
类组件必须继承自React.Component父类,从而可以使用父类中提供的方法或者属性
-
类组件必须提供render()方法,render()方法必须有返回值,表示该组件的结构
class Hello extends React.Component{ render(){ return ( <div> Hello </div> ) } } ReactDOM.render(<Hello/>, document.getElementById('root'))
React.PureComponent
PureComponent表示一个纯组件,可以用来优化React程序,减少render函数执行的次数,从而提高组件的性能。
pureComponent中的 shouldComponentUpdate() 进行的是浅比较,也就是说如果是引用数据类型的数据,数据引用地址没有变化,而数据发生改变的时候render是不会执行的。PureComponent一般会用在一些纯展示组件上。
使用pureComponent的好处:当组件更新时,如果组件的props或者state都没有改变,render函数就不会触发。省去虚拟DOM的生成和对比过程,达到提升性能的目的。这是因为react自动做了一层浅比较。
3. 事件绑定
- 函数组件事件绑定
function Hello(){ function handleClick() { console.log('cn----Hello') } return ( <div onClick={handleClick}> 你好,react </div> ) } ReactDOM.render(<Hello/>, document.getElementById('root'))
- 类组件事件绑定
class Hello extends React.Component{ handleClick() { console.log('cn----Hello') } render(){ return ( <div onClick={this.handleClick}> Hello </div> ) } } ReactDOM.render(<Hello/>, document.getElementById('root'))
4. 有状态组件和无状态组件
函数组件又叫做无状态组件,类组件又叫做有状态组件
- 状态 ---- state【数据可以拆分成属性和状态,状态有一个隐含的意思,就是存在改变状态的行为。因此,不变的数据是属性props,变化的数据是状态state】
- 函数组件没有自己的状态,只负责静态数据展示
- 类组件有自己的状态,可以动态更新页面
组件中的state和setState()
状态(state)即数据,是类组件内部私有的数据,只能在组件内部使用,state的值是对象,表示一个组件可以有很多数据
// 例
class Hello extends React.Component {
constructor() {
super();
this.state = {name: '花花'};
this.tick = this.tick.bind(this)
}
// 简化语法初始化state
// state = {
// date: new Date()
// }
// button元素调用tick()方法,tick()方法的this应该指向button元素,因此必须改变this指向<Hello>组件
tick() {
if(this.state.name === '花花') {
this.setState({
name: '草草'
});
} else {
this.setState({
name: '花花'
});
}
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.name}.</h2>
<button onClick={this.tick}>切换</button>
</div>
);
}
}
ReactDOM.render(
<Hello />,
document.getElementById('root')
);
对有状态组件和无状态组件的理解以及使用场景
-
有状态组件
特点:
- 是类组件
- 有继承
- 可以使用this
- 可以使用react的生命周期
- 使用较多,容易频繁触发生命周期钩子函数,影响性能
- 内部使用state,维护自身状态的变化,有状态组件根据外部组件传入的 props 和自身的 state进行渲染。
使用场景:
- 需要使用到状态的。
- 需要使用状态操作组件的(无状态组件的也可以实现。react-hooks也可实现了)
总结:
类组件可以维护自身的状态变量,即组件的 state ,类组件还有不同的生命周期方法,可以让开发者能够在组件的不同阶段(挂载、更新、卸载),对组件做更多的控制。类组件则既可以充当无状态组件,也可以充当有状态组件。当一个类组件不需要管理自身状态时,也可称为无状态组件。 -
无状态组件
特点:
- 不依赖自身的状态state
- 可以是类组件或者函数组件
- 有更高的性能。当不需要使用生命周期钩子时,应该首先使用无状态函数组件
- 组件内部不维护 state ,只根据外部组件传入的 props 进行渲染的组件,当 props 改变时,组件重新渲染。
使用场景:
- 组件不需要管理state,用于纯展示
优点:
- 简化代码、专注于 render
- 组件不需要被实例化,无生命周期,提升性能。
- 输出(渲染)只取决于输入(属性),无副作用
视图和数据的解耦分离
缺点:
- 无法使用 ref
- 无生命周期方法
- 无法控制组件的重渲染,因为无法使用shouldComponentUpdate 方法,当组件接受到新的属性时则会重渲染
总结:
当一个组件不需要管理自身状态时,也就是无状态组件,应该优先设计为函数组件。比如自定义的<Button/>、 <Input /> 等组件。
5. 受控组件与非受控组件
-
受控组件
受控组件:其值受到react控制的组件成为受控组件
众所周知,html中的表单元素是可输入的,意思就是说它有自己的可变状态。但是react中可变状态通常是保存在state中的,通过setState()方法来进行修改的。因此,react将state与表单元素值vlaue绑定到一起,这样便可以由state的值来控制表单元素的值。<input type="text" value={this.state.txt} onChange={e => this.setState({txt: e.target.value})}/>
-
非受控组件(DOM方式)
借助ref使用原生DOM方式来获取表单元素值
ref的作用:获取DOM或组件// 例 class Hello extends React.Component { constructor() { super(); // 创建ref this.txtRef = React.createRef() } handleClick = () => { console.log(this.txtRef.current.value) } render() { return ( <div> // this.txtRef即可表示该input元素 <input type="text" ref={this.txtRef}/> <button onClick={this.handleClick}>获取表单的值</button> </div> ); } } ReactDOM.render( <Hello />, document.getElementById('root') );
2. 进阶
1. 组件通讯的方式
组件通讯:组件之间进行数据传递
1. 父组件传递数据给子组件
-
使用props进行数据传递。组件是封闭的,要接收父组件的数据应该通过props来实现
传递数据:直接给组件标签添加属性
接受数据:- 函数组件:通过函数参数props接受数据
function Hello(props){ }
- 类组件:通过this.props接受数据
class Hello extends React.Component { render() { return ( <div> <div> 姓名:{this.props.name} </div> <div> 年龄:{this.props.age} </div> </div> ); } } ReactDOM.render(<Hello name='jack' age={18}/>,document.getElementById('root'));
- 函数组件:通过函数参数props接受数据
-
props的特点
- 可以传递任意类型的数据------字符串、数组、函数等等
- props是只读的对象,只能读属性的值,不能修改对象
- 使用类组件时,如果使用了构造函数constructor,应该将props传递给super(),否则,无法在构造函数中获取props
-
父组件传递数据给子组件
- 父组件给子组件标签添加属性,值为state中的数据【要传递的数据】
- 子组件通过props接受父组件传递过来的数据
class People extends React.Component{ constructor(){ super() this.state = { name:'花花' } } render(){ return ( <div> 这是一个人 <Person name={this.state.name}/> </div> ) } } class Person extends React.Component{ constructor(props){ super(props) } render(){ return ( <div> 名字是: {this.props.name} </div> ) } } ReactDOM.render( <People/>, document.getElementById('root') );
2. 子组件传递数据给父组件
思路:利用回调函数,父组件提供回调函数(用于接受数据),将该函数作为属性值传递给子组件,子组件调用该回调函数,将要传递的数据作为回调函数的参数
class People extends React.Component{
constructor(){
super()
this.state = {
name:'花花'
}
}
// 父组件提供给子组件的回调函数
handleData(data){
console.log('cn----data', data)
}
render(){
return (
<div>
这是一个人
<Person name={this.state.name} fn={this.handleData}/>
</div>
)
}
}
class Person extends React.Component{
constructor(props){
super(props)
this.state = {
data: 'hi'
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.props.fn(this.state.data)
}
render(){
return (
<div>
<div>
名字是:{this.props.name}
</div>
<button onClick={this.handleClick}>打个招呼吧</button>
</div>
)
}
}
ReactDOM.render(<People/>, document.getElementById('root') );
3. 兄弟组件之间通信
思想:状态提升。通过父组件进行间接通信
4. 跨组件传递数据----Context
- 调用React.createContext()创建Provider(提供数据)和Consumer(消费数据)两个组件
const { Provider, Consumer } = React.createContext()- 在一个组件中使用Provider组件包裹组件,设置value属性,表示要传递的数据
- 另一个组件调用Consumer组件接收数据
const { Provider, Consumer } = React.createContext()
class People extends React.Component{
render(){
return (
<Provider value="pink">
<div>
<Person/>
</div>
</Provider>
)
}
}
class Person extends React.Component{
render(){
return (
<div>
hhhhh
<Consumer>
{
// data的值就是Provider组件中value属性值
data => <div>{data}</div>
}
</Consumer>
</div>
)
}
}
ReactDOM.render(<People/>,document.getElementById('root'))
5. 状态管理工具 ---- redux
2. React16.4+组件生命周期
组件的生命周期:组件从被创建到被挂载到页面中运行,再到组件不用时卸载的过程。生命周期的每个阶段总伴随着一些方法的调用,这些方法就是生命周期的钩子函数。
钩子函数的作用:为开发人员在不同阶段操作组件提供了时机。
只有类组件才有生命周期
- 组件的生命周期可分成三个状态:
Mounting(挂载):插入真实 DOM
Updating(更新):正在被重新渲染
Unmounting(卸载):已移出真实 DOM - 组件的生命周期基本图:
- 生命周期解析
Mounting(挂载):
- constructor()
完成了React数据的初始化,只要使用了constructor()就必须写super(),否则会导致this指向错误。它接受两个参数:props和context,当想在函数内部使用这两个参数时,需使用super()传入这两个参数。- getDerivedStateFromProps(nextProps, prevState)
作用就是在根据当前新的 props 或者 state 来更新组件的 state,返回null则说明不需要更新state- render()
render() 方法是 class 组件中唯一必须实现的方法。创建虚拟dom,进行diff算法,更新dom树等- componentDidMount()
在组件挂载后(插入 DOM 树中)立即调用。只调用一次Updating(更新):
- getDerivedStateFromProps(nextProps, prevState)
作用就是在根据当前新的 props 来更新组件的 state,返回null则说明不需要更新state- shouldComponentUpdate()
组件接收到新的props或者state时调用,return true就会更新dom(使用diff算法更新),return false能阻止更新(不调用render)- render()
render() 方法是 class 组件中唯一必须实现的方法。创建虚拟dom,进行diff算法,更新dom树等- getSnapshotBeforeUpdate(prevProps, prevState)
getSnapshotBeforeUpdate 会在最终的渲染之前被调用,也就是说在 getSnapshotBeforeUpdate 中读取到的 DOM 元素状态【可以读取但无法使用最新DOM】是可以保证与 componentDidUpdate 中一致的。返回一个值,作为componentDidUpdate的第三个参数- componentDidUpdate()
在组件更新后会被立即调用。Unmounting(卸载):
- componentWillUnMount()
在组件卸载及销毁之前直接调用。Error Handling(错误处理)
- componentDidCatch(error,info)
任何一处的javascript报错会触发