React 16&18 笔记
Date: 2023-03-30 20:36:05
视频链接:尚硅谷React教程
ps:开始学 react 了😆
练习项目地址
前置知识
三件套 HTML、CSS、JS(ES6 语法)
当然学个 TS 更好,有助于理解,对于react,你 js 有多6,react 就有多6
P1 react 简介
React 只关注视图,不关心数据
笔记基于版本 react@16
,在记录过程中引入并使用高版本特性
P2 react 案例
babel.min.js \\ 用来将 jsx 翻译成 js 文件的。 react.development.js \\ 开发,react 核心库 react-dom.development.js \\ 操作 DOM,react 周边库
React 基本实现
<div id="test"></div> <script> const VDOM= `<h1>hello,world!</h1>` ReactDOM.render(VDOM,document.getElementById('test')) </script>
P3 虚拟 DOM 的两种创建方式
为什么要使用 jsx 写法呢?
方便的创建 VDOM,在 JS 里写 HTML
// 第一种创建方式如上 const VDOM = ( <h1 id="title"> <span>Hello,React</span> </h1> ); // 第二种方式 const VDOM = React.createElement("h1",{ id: "title" }, React.createElement("span", {}, "Hello,React") ); // 若是多级元素,则需要嵌套着写,比较麻烦
babel
将 jsx
翻译为第二种方式,所以 jsx
也是一种简写语法糖.
P4 虚拟DOM与真实DOM
VDOM
即 虚拟DOM
,TDOM
即 真实DOM
VDOM
是一个 Obj
,但相对 TDOM
这个对象较简单,即 VDOM
较为轻量
P5 jsx 语法规则
XML 被 JSON 取代了,
JSON => Obj
,可用 JSON.parse
Obj => JSON
,可用 JSON.stringfy
在 localStorage 可用其存 Obj、Arr。
jsx 语法规则,🌠 以下为具体规则,其在下面实例代码中体现
- 定义VDOM,使用 ()
- 标签混入 JS 表达式,使用
{}
- 如果想要写类名,需要使用驼峰命名法
className
- 如果想要写样式 在标签上
style={{color:'pink',fontSize:60px}}
VDOM
只能有一个根标签(类似 vue2)- 标签务必闭合
- 标签首字母小写会解析成HTML元素,大写为组件
const myId = "imID"; const myData = "HeLlo,world!"; //1.创建虚拟DOM const VDOM = ( <div> <h2 className="title" id={myId.toLowerCase()}> <span style={{ color: "pink", fontSize: "60px" }}> {myData.toLowerCase()} </span> </h2> <input type="text" /> </div> ); //2.渲染虚拟DOM到页面 ReactDOM.render(VDOM, document.getElementById("test"));
P6 jsx 小练习
js 表达式与 js 语句的区别?
js 表达式 会 return 一个 val,用 console.log()
即可查看
js 语句 则不会返回值
面试题也会问
常用方法
const arr=[1,2,3] const result = arr.map(num => num+1) // [2,3,4]
P7 组件与模块
- 模块 js 代码封装的功能
- 组件 js,html,css 的集合体(有必要将 img,video 放在一起吗?可在 assets 文件夹里,建立文件夹)
- 模块化、组件化、工程化
P8 开发者工具的安装
在 chrome
的插件商店安装 react
,redux
即可,需FQ
react
的 profiler
为性能测试工具
P9 函数式组件
//1.创建函数式组件 function ReactFunctionComponent() { console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式 return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>; } ReactDOM.render( //2.渲染组件到页面 <ReactFunctionComponent />, document.getElementById("test") );
执行过程
React
解析标签过程中,发现首字母大写标签- 调用函数组件,然后将
VDOM => TDOM
P10 复习类相关知识
看这里即可,不想复制了🤔
P11 类式组件
React 中可以写函数式组件和类式组件,用的比较多的是类式组件,
因为函数式相对类处理业务较为麻烦,Hooks 出来后,函数式组件也可使用 State 之类的,但一般稍微复杂点的组件还是用 类式组件进行书写
P12~19 React三大属性 state
对 state 的理解 P12
state
跟 vue的data()
类似,用来存储业务相关数据的
初始化 state P13
定义 state
react中的事件绑定 P14
假如你想在元素上添加一个点击事件,跟 vue 模板语法类似,使用 onClick={handleClick}
在标签上书写,然后在类中定义方法。
类中的 this 指向问题 P15
class Person { handleClick = ()=>{ //箭头函数 this 指向父元素,即 react组件实例,可直接调用 ··· } // 第二种 handle() { //存在 this 指向问题,class使用局部严格模式,指向 undefind ··· } }
解决类中的 this 指向问题 P16
如果用第二种写法,存在 this 指向问题,可以在构造函数中将 handClick 指向原型对象,非实例对象
constructor() { //构造函数,用于初始化状态,执行 1 次 this.handleClick = this.handleClick.bind(this) //将原型 this => 实例 this // 之后在 handClick 方法中,this => 实例 this } handle() { // this => 实例 console.log(this) }
当然用箭头函数可以完美避免这个问题🤔
setState 的使用 P17
setState
是 react实例对象原型链
上的一个方法,可将其内部数据合并到 实例对象state
中
this.setState({ //setState是一个异步函数,执行 n 次 isHot: "春天来了" }) console.log(this) // this 为实例对象
类式组件标准写法
//1.创建虚拟DOM class ClassComponent extends React.Component { constructor(props) { //构造函数,用于初始化状态,执行 1 次 super(props); this.state = { isHot: true, wind: "习习微风" }; this.handleClick = this.handleClick.bind(this) //将原型 this => 实例 this } handleClick() { // 方法使用的时机是 原型类实例化之后,所以 this 指向的是实例对象 // 类中的方法开启了局部严格模式 ,this指向的是undefined this.setState({ //setState是一个异步函数,执行 n 次 isHot: !this.state.isHot, }) } render() { //render函数,用于渲染虚拟DOM,执行 n+1 次 return ( <h1 onClick={this.handleClick}> 我是 ClassComponent,今天的天气{this.state.isHot ? "热" : "凉"} </h1> ); } } //2.渲染虚拟DOM到页面 ReactDOM.render(<ClassComponent />, document.getElementById("test"));
state 的简写方式 P18(类式组件简写方式)
state 写在构造器外边,指向不变
class ClassComponent extends React.Component { state = { isHot: true }; //初始化状态 handleClick = () => { this.setState({ isHot: !this.state.isHot }); }; render() { return ( <h1 onClick={this.handleClick}> 我是 ClassComponent,今天的天气{this.state.isHot ? "热" : "凉"} </h1> ); } }
总结 state P19
理解
state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
- 组件中render方法中的this为组件实例对象
- 组件自定义的方法中this为undefined,如何解决?
- 强制绑定this: 通过函数对象的bind()
- 箭头函数
- 状态数据,不能直接修改或更新
setState
更新合并
P20~26 React三大属性 props
props 的基本使用 P20
父组件 => 子组件
传递数据的一种方式,跟 vue 的 props 差不多
class ClassComponent extends React.Component { render() { const { name, age, sex } = this.props; return ( <ul> <li>姓名:{name}</li> <li>年龄:{age}</li> <li>性别:{sex}</li> </ul> ); } } //2.渲染虚拟DOM到页面 ReactDOM.render( <ClassComponent name="gavin" age="25" sex="男" />, document.getElementById("test") );
批量传递 props P21
在标签里传递对象,<ClassComponent {...Obj}/>
原生 js 只能使用展开运算符展开 Arr,不能展开 Obj,此处是 react+babel
的结果
对 props 进行限制 P22
react@16
之前默认生效,之后需要引入单独的包 propTypes
TS 出现之后,这东西的类型限制感觉没什么必要了
Person.propTypes = { //对标签属性进行类型、必要性的限制 name:PropTypes.string.isRequired, //限制name必传,且为字符串 sex:PropTypes.string, //限制sex为字符串 speack:PropTypes.func // 函数 } Person.defaultProps = { //指定默认标签属性值 sex:'男',//sex默认值为男 age:18 } const p = { name: "tom", age: 18, speak() { console.log("我说话了"); }, }; <Person {...p}/> // html
props 的简写方式 P23
react 使用的是不可变数据实现响应式的
类型限制可以写在 类式组件的类
里面
class Person { static propTypes = { //对标签属性进行类型、必要性的限制 name:PropTypes.string.isRequired, //限制name必传,且为字符串 sex:PropTypes.string, //限制sex为字符串 speack:PropTypes.func // 函数 } static defaultProps = { //指定默认标签属性值 sex:'男',//sex默认值为男 age:18 } render(){ return ( // jsx data ) } }
类式组件中构造器与 props P24
构造器最大的作用是使用且重构
继承父类的构造器方法。
开发时尽量不要写构造器
函数式组件使用 props P25
直接看例子即可
function Person(props) { const { name, age } = props; return ( <ul> <li>{name}</li> <li>{age}</li> </ul> ); } ReactDOM.render( <Person name="gavin" age={18} />, document.getElementById("test") );
想使用类型限制,直接在函数后添加即可 Person.propTypes={}
总结 props P26
限制 props
类型时注意大小写
P27~31 React三大属性 ref
ref 就是打标识,相当于简化的 ID 属性?用于快速获取标签
字符串形式的 ref P27
String类型的ref 字符串写法可能废弃,存在效率问题,操作DOM?
回调形式的 ref P28
就是将当前节点,以回调的形式挂在 react组件实例对象上。
回调ref中调用次数的问题 P29
每次更新会调用一次回调函数,不会影响功能。
定义class绑定函数可以完美解决这个问题。
开发就写成内联回调,怎么快怎么来
createRef 的使用 P30
react 最推荐的使用方式,书写跟 定义class绑定函数
差不多。
总结 ref P31
尽可能避免 String 类型 Ref
就行了,以下为例子
Ref 例子
export default class RefLearn extends Component { myRef = React.createRef(); //createRef创建ref空间 handleInput = (c) => { //类绑定函数 this.input2 = c; }; handleClick = () => { console.log("this对象", this) console.log("String类型ref(尽量避免)----", this.refs.input3.value) console.log("回调ref----" + this.input1.value); console.log("类绑定函数----" + this.input2.value); console.log("createRef----" + this.myRef.current.value); }; render() { return ( <div> <input placeholder="回调ref写法" type="text" ref={(c) => { this.input1 = c; console.log("回调ref", c); }} /> <input placeholder="类绑定函数写法" type="text" ref={this.handleInput} /> <input placeholder="createRef 写法" type="text" ref={this.myRef} /> <input placeholder="String类型ref(尽量避免)" type="text" ref="input3" /> <button onClick={this.handleClick}>点击打印值</button> </div> ); } }
P32 react 中的事件处理
- 通过
onXxx 属性
指定事件处理函数(注意大小写)- React 使用的是自定义(合成)事件, 而不是使用的原生DOM事件 更好的兼容性
- React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) 更高效
onXxx 属性
会带过来一个event.target
,能得到发生事件的DOM元素对象 不要过度使用 ref,追求简洁高效的代码
P33 非受控组件
收集表单数据
现用现取
P34 受控组件
输入框的内容一直管理。相当于 vue 的双向数据绑定
P35 高阶函数-函数柯里化
高阶函数: 如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
- 若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
- 若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数有:Promise、setTimeout、arr.map()
等等
函数的柯里化:
通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
function sum(a){ return(b)=>{ return (c)=>{ return a+b+c } } }
P36 不用柯里化的写法
传参之前想办法将参数处理一下
<input change={(event)=>{return sum(event.value,b)}}/>
然后参数同时处理 function sum(a,b,c){ return a+b+c }
P37~47 React 生命周期
引出生命周期 ---P37
- 组件从创建到死亡它会经历一些特定的阶段。
- React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。
- 我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。
旧的生命周期 ---P38~42
旧生命周期图

左边为组件挂载流程,右边为组件更新流程
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染 1. constructor() 2. componentWillMount() 3. render() - componentDidMount() =====> 常用 一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息 2. 更新阶段: 由组件内部this.setSate()或父组件render触发 1. shouldComponentUpdate() 2. componentWillUpdate() 3. render() =====> 必须使用的一个 4. componentDidUpdate() 3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发 - componentWillUnmount() =====> 常用 一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
- setState 流程
shouldComponentUpdate( return true)
控制组件更新 - forceUpdate 流程
forceUpdate()
强制更新 - render 流程
常用重要的钩子
componentDidMount()
初始化操作,发送请求,开启定时器等componentWillUnmount()
关闭定时器,取消订阅等render()
渲染更新页面结构
新旧生命周期对比 ---P43
在 react@17
中,UNSAFE_ 表示有可能废除,在未来版本有可能有 bug,且这三个钩子不常用,被滥用
新生命周期图

多了个 getSnapshotBeforeUpdate
新的生命周期 ---P44~47
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染 1. constructor() 2. getDerivedStateFromProps 3. render() - componentDidMount() =====> 常用 一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息 2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发 1. getDerivedStateFromProps 2. shouldComponentUpdate() 3. render() 4. getSnapshotBeforeUpdate 5. componentDidUpdate() 3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发 - componentWillUnmount() =====> 常用 一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
getDerivedStatetFormProps()
得到 props 的派生属性,state 的值由 props 决定。了解即可,没啥意义getSnapshotBeforeUpdate()
获取组件更新之前的信息,如 滚动位置,这个钩子的作用相当于vue2 的 beforeUpdated()
P48 DOM 的 diff 算法
P49 初始化 react 脚手架
npm 安装 react 脚手架
现在都流行用 vite 创建 react 项目
P50 脚手架文件介绍 public
P53 样式的模块化
让组件间的样式不冲突,vue
可在 style 标签配置 scope 字段进行限制 <style scope></style>
react 解决组件同名样式冲突的方式
一、可以使用预编译语言 sass\less
,样式嵌套来解决
二、利用ES6模块化思路,引入的时候
- 将组件样式名称修改为
index.module.css
- 引入的时候
import hello from 'index.module.css'
- 在使用时,
<h3 className={hello.title}></h3>
P54 vscode 中 react 插件的安装
react 插件快速生成结构 rcc rfc
P55 组件化编码流程
-
合理拆分组件
-
实现静态组件界面效果
-
实现动态组件
- 动态展示初始数据
- 设计交互
P56~64 TodoList 案例
P75~93 react-router-dom@5
此处是 react-router@5
版本
对路由的理解 ---P75
前端路由原理 ---P76
通过 windown.history 对象
实现路由操作
React 中路由的基本使用 ---P77
-
yarn add react-router-dom@5
库 -
明确好界面中的导航区、展示区,之后
- 导航区的a标签改为Link标签
<Link to="/xxxxx">Demo</Link>
- 展示区写Route标签进行路径的匹配
<Route path='/xxxx' component={Demo}/>
<App>
的最外侧包裹了一个<BrowserRouter>
或<HashRouter>
- 导航区的a标签改为Link标签
路由组件与一般组件 ---P78
- 页面上的是一般组件
- 控制路由匹配显示的是路由组件
路由组件能收到 props.history
对象
路由组件与一般组件的对比
- 写法不同:
一般组件:<Demo/>
路由组件:<Route path="/demo" component={Demo}/>
- 存放位置不同:
一般组件:components
路由组件:pages - 接收到的 props 不同:
一般组件:写组件标签时传递了什么,就能收到什么
路由组件:接收到 history对象的三个固定的属性
history 对象信息
history: go: ƒ go(n) goBack: ƒ goBack() goForward: ƒ goForward() push: ƒ push(path, state) replace: ƒ replace(path, state) location: pathname: "/about" search: "" // search 传数据 state: undefined // state 传数据 match: params: {} // params 传数据 path: "/about" url: "/about"
NavLink 的使用和封装 ---P79-80
实现点击追加高亮效果,点击追加 active
属性样式
对原生路由组件进行封装,props 数据传过去就行了
组件标签体的内容,如何传给组件?
- vue2 有插槽
- 组件
this.props.children
可以收集到 父组件在自定义组件中写的内容
Switch 的使用 ---P81
增加匹配效率,点到即止不再匹配
通常情况下,path 和 component 是一一对应的关系,Switch 可以提高路由匹配效率(单一匹配)。
解决样式丢失问题 ---P82
二级路径时,刷新请求资源会改变,导致页面使用资源失效
如果请求资源不存在,返回 index.html
- 相对路径删掉
public/index.html
中 引入样式时不写 ./
,写 /
(常用) - 引用,三方css用绝对路径
public/index.html
中 引入样式时不写 ./
,写 %PUBLIC_URL%
(常用) - 用
HashRouter
解决
路由的严格匹配与模糊匹配 ---P83
包管理器尽量使用 yarn
,不要混用,混用可能出现 bug
- 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
- 开启严格匹配:
- 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
Redirect的使用 ---P84
上来选中 About 组件
一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到 Redirect 指定的路由
具体编码:
<Switch> <Route path="/about" component={About}/> <Route path="/home" component={Home}/> <Redirect to="/about"/> </Switch>
嵌套路由(多级路由) ---P85
注册子路由时要写上父路由的 path 值
<Route path="/home/news" component={News}/>
路由的匹配是按照注册路由的顺序进行的
向路由组件传递参数 ---P86~89
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
备注:刷新也可以保留住参数
push 与 replace ---P90
在跳转标签上添加 <Link replace={true}/>
实现覆盖式跳转
编程式路由导航 ---P91
借助 this.props.history
对象上的API对操作路由 跳转、前进、后退
- this.props.history.push() - this.props.history.replace() - this.props.history.goBack() - this.props.history.goForward() - this.props.history.go()
withRouter 的使用 ---P92
让一般组件可以路由跳转
因为只有路由组件有 History 对象
import { withRouter } from 'react-router-dom'; export default withRouter(Header)
BrowserRouter与HashRouter的区别 ---P93
底层原理不一样:
- BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
- HashRouter使用的是URL的哈希值。
path表现形式不一样
- BrowserRouter的路径中没有#,
例如:localhost:3000/demo/test - HashRouter的路径包含#,
例如:localhost:3000/#/demo/test
刷新后对路由state参数的影响
- BrowserRouter 没有任何影响,因为state保存在history对象中。
- HashRouter 刷新后会导致路由state参数的丢失!!!
备注:HashRouter可以用于解决一些路径错误相关的问题。
react-redux P97~114
redux 简介 ---P97
redux 工作流程 ---P98
求和案例 ---P99-100
redux 理解 ---103
P116~126 React 新特性(Hooks 方法式组件写法)
setSate ---P116
- 对象式 setState
setState(stateChangeObj, [callback] )
- 函数式 setState ,能看到
state
和props
this.setState((state,props)=>{ return 对象 }, [callback])
- setState() 是同步行为, 引起的动作更新页面是异步行为,方法回调callback在最异步行为完成后运行
- 对象式写法是函数式写法的语法糖
lazyLoad ---P117
组件实现路由懒加载
Suspense
- 先引入
import {lazy,Susoense} from 'react'
- 对要懒加载的组件进行定义
const Home = lazy(()=> import('./Home'))
- 在 jsx 中使用组件
<Suspense fallback={ <h3>加载中</h3>} > <Route path='/home' component={Home} /> </Suspense>
setHooks ---P118~120
react@16.8 之后
在函数式组件中使用 react 特性
函数式组件求和案例
Demo(){}
相当于render
React Hook/Hooks是什么?
(1). Hook是React 16.8.0版本增加的新特性/新语法
(2). 可以让你在函数组件中使用 state 以及其他的 React 特性
三个常用的Hook
- State Hook:
useState()
使用三大属性 State - Effect Hook:
useEffect()
使用 React 生命周期 - Ref Hook:
useRef()
使用三大属性 Ref
State Hook
State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
useState() 说明:
- 参数: 第一次初始化指定的值在内部作缓存
- 返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
setXxx() 2种写法:
setXxx(newValue)
: 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值setXxx(value => newValue)
: 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
代码示例
import { useState } from "react"; //引入 export default function MyState() { const [count, setCount] = useState(1);; // 使用 function handle() { setCount((count) => count + 1); } }
Effect Hook
Effect Hook 可以让你在函数组件中执行副作用操作
(用于 模拟类组件中的生命周期钩子
)
React中的副作用操作:
- 发ajax请求数据获取
- 设置订阅 / 启动定时器
- 手动更改真实DOM
代码示例
import { useState, useEffect } from "react"; export default function MyLife() { const [now, setNow] = useState(0); useEffect(() => { //使用副作用 // 在此可以执行任何带副作用操作 //componentDidMount(),componentDidUpdate() let Timer = setInterval(() => { setNow((now) => now + 1); }, 1000); return () => { // 在组件卸载前执行一些收尾工作 componentWillUnmount clearInterval(Timer); }; }, [0]); // 如果指定的是[], 回调函数只会在第一次render()后执行 }
可以把 useEffect Hook 看做如下三个函数的组合
- componentDidMount()
- componentDidUpdate()
- componentWillUnmount()
Ref Hook
在函数组件中存储/查找组件内的标签或任意其它数据
作用: 保存标签对象,功能与 React.createRef()
一样
代码示例
import { useRef } from "react"; export default function MyRefs() { const inputVal = useRef(); return ( <input type="text" ref={inputVal} /> ) }
Fragment ---P121
Fragment 碎片,不用写根标签了 跟 vue3 一样,少了一层
代码示例
<Fragment><Fragment> <></>
Context ---P122 🤔🤔🤔
Context 上下文
用于【祖组件】===>【后代组件】间通信
使用
1) 创建Context容器对象: const XxxContext = React.createContext() 2) 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据: <xxxContext.Provider value={数据}> 子组件 </xxxContext.Provider> 3) 后代组件读取数据: //第一种方式:仅适用于类组件 static contextType = xxxContext // 声明接收context this.context // 读取context中的value数据 //第二种方式: 函数组件与类组件都可以 <xxxContext.Consumer> { value => ( // value就是context中的value数据 要显示的内容 ) } </xxxContext.Consumer>
注意:在应用开发中一般不用context, 一般都它的封装react插件
PureComponent ---P123
Component的问题
只要执行setState(),即使不改变状态数据, 组件也会重新render(),也会自动重新render子组件,效率低
效率高的做法: 只有当组件的state或props数据发生改变时才重新render()
原因: Component 中的 shouldComponentUpdate() 总是返回true
解决
- > 一、 重写
shouldComponentUpdate()
方法
比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false - >二、 使用
PureComponent
PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
注意:- 只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false
- 不要直接修改state数据, 而是要产生新数据
- 项目中一般使用 PureComponent 来优化
- react 实现响应式使用的是不可变数据,所以修改数据要覆盖原数据
renderProps ---P124
react 实现插槽功能
- Vue中:
使用slot技术, 也就是通过组件标签体传入结构<AA><BB/></AA>
- React中:
- 使用children props: 通过组件标签体传入结构,存在问题
- 使用render props: 通过组件标签属性传入结构, 一般用render函数属性
children props
<A> <B>xxxx</B> </A> {this.props.children}
问题: 如果B组件需要A组件内的数据, 做不到
render props
<A render={(data) => {<C data={data}></C>} }></A>
A组件: {this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示 {this.props.data}
ErrorBoundary ---P125
防止容易发生的错误导致页面出不来
理解 错误边界:用来捕获后代组件错误,渲染出备用页面
特点
只能捕获 后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误
使用方式
getDerivedStateFromError 配合 componentDidCatch
// 生命周期函数,一旦后台组件报错,就会触发 static getDerivedStateFromError(error) { console.log(error); // 在render之前触发 // 返回新的state return { hasError: true, }; } componentDidCatch(error, info) { // 统计页面的错误。发送请求发送到后台去 console.log(error, info); }
React 组件间通信方式总结 ---P126
- 父子组件
- 兄弟组件
- 跨级组件
- props,传函数方法
- ref
- pubsub
- redux
- context,封装插件
P127 React Router @6
vue router5 是为了新的组合式api 服务的
react router 6 是为了函数式组件服务的
路由表的使用 ---P131
"react-router-dom": "^6.10.0"
创建 routes 文件,写好配置
使用 useRoutes 钩子
多级路由 ---P132
import { Outlet } from "react-router-dom"; export default function index() { return (<Outlet/>) }
路由的 params 参数 ---P133
作用:回当前匹配路由的params
参数,类似于5.x中的match.params
。
示例代码
路由表 path 写参数进行占位,父组件写路由跳转时传参数, 子组件收值
// 路由表占坑 path: "rparams/:id/:age/:sex", // 路由组件收参 import { useParams,useMatch } from 'react-router-dom'; function ProfilePage() { let { name } = useParams(); // 获取URL中携带过来的params参数 } // 父组件传参 <NavLink to={`rparams/${name}/${age}/${sex}`} >
路由的 search 参数 ---P134
不用路由表占位,直接在父组件里定义好即可,然后子组件接收
// 父组件传参 <NavLink to={`rparams?name=${name}&age=${age}&sex=${sex}`}>A</NavLink> // 子组件收参 import { useSearchParams,useLocation } from "react-router-dom"; export default function MyParams() { //setSearch 更新地址栏收到的参数 const [search, setSearch] = useSearchParams(); const name = search.get("name"); const age = search.get("age"); const sex = search.get("sex"); }
路由的 State 参数 ---P135
利用 useLocation 对象,最好用的方法
// 父组件传值 <NavLink to="rparams" state={{ name, age, sex }}>Params</NavLink> // 子组件接收 import { useLocation } from "react-router-dom"; export default function MyParams() { const { state: { name, age, sex }, } = useLocation(); }
编程式路由导航 ---P136
import { useNavigate } from "react-router-dom"; export default function index() { const navigate = useNavigate();// 仅支持 携带State 参数 handleRoute = (e) =>{ navigate('route', { replace: false, state: { id: 1, title: "One", content: "isContent", }, }); } }
useRouterContext ---P137
USE: 用来判断组件是否被路由管理
作用:如果组件在
useNavigationType
USE: 获取用户是如何来到当前页面的
作用:返回当前的导航类型。
返回值:POP、PUSH、REPLACE。
备注:POP 是指在浏览器中直接打开了这个路由组件(刷新页面来的)。
useOutlet()
作用:用来呈现当前组件中渲染的嵌套路由。
示例代码:
const result = useOutlet() console.log(result)
- 如果嵌套路由没有挂载,则result为null
- 如果嵌套路由已经挂载,则展示嵌套的路由对象
useResolvedPath()
给定一个 URL值,解析其中的:path、search、hash值。
总结 P141
react-router@6 的设计目的,是为了函数式组件服务的,所以 5 的东西,6都写成了可以按需引入的形式,提高了开发人员的效率
本文作者:悠悠江水
本文链接:https://www.cnblogs.com/isgavin/p/17274566.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步