英文官网: https://reactjs.org/
中文官网: https://react.docschina.org/
介绍描述
用于动态构建用户界面的 JavaScript 库(只关注于视图)
由Facebook开源
React的特点
声明式编码
组件化编码
React Native 编写原生应用
高效(优秀的Diffing算法)
React高效的原因
使用虚拟(virtual)DOM, 不总是直接操作页面真实DOM。
DOM Diffing算法, 最小化页面重绘。
组件名必须首字母大写
虚拟DOM元素只能有一个根元素
虚拟DOM元素必须有结束标签
1.定义虚拟DOM时,不要写引号。
2.标签中混入JS表达式时要用{}。
3.样式的类名指定不要用class,要用className。
4.内联样式,要用style={{key:value}}的形式去写。
5.只有一个根标签
6.标签必须闭合
7.标签首字母
(1).若小写字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错。
(2).若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
//1.创建函数式组件
function MyComponent(){
console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
return 我是用函数定义的组件(适用于【简单组件】的定义)
}
//2.渲染组件到页面
ReactDOM.render( ,document.getElementById('test'))
执行了ReactDOM.render(…之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
//1.创建类式组件
class MyComponent extends React.Component {
render(){
//render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
//render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。
console.log('render中的this:',this);
return 我是用类定义的组件(适用于【复杂组件】的定义)
}
}
//2.渲染组件到页面
ReactDOM.render( ,document.getElementById('test'))
执行了ReactDOM.render(…之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
原始
//1.创建组件
class Weather extends React.Component {
//构造器调用几次? ———— 1次
constructor(props) {
console.log('constructor');
super(props)
//初始化状态
this.state = { isHot: false, wind: '微风' }
//解决changeWeather中this指向问题
this.changeWeather = this.changeWeather.bind(this) //将绑定在实例原型对象上的方法绑定在实例上
}
//render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数
render() {
console.log('render');
//读取状态
const { isHot, wind } = this.state
return 今天天气很{isHot ? '炎热' : '凉爽'},{wind}
}
//changeWeather调用几次? ———— 点几次调几次
changeWeather() {
//changeWeather放在哪里? ———— Weather的原型对象上,供实例使用
//由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
//类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
console.log('changeWeather');
//获取原来的isHot值
const isHot = this.state.isHot
//严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
this.setState({ isHot: !isHot })
console.log(this);
//严重注意:状态(state)不可直接更改,下面这行就是直接更改!!!
//this.state.isHot = !isHot //这是错误的写法
}
}
状态必须通过setState进行更新,且更新是一种合并,不是替换。
简写
//1.创建组件
class Weather extends React.Component {
//初始化状态
state = { isHot: false, wind: '微风' }
render() {
const { isHot, wind } = this.state
return 今天天气很{isHot ? '炎热' : '凉爽'},{wind}
}
//自定义方法————要用赋值语句的形式 + 箭头函数
changeWeather = () => {
const isHot = this.state.isHot
this.setState({ isHot: !isHot })
}
}
ReactDOM.render(<Person name="jerry" age={19} sex="男" />, document.getElementById('test1'))
const p = { name: '老刘', age: 18, sex: '女' }
ReactDOM.render( , document.getElementById('test3'))
对象的解构只有在这里才能用,此处的{}不是指对象的浅拷贝
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
}
必须先指定接收的属性类型再指定是否必须
React v15.5 开始已弃用在React上携带propTypes这样React太重了
现在需引入prop-types,用于对组件标签属性进行限制
constructor(props){
//构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
// console.log(props);
super(props)
console.log('constructor',this.props);
}
简写
static propTypes = {...}
static defaultProps = {...}
//创建组件
function Person (props){
const {name,age,sex} = props
return (
- 姓名:{name}
- 性别:{sex}
- 年龄:{age}
)
}
Person.propTypes = {
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string,//限制sex为字符串
age:PropTypes.number,//限制age为数值
}
//指定默认标签属性值
Person.defaultProps = {
sex:'男',//sex默认值为男
age:18 //age默认值为18
}
<input ref="input1" type="text" placeholder="点击按钮提示数据"/>
<input ref={c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>
更新时会传入两次,第一次传入null
myRef = React.createRef()
...
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>
...
this.myRef.current.value
(1).通过onXxx属性指定事件处理函数(注意大小写)
a.React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性
b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效
(2).通过event.target得到发生事件的DOM元素对象 ——————————不要过度使用ref
现用现取
相当于vue中的双向数据绑定
//初始化状态
state = {
username:'', //用户名
password:'' //密码
}
//保存用户名到状态中
saveUsername = (event)=>{
this.setState({username:event.target.value})
}
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数有:Promise、setTimeout、arr.map()等等
//通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
function sum(a){
return(b)=>{
return (c)=>{
return a+b+c
}
}
}
//卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
//强制更新
this.forceUpdate()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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函数,父组件第一次引用子组件的时时不会调用的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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
}
虚拟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也是可以的
使用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库的支持)
在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、缺点:配置繁琐,前端请求资源时必须加前缀
工具库: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组件
//导航区的a标签改为Link标签
Demo
//展示区写Route标签进行路径的匹配
//的最外侧包裹了一个或
一般组件:
路由组件:
一般组件:components
路由组件:pages
一般组件:写组件标签时传递了什么,就能收到什么
路由组件:接收到三个固定的属性
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"
NavLink可以实现路由链接的高亮,通过activeClassName指定样式名
默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
开启严格匹配:
严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由
路由链接(携带参数) | 详情 |
---|---|
注册路由(声明接收) | |
接收参数 | this.props.match.params |
路由链接(携带参数) | 详情 |
---|---|
注册路由(无需声明,正常注册即可) | |
接收参数 | this.props.location.search |
备注 | 获取到的search是urlencoded编码字符串,需要借助querystring解析 |
路由链接(携带参数) | 详情 |
---|---|
注册路由(无需声明,正常注册即可) | |
接收参数 | 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()
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
HashRouter使用的是URL的哈希值。
BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
HashRouter的路径包含#,例如:localhost:3000/#/demo/test
(1).BrowserRouter没有任何影响,因为state保存在history对象中。
(2).HashRouter刷新后会导致路由state参数的丢失!!!
默认开启的是push模式,push模式就是说每次的点击跳转改变路径,都是往浏览器历史记录的栈中不断追加一条记录,然后你点回退按钮时,它会指向当前栈顶记录的前一条,replcae模式就是说替换掉当前的那条记录,然后你点回退的时候,就不会显示上次被替换掉的那条记录了,只会显示上上条记录,那要怎么设置为replace模式呢?直接在****标签上添加一个replace属性即可
作用:它就是专门解决在一般组件中想要使用路由组件的那几个API的这个问题的,它接收一个一般组件,然后调用后,该一般组件身上也有了路由组件的history、match等属性
import {withRouter} from 'react-router-dom'
class Header extends Component {}
export default withRouter(Header)
//withRouter可以加工一般组件,让一般组件具备路由组件所特有的API
//withRouter的返回值是一个新组件
官网: http://www.material-ui.com/#/
Github: https://github.com/callemall/material-ui
官网: https://ant.design/index-cn
Github: https://github.com/ant-design/ant-design/
它是专门做状态管理的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.该文件是用于创建一个为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
}
}
store.dispatch({type:'increment',data:value*1})
ReactDOM.render( < App / > , document.getElementById('root'))
store.subscribe(() => {
ReactDOM.render( < App / > , document.getElementById('root'))
})
//count_action.js 专门用于创建action对象
export const increase = data => ({type: INCREMENT, data})
//引入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)
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YEB5n4W4-1654776804156)(D:\黑马程序员$React\react全家桶资料\02_原理图\react-redux模型图.png)]
//引入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)
{
jia:createIncrementAction,
jian:createDecrementAction,
jiaAsync:createIncrementAsyncAction,
}
一个组件要和redux“打交道”要经过哪几步?
定义好UI组件—不暴露
引入connect生成一个容器组件,并暴露,写法如下:
connect(
state => ({key:value}), //映射状态
{key:xxxxxAction} //映射操作状态的方法
)(UI组件)
在UI组件中通过this.props.xxxxxxx读取和操作状态
为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
})
交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”。
import {composeWithDevTools} from 'redux-devtools-extension'
const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
npm run build 它会生成一个build文件夹
服务器搭建:
setState(stateChange, [callback])------对象式的setState
//异步调用
this.setState({count:count+1},()=>{
//更新后的值
console.log(this.state.count);
})
//更新前的值
console.log(this.state.count);
setState(updater, [callback])------函数式的setState
this.setState( state => ({count:state.count+1}))
使用原则:
//1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
import Loading from './Loading'
const Home = lazy(() => import('./Home'))
const About = lazy(() => import('./About'))
//2.通过指定在加载得到路由打包文件前显示一个自定义loading界面
}>
让函数组件也可以有state状态, 并进行状态数据的读写操作
const [xxx, setXxx] = React.useState(initValue)
useState()说明 | |
---|---|
参数 | 第一次初始化指定的值在内部作缓存 |
返回值 | 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数 |
setXxx()2种写法:
用于模拟类组件中的生命周期钩子
React.useEffect(() => {
// 在此可以执行任何带副作用操作
return () => { // 在组件卸载前执行
// 在此做一些收尾工作, 比如清除定时器/取消订阅等
}
}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
可以把 useEffect 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 (
Son
- state:{qggState}
- props:{props.qggProps}
)
}
这个标签就是用在有时页面结构层级太多,而且有些都是语法要求,实际没意义的结构层级(return()中的根节点就是这个情况),这时你就可以用Fragment标签,当然<>>在一般情况下和Fragment标签作用相同,当时有一点不一样,就是Fragment标签能接收一个key属性,而<>>什么属性都不能接收
import React, { Component, Fragment } from 'react'
一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信
创建Context容器对象:
const XxxContext = React.createContext()
渲染子组件时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
子组件
后代组件读取数据:
//第一种方式:仅适用于类组件
static contextType = xxxContext // 声明接收context
this.context // 读取context中的value数据
//第二种方式: 函数组件与类组件都可以
{
value => ( // value就是context中的value数据
要显示的内容
)
}
- 只要执行setState(),即使不改变状态数据,组件也会重新render() ==> 效率低
- 只要当前组件重新render(),就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低
Component中的shouldComponentUpdate()总是返回true
只有当组件的state或props数据发生改变时才重新render()
比较新旧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
}
shouldComponentUpdate(nextProps,nextState){
console.log(this.props,this.state); //目前的props和state
console.log(nextProps,nextState); //接下要变化的目标props,目标state
return !this.props.carName === nextProps.carName
}
PureComponent重写了shouldComponentUpdate(),只是进行state和props数据的浅比较
只有state或props数据有变化才返回true,如果只是数据对象内部数据变了,返回false
向组件内部动态传入带内容的结构(标签)
通过组件标签体传入结构
//父组件
xxxx
//子组件
{this.props.children}
问题: 如果B组件需要A组件内的数据 ==> 做不到
通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性
//祖组件
}>
//A组件
{this.props.render(内部state数据)}
//C组件读取A组件传入的数据
{this.props.data}
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 (
我是Parent组件
{this.state.hasError ? 当前网络不稳定,稍后再试
: }
)
}
}
props | children props render props |
---|---|
消息订阅-发布 | pubs-sub、event |
集中式管理 | redux、dva |
Context | 生产者-消费者模式 |
搭配方式
父子组件 | props |
---|---|
兄弟组件 | 消息订阅-发布、集中式管理 |
祖孙组件(跨级组件) | 消息订阅-发布、集中式管理、Context(开发用的少,封装插件用的多) |