React
安装
- 全局安装脚手架
create-react-app
npm i -g create-react-app
- 创建项目
create-react-app projectName
- 打开项目
cd projectName
- 启动项目
npm start
- 项目默认的配置项是隐藏的,通过执行以下命令可以暴露配置项
npm run eject
文件结构
React和ReactDom
React负责逻辑控制,数据 -> VDOM,创建虚拟DOM
react中的的JSX语法
JSX => React.createElement(…)
ReactDom渲染实际DOM,VDOM -> DOM 。
ReactDOM.render(
<div>react</div>,
document.getElementById('root')
);
JSX
JSX是⼀种JavaScript的语法扩展,其格式⽐较像模版语⾔,但事
实上完全是在JavaScript内部实现的。
使用JSX
1、表达式{}
的使用
const name = 'react';
const jsx = <h1>{name}</h1>
2、函数
function sayHi(name){
return `${name} says hi`;
}
const jsx = <h1>{sayHi('react')}</h1>
3、条件语句
const name = 'react';
const show = true
const content = show ? <h1>{name}</h1> : null
const jsx = <h1>{content}</h1>
4、数组
const arr = [1,2,3,4].map(val =><li key={val}>{val}</li>)
const jsx=(
<div>
<ul>{arr}</ul>
</div>
)
5、属性
静态值用双引号,动态值则是一个花括号。
import logo from './logo.svg'
const jsx = (
<div>
<img src={logo} style={{width:50}} />
</div>
)
组件
组件是抽象的独⽴功能模块,react应⽤程序由组件构建⽽成。
组件有两种形式
function组件 和 class组件
1、 class组件
class组件通常拥有状态和⽣命周期,继承了 Component,实现render⽅法。
创建一个组件
import React,{ Component } from 'react'
export default class Com1 extends Component{
render(){
const content = '这是一个组件'
return (
<div>
{content}
</div>
)
}
}
在需要使用该组件是时候直接引入即可。
import Com1 from '@/components/com1'
2、 function组件
函数组件通常⽆状态,仅关注内容展示,返回渲染结果即可。
function com2(){
return(
<div>
这是function组件
</div>
)
}
从React16.8开始引⼊了hooks,函数组件也能够拥有状态。
组件状态管理
如果组件中数据会变化,并影响⻚⾯内容,则组件需要拥有状态(state)并维护状态。
class组件中的状态管理
class组件使⽤state和setState维护状态
创建一个组件
import React,{ Component } from 'react'
export default class Com1 extends Component{
constructor(props){
super(props); // 继承Component
this.state={
name : '组件1'
}
// 使⽤state属性维护状态,在构造函数中初始化状态
}
render(){
return (
<div>
{this.state.name}
</div>
)
}
}
state中的状态不能直接更改
this.state.name = '这就是组件1啊' // 错误
而是要采用setState()
this.setState({
name:'这就是组件1啊'
})
而setState()
方法是异步的,也就说你在该函数里面改变某个值后,立即调用该属性,其值并未发生改变。
this.setState({
name:'这就是组件1啊'
})
console.log(this.state.name) // 组件1
如果想在改变后立即调用,有以下三种方式:
- 传递函数给setState方法
this.setState({
name:'这是组件1啊'
},()=>{
console.log(this.state.name) // 这是组件1啊
})
import React,{ Component } from 'react'
export default class Com1 extends Component{
constructor(props){
super(props);
this.state={
counter: 0
}
}
componentDidMount(){
this.setState({
counter:this.state.counter+3
})
this.setState({
counter:this.state.counter+1
}) // 调用了两次,其实只执行了最后一个+1
}
render(){
return (
<div>
{this.state.counter} // {/* 1 */}
</div>
)
}
}
this.setState((nextState,props)=>({
counter:nextState.counter+1
}))
this.setState((nextState,props)=>({
counter:nextState.counter+1
}))
// 此时执行counter 为 2
- 使用定时器
其实就是利用了事件队列。
componentDidMount() {
setTimeout(() => {
this.changeValue();
console.log(this.state.name); // 这就是组件1啊
}, 0);
}
changeValue = () => {
this.setState({
name: '这就是组件1啊' // 初始值是 组件1
})
}
- 原生事件中修改
componentDidMount() {
document.body.addEventListener('click',this.changeValue,false)
}
changeValue = () => {
this.setState({
name: '这就是组件1啊'
})
}
总结: setState只有在合成事件和⽣命周期函数中是异步的,在原⽣事件如addEventListener和setTimeout、setInterval中都是同步的。
原⽣事件绑定不会通过合成事件的⽅式处理,⾃然也不会进⼊更新事务的处理流程。setTimeout也⼀样,在setTimeout回调执⾏时已经完成了原更新组件流程,也不会再进⼊异步更新流程,其结果⾃然就是是同步的了。
function组件中的状态管理
函数组件通过hooks api维护状态
import React,{ useState,useEffect } from 'react'
export default function Com2(){
const [name,setName] = useState('组件2')
useEffect(()=>{
setTimeout(()=>{
setName('这是组件2了')
})
})
return (
<div>{name}</div>
)
}
其实可以把useEffect()
看作是class组件中的componentDidMount()
、componentDidUpdate()
以及componentWillUnmount()
三者的组合。
事件处理
事件回调函数注意绑定this指向,常⻅三种⽅法:
- 构造函数中绑定并覆盖:this.change =this.change.bind(this)
- ⽅法定义为箭头函数:change = ()=>{},使⽤箭头函数,不需要指定回调函数this,且便于传递参数
- 事件中定义为箭头函数:onChange={()=>this.change()}
react⾥遵循单项数据流,没有双向绑定,输⼊框要设置value
和onChange,称为受控组件
组件通信
props传递
属性:
// 父
<Son name="leo"></Son>
// son.js
<h1>{this.props.name}</h1>
函数
// 父
<Son change={this.onChange}></Son>
// son.js
this.props.change(this.state.name) // 可以把子组件的信息传入父组件,这个叫做状态提升。
生命周期
v16.4之后的生命周期中废弃了以下三个方法:
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
如果要使用的话需要在头部加上UNSAFE_
;
同时引入了两个新的生命周期函数:
- static getDerivedStateFromProps
- getSnapshotBeforeUpdate
废弃缘由:
原来(React v16.0前)的⽣命周期在React v16推出的Fiber之后
就不合适了,因为如果要开启async rendering,在render函数
之前的所有函数,都有可能被执⾏多次。
原来(React v16.0前)的⽣命周期有哪些是在render前执⾏的呢?
- componentWillMount
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
static getDerivedStateFromProps(props,state)
getDerivedStateFromProps 会在调⽤ render ⽅法之前调⽤,并且在初始挂载及后续更新时都会被调⽤。它应返回⼀个对象来更新 state,如果返回 null 则不更新任何内容。
请注意,不管原因是什么,都会在每次渲染前触发此⽅法。与UNSAFE_componentWillReceiveProps 形成对⽐,后者仅在⽗组件重新渲染时触发,⽽不是在内部调⽤ setState 时。
static getDerivedStateFromProps(props, state) 在组件创建时和更新时的render⽅法之前调⽤,它应该返回⼀个对象来更新状态,或者返回null来不更新任何内容。
import React, { Component } from 'react'
export default class Com1 extends Component {
constructor(props) {
super(props);
this.state = {
name: '组件1',
}
}
static getDerivedStateFromProps(props,state){
const {name} = state
return name !== '组件11'?null:{name:'如果输入结果等于组件11就返回这个state'}
}
change(e){
this.setState({
name:e.target.value
})
}
render() {
return (
<div>
{this.state.name} {/* 1 */}
<input onChange={(e)=>this.change(e)} value={this.state.name}/>
</div>
)
}
}
getSnapshotBeforeUpdate(prevProps, prevState)
getSnapshotBeforeUpdate() 在最近⼀次渲染输出(提交到DOM 节点)之前调⽤。它使得组件能在发⽣更改之前从 DOM中捕获⼀些信息(例如,滚动位置)。此⽣命周期的任何返回值将作为参数传递给 componentDidUpdate()。就相当于获取了上一次渲染的state的值。
此⽤法并不常⻅,但它可能出现在 UI 处理中,如需要以特殊⽅
式处理滚动位置的聊天线程等。
import React, { Component } from 'react'
export default class Com1 extends Component {
constructor(props) {
super(props);
this.state = {
name: '组件1',
}
}
getSnapshotBeforeUpdate(prevProps,prevState){
const {name} = prevState
console.log('get Snapshot: ',name) // 输出组件1
return null
}
componentDidUpdate(){ // 假如第一次更新name的值为‘组件11’
console.log('componentDidUpdate: ',this.state.name) // 输出组件11
}
change(e){
this.setState({
name:e.target.value
})
}
render() {
return (
<div>
{this.state.name} {/* 1 */}
<input onChange={(e)=>this.change(e)} value={this.state.name}/>
</div>
)
}
}