react笔记

React尚硅谷张天禹笔记(更新中......)_尚硅谷react笔记_草原三件套的博客-CSDN博客

用于动态构建用户界面的 JavaScript 库(只关注于视图)

React的特点:

声明式编码

组件化编码

React Native 编写原生应用

高效(优秀的Diffing算法)

React高效的原因

使用虚拟(virtual)DOM, 不总是直接操作页面真实DOM。

DOM Diffing算法, 最小化页面重绘。


本质是Object类型的对象(一般对象)
虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。
虚拟DOM最终会被React转化为真实DOM,呈现在页面上
组件名必须首字母大写

虚拟DOM元素只能有一个根元素

虚拟DOM元素必须有结束标签

JSX
1.定义虚拟DOM时,不要写引号。

2.标签中混入JS表达式时要用{}。

3.样式的类名指定不要用class,要用className。

4.内联样式,要用style={{key:value}}的形式去写。

5.只有一个根标签

6.标签必须闭合

7.标签首字母

(1).若小写字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错。

(2).若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。

定义组件
函数式组件

执行了ReactDOM.render(…之后,发生了什么?

 1.React解析组件标签,找到了MyComponent组件。

2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。


执行了ReactDOM.render(…之后,发生了什么?

1.React解析组件标签,找到了MyComponent组件。

2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。

3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。

组件实例三大属性(state,props,refs)
state
原始
状态必须通过setState进行更新,且更新是一种合并,不是替换。

简写


箭头函数的this指向定义时所在的外层第一个普通函数,跟使用位置没有关系。
被继承的普通函数的this指向改变,箭头函数的this指向会跟着改变


props

ReactDOM.render(<Person name="jerry" age={19} sex="男" />, document.getElementById('test1'))
1
const p = { name: '老刘', age: 18, sex: '女' }
ReactDOM.render(<Person {...p} />, document.getElementById('test3'))
1
2
对象的解构只有在这里才能用,此处的{}不是指对象的浅拷贝

 

Person.propTypes = {
    name:PropTypes.string.isRequired, //限制name必传,且为字符串
    sex:PropTypes.string,//限制sex为字符串
    age:PropTypes.number,//限制age为数值
    speak:PropTypes.func,//限制speak为函数
}
//指定默认标签属性值
Person.defaultProps = {
    sex:'男',//sex默认值为男
    age:18 //age默认值为18
}
1
2
3
4
5
6
7
8
9
10
11
必须先指定接收的属性类型再指定是否必须

React v15.5 开始已弃用在React上携带propTypes这样React太重了

现在需引入prop-types,用于对组件标签属性进行限制

constructor(props){
    //构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
    // console.log(props);
    super(props)
    console.log('constructor',this.props);
}
1
2
3
4
5
6
简写

static propTypes = {...}
static defaultProps = {...}
1
2
函数
//创建组件
function Person (props){
    const {name,age,sex} = props
    return (
        <ul>
            <li>姓名:{name}</li>
            <li>性别:{sex}</li>
            <li>年龄:{age}</li>
        </ul>
)
}
Person.propTypes = {
    name:PropTypes.string.isRequired, //限制name必传,且为字符串
    sex:PropTypes.string,//限制sex为字符串
    age:PropTypes.number,//限制age为数值
}

//指定默认标签属性值
Person.defaultProps = {
    sex:'男',//sex默认值为男
    age:18 //age默认值为18
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
refs
字符串
<input ref="input1" type="text" placeholder="点击按钮提示数据"/>
1
回调函数
<input ref={c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>
1
更新时会传入两次,第一次传入null

createRef
myRef = React.createRef()
...
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>
...
this.myRef.current.value
1
2
3
4
5
事件处理
(1).通过onXxx属性指定事件处理函数(注意大小写)

a.React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性

b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效

(2).通过event.target得到发生事件的DOM元素对象 ——————————不要过度使用ref

收集表单数据
非受控组件
现用现取

受控组件
相当于vue中的双向数据绑定

//初始化状态
state = {
    username:'', //用户名
    password:'' //密码
}

//保存用户名到状态中
saveUsername = (event)=>{
    this.setState({username:event.target.value})
}
1
2
3
4
5
6
7
8
9
10
高阶函数
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。

1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。

2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。

常见的高阶函数有:Promise、setTimeout、arr.map()等等

函数柯里化
//通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。 
function sum(a){
    return(b)=>{
        return (c)=>{
            return a+b+c
        }
    }
}
1
2
3
4
5
6
7
8
生命周期函数
//卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
//强制更新
this.forceUpdate()
1
2
3
4
OLD
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CaxV5j0b-1654776804145)(D:\黑马程序员$React\react全家桶资料\02_原理图\react生命周期(旧)].png)

初始化阶段: 由ReactDOM.render()触发 初次渲染

constructor()

componentWillMount()

render()

componentDidMount()

一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息

更新阶段: 由组件内部this.setSate()或父组件render触发

shouldComponentUpdate()

componentWillUpdate()

render() =====> 必须使用的一个

componentDidUpdate()

卸载组件: 由ReactDOM.unmountComponentAtNode()触发

componentWillUnmount() =====> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

挂载时:先执行构造器(constructor)=》组件将要挂载(componentWillMount)=》组件挂载渲染(render)=》组件挂载完成(componentDidMount)=》组件销毁(componentWillUnmount)

组件内部状态更新:组件是否应该更新(shouldComponentUpdate)=》组件将要更新(componentWillUpdate)=》组件更新渲染(render)=》组件更新完成(componentDidUpdate)

强制更新:调用this.forceUpdate(),这个api和setState一样都是react自带的,一般这个强制更新很少用,它的执行流程就是比上述的正常更新流程少一步询问是否更新(shouldComponentUpdate)

父组件重新render:调用组件将要接收新props(componentWillReceiveProps)=》组件是否应该更新(shouldComponentUpdate)=》组件将要更新(componentWillUpdate)=》组件更新渲染(render)=》组件更新完成(componentDidUpdate)

注意:上述加粗的函数,只有在父组件状态发生改变了,重新调用render时才会调用子组件的componentWillReceiveProps函数,父组件第一次引用子组件的时时不会调用的

NEW
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w6e4vKIf-1654776804150)(D:\黑马程序员$React\react全家桶资料\02_原理图\react生命周期(新)].png)

新版生命周期函数和旧版的差别:

新版即将废弃老的3个钩子(componentWillMount、componentWillReceiveProps、componentWillUpdate)

新增了2个钩子(getDerivedStateFromProps、getSnapshotBeforeUpdate)

//若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
static getDerivedStateFromProps(props,state){
    console.log('getDerivedStateFromProps',props,state);
    return null
}

getSnapshotBeforeUpdate(){
    return this.refs.list.scrollHeight
}

componentDidUpdate(preProps,preState,height){
    this.refs.list.scrollTop += this.refs.list.scrollHeight - height
}
1
2
3
4
5
6
7
8
9
10
11
12
13
DOM的Diff算法
虚拟DOM中的key的作用:

当状态中的数据发生改变时,react会根据【新数据】生成【新虚拟DOM】,随后react会进行【新虚拟DOM】和【旧虚拟DOM】的diff算法比较,具体的比较规则如下:

若【旧DOM】中找到了与【新DOM】相同的key,则会进一步判断两者的内容是否相同,如果也一样,则直接使用之前的真实DOM,如果内容不一样,则会生成新的真实DOM,替换掉原先的真实DOM
若【旧DOM】中没找到与【新DOM】相同的key,则直接生成新的真实DOM,然后渲染到页面
用index作为key可能引发的问题

若对数据进行:逆序添加、逆序删除等破坏顺序的操作时会产生不必要的真实DOM更新,造成效率低下
如果结构中还包含输入类的dom,会产生错误dom更新,出现界面异常
开发中如何选择key

最好选中标签的唯一标识id、手机号等
如果只是简单的展示数据,用index也是可以的

React脚手架
使用create-react-app(脚手架工具)创建一个初始化项目

1、下载脚手架工具:npm i -g create-react-app

2、创建引用:create-react-app my-app

3、运行应用:cd my-app(进入应用文件夹),npm start(启动应用)

public ---- 静态资源文件夹

​ favicon.icon ------ 网站页签图标

​ index.html -------- 主页面

​ logo192.png ------- logo图

​ logo512.png ------- logo图

​ manifest.json ----- 应用加壳的配置文件

​ robots.txt -------- 爬虫协议文件

src ---- 源码文件夹

​ App.css -------- App组件的样式

​ App.js --------- App组件

​ App.test.js ---- 用于给App做测试

​ index.css ------ 样式

​ index.js ------- 入口文件

​ logo.svg ------- logo图

​ reportWebVitals.js

​ — 页面性能分析文件(需要web-vitals库的支持)

​ setupTests.js

​ ---- 组件单元测试的文件(需要jest-dom库的支持)

React脚手架配置代理
简单
在package.json中追加如下配置

“proxy”:“http://localhost:5000”

1、优点:配置简单,前端请求资源可以不加任何前缀
2、缺点:不能配置多个代理(如果请求的不同服务器就不行)
3、工作方式:当请求了自身3000端口不存在的资源时,那么会转发给5000端口(优先会匹配自身的资源,如果自己有就不会请求5000端口了)

麻烦
创建代理配置文件

在src下创建配置文件:src/setupProxy.js

编写代理配置规则

const {createProxyMiddleware:proxy} = require('http-proxy-middleware')

module.exports = function(app) {
  app.use(
    proxy('/api1', {  //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
      target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址)
      changeOrigin: true, //控制服务器接收到的请求头中host字段的值
      /*
          changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
          changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
          changeOrigin默认值为false,但我们一般将changeOrigin值设为true
      */
      pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
    }),
    proxy('/api2', { 
      target: 'http://localhost:5001',
      changeOrigin: true,
      pathRewrite: {'^/api2': ''}
    })
  )
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1、优点:可以配置多个代理,可以灵活控制请求是否走代理
2、缺点:配置繁琐,前端请求资源时必须加前缀

消息订阅-发布机制
工具库:PubSubJs

下载:npm install pubsub-js --save

先引入:import PubSub from “pubsub-js”

要接收数据方订阅    PubSub.subscribe(‘消息名’,(data)=>{ console.log(data) })
传递数据方发布    PubSub.publish(‘消息名’,data)
路由
一个路由就是一个映射关系
key永远为路径,value可能是function或者component

路由分类
后端路由
后端路由的key还是路径,只不过value是上述说的function

注册路由:router.get(path, function(req,res){…})
工作过程:当node接收到一个请求时,会根据请求路径去匹配对应的路由,然后调用对应路由中的函数来处理请求,返回响应数据

前端路由
浏览器端路由,value是对应组件(component),用于展示页面内容
注册路由:
工作过程:当浏览器path变为/test时,当前路由组件就会变成Test组件

react-router-dom
内置组件
路由的基本使用
//导航区的a标签改为Link标签
<Link to="/xxxxx">Demo</Link>
//展示区写Route标签进行路径的匹配
<Route path='/xxxx' component={Demo}/>
//<App>的最外侧包裹了一个<BrowserRouter>或<HashRouter>
1
2
3
4
5
路由组件与一般组件
写法不同
一般组件:

路由组件:

存放位置不同:
一般组件:components
路由组件:pages

接收到的props不同:
一般组件:写组件标签时传递了什么,就能收到什么
路由组件:接收到三个固定的属性

history:
    go: ƒ go(n)
    goBack: ƒ goBack()
    goForward: ƒ goForward()
    push: ƒ push(path, state)
    replace: ƒ replace(path, state)
location:
    pathname: "/about"
    search: ""
    state: undefined
match:
    params: {}
    path: "/about"
    url: "/about"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
NavLink与封装NavLink
NavLink可以实现路由链接的高亮,通过activeClassName指定样式名

Switch的使用
通常情况下,path和component是一一对应的关系。
Switch可以提高路由匹配效率(单一匹配)。
匹配一个成功后,后续相同路径组件也不会生效了
解决多级路径刷新页面样式丢失的问题
public/index.html 中 引入样式时不写 ./ 写 / (常用)
public/index.html 中 引入样式时不写 ./ 写 %PUBLIC_URL% (常用)
使用HashRouter
路由的严格匹配与模糊匹配
默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)

开启严格匹配:

严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

Redirect的使用
一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由

<Switch>
    <Route path="/about" component={About}/>
    <Route path="/home" component={Home}/>
    <Redirect to="/about"/>
</Switch>
1
2
3
4
5
嵌套路由
注册子路由时要写上父路由的 path 值
路由的匹配是按照注册路由的顺序进行的
向路由组件传递参数
params参数
路由链接(携带参数)    <Link to=‘/demo/test/tom/18’}>详情</Link>
注册路由(声明接收)    <Route path=“/demo/test/:name/:age” component={Test}/>
接收参数    this.props.match.params
search参数
路由链接(携带参数)    <Link to=‘/demo/test?name=tom&age=18’}>详情</Link>
注册路由(无需声明,正常注册即可)    <Route path=“/demo/test” component={Test}/>
接收参数    this.props.location.search
备注    获取到的search是urlencoded编码字符串,需要借助querystring解析
state参数
路由链接(携带参数)    <Link to={{pathname:‘/demo/test’,state:{name:‘tom’,age:18}}}>详情</Link>
注册路由(无需声明,正常注册即可)    <Route path=“/demo/test” component={Test}/>
接收参数    this.props.location.state
备注    刷新也可以保留住参数
编程式路由导航
借助this.props.history对象上的API对操作路由跳转、前进、后退

this.props.history.push()
this.props.history.replace()
this.props.history.goBack()
this.props.history.goForward()
this.props.history.go()
1
2
3
4
5
BrowserRouter与HashRouter的区别
底层原理不一样
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。

HashRouter使用的是URL的哈希值。

path表现形式不一样
BrowserRouter的路径中没有#,例如:localhost:3000/demo/test

HashRouter的路径包含#,例如:localhost:3000/#/demo/test

刷新后对路由state参数的影响
(1).BrowserRouter没有任何影响,因为state保存在history对象中。

(2).HashRouter刷新后会导致路由state参数的丢失!!!

push与replace
默认开启的是push模式,push模式就是说每次的点击跳转改变路径,都是往浏览器历史记录的栈中不断追加一条记录,然后你点回退按钮时,它会指向当前栈顶记录的前一条,replcae模式就是说替换掉当前的那条记录,然后你点回退的时候,就不会显示上次被替换掉的那条记录了,只会显示上上条记录,那要怎么设置为replace模式呢?直接在**<Link replace to=‘XXX’>**标签上添加一个replace属性即可

withRouter
作用:它就是专门解决在一般组件中想要使用路由组件的那几个API的这个问题的,它接收一个一般组件,然后调用后,该一般组件身上也有了路由组件的history、match等属性

import {withRouter} from 'react-router-dom'

class Header extends Component {}
export default withRouter(Header)

//withRouter可以加工一般组件,让一般组件具备路由组件所特有的API
//withRouter的返回值是一个新组件
1
2
3
4
5
6
7
UI组件库
material-ui(国外)
官网: http://www.material-ui.com/#/

Github: https://github.com/callemall/material-ui

ant-design(国内蚂蚁金服)
官网: https://ant.design/index-cn

Github: https://github.com/ant-design/ant-design/

redux
简介
它是专门做状态管理的js库,不是react插件库

它可以用在angular、vue、react等项目中,但与react配合用到最多

集中式管理react应用中多个组件共享的状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NLlNdpoR-1654776804155)(D:\黑马程序员$React\react全家桶资料\02_原理图\redux原理图.png)]

基本使用
去除Count组件自身的状态

src下建立:

redux/

​ store.js

​ count_reducer.js

store.js:

//引入redux中的createStore函数,创建一个store
import {createStore} from 'redux'
//createStore调用时要传入一个为其服务的reducer
import countReducer from './count_reducer'
//暴露store对象
export default createStore(countReducer)
1
2
3
4
5
6
count_reducer.js:
/* 
    1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数,返回加工后的状态
    2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
    3.reducer有两个作用:初始化状态,加工状态,reducer被第一次调用时,是store自动触发的
*/
import {
    INCREMENT,
    DECREMENT
} from "./constant";

const initState = 0
export default function countReducer(previousState = initState, action) {
    console.log(previousState, action); //undefined {type: '@@redux/INIT1.m.x.g.h.b'}
    const {
        type,
        data
    } = action
    switch (type) {
        case INCREMENT:
            return previousState + data
        case DECREMENT:
            return previousState - data
        default:
            return previousState
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
在index.js中监测store中状态的改变,一旦发生改变重新渲染**<App/>**
store.dispatch({type:'increment',data:value*1})
1
ReactDOM.render( < App / > , document.getElementById('root'))
store.subscribe(() => {
    ReactDOM.render( < App / > , document.getElementById('root'))
})
1
2
3
4
备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。
action
//count_action.js 专门用于创建action对象
export const increase = data => ({type: INCREMENT, data})
1
2
异步action
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
export default createStore(countReducer, applyMiddleware(thunk))


//count_reducer.js
export const createIncrementAsyncAction = (data,time) => {
    return (dispatch)=>{
        //异步任务有结果后,分发一个同步的action去真正操作数据
        setTimeout(()=>{
            //异步action中一般都会调用同步action
            dispatch(createIncrementAction(data))
        },time)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
react-redux
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YEB5n4W4-1654776804156)(D:\黑马程序员$React\react全家桶资料\02_原理图\react-redux模型图.png)]

react-redux基本使用
//引入Count的UI组件
import CountUI from '../../components/Count'
//引入action
import {
    createIncrementAction,
    createDecrementAction,
    createIncrementAsyncAction
} from '../../redux/count_action'

//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'

/* 
    1.mapStateToProps函数返回的是一个对象;
    2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
    3.mapStateToProps用于传递状态
*/
function mapStateToProps(state){
    return {count:state}
}

/* 
    1.mapDispatchToProps函数返回的是一个对象;
    2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
    3.mapDispatchToProps用于传递操作状态的方法
*/
function mapDispatchToProps(dispatch){
    return {
        jia:number => dispatch(createIncrementAction(number)),
        jian:number => dispatch(createDecrementAction(number)),
        jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
    }
}

//使用connect()()创建并暴露一个Count的容器组件
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
react-redux优化
容器组件和UI组件整合一个文件
无需自己给容器组件传递store,给<App/>包裹一个**<Provider store={store}></Provider>**即可
使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作
mapDispatchToProps也可以简单的写成一个对象
{
    jia:createIncrementAction,
        jian:createDecrementAction,
            jiaAsync:createIncrementAsyncAction,
}
1
2
3
4
5
一个组件要和redux“打交道”要经过哪几步?

定义好UI组件—不暴露
引入connect生成一个容器组件,并暴露,写法如下:

connect(
    state => ({key:value}), //映射状态
    {key:xxxxxAction} //映射操作状态的方法
)(UI组件)
1
2
3
4
在UI组件中通过this.props.xxxxxxx读取和操作状态

react-redux数据共享版
为Person组件编写:reducer、action,配置constant常量。

重点:Person的reducer和Count的Reducer要使用combineReducers进行合并,合并后的总状态是一个对象!!!

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware,combineReducers} from 'redux'

//汇总所有的reducer变为一个总的reducer
const allReducer = combineReducers({
    he:countReducer,
    rens:personReducer
})
1
2
3
4
5
6
7
8
交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”。

react-redux开发者工具的使用
import {composeWithDevTools} from 'redux-devtools-extension'
const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
1
2
react-redux最终版
所有变量名字要规范,尽量触发对象的简写形式。
reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer
项目打包运行
npm run build 它会生成一个build文件夹

服务器搭建:

用node+express可以搭建一个简单的服务器
需要用到一个库serve,使用前需要先下载npm i serve -g,然后进入build文件夹中执行serve即可
拓展
setState
对象式
setState(stateChange, [callback])------对象式的setState

stateChange为状态改变对象(该对象可以体现出状态的更改)
callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
//异步调用
this.setState({count:count+1},()=>{
    //更新后的值
    console.log(this.state.count);
})
//更新前的值
console.log(this.state.count);
1
2
3
4
5
6
7
函数式
setState(updater, [callback])------函数式的setState

updater为返回stateChange对象的函数。
updater可以接收到state和props。
callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
this.setState( state => ({count:state.count+1}))
1
使用原则:

如果新状态不依赖于原状态 ===> 使用对象方式
如果新状态依赖于原状态 ===> 使用函数方式
如果需要在setState()执行后获取最新的状态数据,要在第二个callback函数中读取
lazyLoad
//1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
import Loading from './Loading'
const Home = lazy(() => import('./Home'))
const About = lazy(() => import('./About'))

//2.通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面
<Suspense fallback={<Loading />}>
        <Switch>
            <Route path="/xxx" component={Xxxx}/>
            <Redirect to="/login"/>
        </Switch>
</Suspense>
1
2
3
4
5
6
7
8
9
10
11
12
Hooks
React Hook/Hooks是什么
Hook是React 16.8.0版本增加的新特性/新语法
可以让你在函数组件中使用 state 以及其他的 React 特性
State Hook
让函数组件也可以有state状态, 并进行状态数据的读写操作

const [xxx, setXxx] = React.useState(initValue)
1
useState()说明    
参数    第一次初始化指定的值在内部作缓存
返回值    包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
setXxx()2种写法:

setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
Effect Hook
用于模拟类组件中的生命周期钩子

React.useEffect(() => { 
    // 在此可以执行任何带副作用操作
        return () => { // 在组件卸载前执行
        // 在此做一些收尾工作, 比如清除定时器/取消订阅等
    }
}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
1
2
3
4
5
6
可以把 useEffect Hook 看做如下三个函数的组合

componentDidMount()
componentDidUpdate()
componentWillUnmount()
Ref Hook
可以在函数组件中存储/查找组件内的标签或任意其它数据

const myRef = React.useRef()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gPRbdsO3-1654776804157)(C:\Users\邱嘎噶\AppData\Roaming\Typora\typora-user-images\image-20220605171554829.png)]

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'

export default function Son(props) {
    // 使用refs
    const qggRef = React.useRef()
    const showRef = () => {
        alert(qggRef.current.innerHTML)
    }
    // 使用state
    const [qggState, setQggState] = React.useState('qggState')
    const changeQggState = () => {
        setQggState(value => value + 'nb')
    }
    const death = () => {
        ReactDOM.unmountComponentAtNode(document.getElementById('root'))
    }
    // 使用Effect模仿三个生命周期钩子
    React.useEffect(() => {
        const timer = setInterval(
            () => {
                setQggState(value => value + 'nb')
            }, 1000)
        return () => {
            clearInterval(timer)
        }
    }, [])
    return (
        <div className="son">
            <span>Son</span>
            <ul>
                <li ref={qggRef}>state:{qggState}</li>
                <li>props:{props.qggProps}</li>
                <li><button onClick={showRef}>refs</button></li>
            </ul>
            <button onClick={changeQggState}>setQggState</button><br />
            <button onClick={death}>death</button><br />
        </div>
    )
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Fragment
这个标签就是用在有时页面结构层级太多,而且有些都是语法要求,实际没意义的结构层级(return()中的根节点就是这个情况),这时你就可以用Fragment标签,当然<></>在一般情况下和Fragment标签作用相同,当时有一点不一样,就是Fragment标签能接收一个key属性,而<></>什么属性都不能接收

<Fragment><Fragment>
import React, { Component, Fragment } from 'react'
<Fragment key={1}>
    <input type="text"/>
    <input type="text"/>
</Fragment>
1
2
3
4
5
<></>
Context
一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信

创建Context容器对象:

const XxxContext = React.createContext()  
1
渲染子组件时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:

<xxxContext.Provider value={数据}>
    子组件
</xxxContext.Provider>
1
2
3
后代组件读取数据:

//第一种方式:仅适用于类组件 
  static contextType = xxxContext  // 声明接收context
  this.context // 读取context中的value数据

//第二种方式: 函数组件与类组件都可以
  <xxxContext.Consumer>
    {
      value => ( // value就是context中的value数据
        要显示的内容
      )
    }
  </xxxContext.Consumer>
1
2
3
4
5
6
7
8
9
10
11
12
组件优化
Component的2个问题
只要执行setState(),即使不改变状态数据,组件也会重新render() ==> 效率低
只要当前组件重新render(),就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低
原因
Component中的shouldComponentUpdate()总是返回true

效率高的做法
只有当组件的state或props数据发生改变时才重新render()

解决
重写shouldComponentUpdate()方法
比较新旧state或props数据,如果有变化才返回true,如果没有返回false

缺点:得一个一个写

父组件
shouldComponentUpdate(nextProps,nextState){
    // console.log(this.props,this.state); //目前的props和state
    // console.log(nextProps,nextState); //接下要变化的目标props,目标state
    return !this.state.carName === nextState.carName
}
1
2
3
4
5
子组件
shouldComponentUpdate(nextProps,nextState){
    console.log(this.props,this.state); //目前的props和state
    console.log(nextProps,nextState); //接下要变化的目标props,目标state
    return !this.props.carName === nextProps.carName
}
1
2
3
4
5
PureComponent
PureComponent重写了shouldComponentUpdate(),只是进行state和props数据的浅比较

只有state或props数据有变化才返回true,如果只是数据对象内部数据变了,返回false

render props
向组件内部动态传入带内容的结构(标签)

children props
通过组件标签体传入结构

//父组件
<A>
  <B>xxxx</B>
</A>
//子组件
{this.props.children}
1
2
3
4
5
6
问题: 如果B组件需要A组件内的数据 ==> 做不到

render props
通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性

//祖组件
<A render = {data => <C data={data} />}></A>
//A组件
{this.props.render(内部state数据)}
//C组件读取A组件传入的数据
{this.props.data} 
1
2
3
4
5
6
错误边界
Error boundary:用来捕获后代组件错误,渲染出备用页面

只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误

使用方式:getDerivedStateFromError配合componentDidCatch

import React, { Component } from 'react'
import Child from './Child'

export default class Parent extends Component {

    state = {
        hasError: '' //用于标识子组件是否产生错误
    }

    // 当Parent的子组件出现报错时候,会触发getDerivedStateFromError调用,并携带错误信息
    // 在render之前触发
    // 返回新的state
    static getDerivedStateFromError(error) {
        console.log(error);
        return { hasError: error }
    }

    componentDidCatch() {
        console.log('此处统计错误,反馈给服务器,用于通知编码人员进行bug的解决');
    }

    render() {
        return (
            <div>
                <h2>我是Parent组件</h2>
                {this.state.hasError ? <h2>当前网络不稳定,稍后再试</h2> : <Child />}
            </div>
        )
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
组件通信方式总结
props    children props
render props
消息订阅-发布    pubs-sub、event
集中式管理    redux、dva
Context    生产者-消费者模式
搭配方式

父子组件    props
兄弟组件    消息订阅-发布、集中式管理
祖孙组件(跨级组件)    消息订阅-发布、集中式管理、Context(开发用的少,封装插件用的多)

————————————————
版权声明:本文为CSDN博主「草原三件套」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_52617854/article/details/125131757

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值