react.js
:React核心库。react-dom.js
:提供操作DOM的react扩展库。babel.min.js
:解析JSX语法代码转为JS代码的库。
1_使用jsx创建虚拟DOM
使用jsx创建虚拟DOM
/*
关于虚拟DOM:
1.本质是Object类型的对象(一般对象)
2.虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。
3.虚拟DOM最终会被React转化为真实DOM,呈现在页面上。
*/
jsx语法规则:
1.定义虚拟DOM时,不要写引号。
2.标签中混入JS表达式时要用{}。
3.样式的类名指定不要用class,要用className。
4.内联样式,要用style={{key:value}}的形式去写。
5.只有一个根标签
6.标签必须闭合
7.标签首字母
(1).若小写字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错。
(2).若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
一定注意区分:【js语句(代码)】与【js表达式】
1.表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
下面这些都是表达式:
(1). a
(2). a+b
(3). demo(1)
(4). arr.map()
(5). function test () {}
2.语句(代码):
下面这些都是语句(代码):
(1).if(){}
(2).for(){}
(3).switch(){case:xxxx}
- state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
- 组件被称为"状态机",通过更新组件的state来更新对应的页面显示(重新渲染组件)
// 创建一个列表展示个人信息【姓名】【性别】【年龄】
第一种方式(React v15.5 开始已弃用)
Person.propTypes = {
name: React.PropTypes.string.isRequired,
age: React.PropTypes.number
}
第二种方式(新):使用prop-types库进限制(需要引入prop-types库)
Person.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.
}
示例代码
内联形式的回调ref,函数会被调用两次;
类的绑定函数形式,调用一次;
示例代码
受控组件:页面中说有输入类的DOM(比如:input,select,checkbox),随着用户的输入,将输入的内容维护到状态state中去,等需要用的时候,从状态中取出来。这就属于受控组件。(类比于Vue的双向数据绑定)。现用现取就是非受控,随着输入维护状态就是受控。非受控组件使用ref实现,受控组件的优势能够减少ref的使用。
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数有:Promise、setTimeout、arr.map()等等
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
function sum(a){
return(b)=>{
return (c)=>{
return a+b+c
}
}
}
//创建组件
class Login extends React.Component{
//初始化状态
state = {
username:'', //用户名
password:'' //密码
}
//保存表单数据到状态中
saveFormData = (dataType)=>{
return (event)=>{
this.setState({[dataType]:event.target.value})
}
}
//表单提交的回调
handleSubmit = (event)=>{
event.preventDefault() //阻止表单提交
const {username,password} = this.state
alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
}
render(){
return(
)
}
}
//渲染组件
ReactDOM.render( ,document.getElementById('test'))
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2JcwhzpR-1648792072214)(https://note.youdao.com/yws/api/personal/file/CAA3B1141398461ABDF949249072A201?method=download&shareKey=7b75d75c486dc54de58d8bd23147e8d6)]
生命周期的三个阶段(旧)
/*
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1. constructor()
2. componentWillMount()
3. render()
4. componentDidMount() =====> 常用
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件render触发
1. shouldComponentUpdate()
2. componentWillUpdate()
3. render() =====> 必须使用的一个
4. componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount() =====> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
*/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I1ZGqtCW-1648792072214)(https://note.youdao.com/yws/api/personal/file/56CFE17764CC45C080A38C501ABB5CA5?method=download&shareKey=8fc6ee84f9e3e627f00fd4453d055777)]
生命周期的三个阶段(新)
/*
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1. constructor()
2. getDerivedStateFromProps
3. render()
4. componentDidMount() =====> 常用
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1. getDerivedStateFromProps
2. shouldComponentUpdate()
3. render()
4. getSnapshotBeforeUpdate
5. componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount() =====> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
*/
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M4JYZHj5-1648792072215)(https://note.youdao.com/yws/api/personal/file/2353537E8B60428285E27EFB0A2CEED8?method=download&shareKey=faa39c0ea2c37f96d15d9e4832221291)]
/*
1). react/vue中的key有什么作用?(key的内部原理是什么?)
2). 为什么遍历列表时,key最好不要用index?
1. 虚拟DOM中key的作用:
1). 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。
2). 详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
(1).若虚拟DOM中内容没变, 直接使用之前的真实DOM
(2).若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
b. 旧虚拟DOM中未找到与新虚拟DOM相同的key:
根据数据创建新的真实DOM,随后渲染到到页面
2. 用index作为key可能会引发的问题:
1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
2. 如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题。
3. 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,
仅用于渲染列表用于展示,使用index作为key是没有问题的。
3. 开发中如何选择key?:
1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
2.如果确定只是简单的展示数据,用index也是可以的。
*/
1.xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目
1.包含了所有需要的配置(语法检查、jsx编译、devServer…)
2.下载好了所有相关的依
3.可以直接运行一个简单效果
2.react提供了一个用于创建react项目的脚手架库: create-react-app
3.项目的整体技术架构为: react + webpack + es6 + eslint
4.使用脚手架开发的项目的特点: 模块化, 组件化, 工程化
npm i -g create-react-app
create-react-app hello-react
cd hello-react
npm start
react_staging 项目名称
├── package.json 包管理文件
├── public 静态资源文件夹
│ ├── favicon.ico 网页页签图标
│ ├── index.html 主页面
│ ├── logo192.png logo图
│ ├── logo512.png logo图
│ ├── manifest.json 应用加壳的配置文件
│ └── robots.txt 爬虫配置文件
├── README.md 项目说明文件
├── src 源码文件
│ ├── App.css 组件的样式文件
│ ├── App.js App组件
│ ├── App.test.js 用于给App组件做测试
│ ├── index.css 入口样式(全局通用样式)
│ ├── index.js 入口文件
│ ├── logo.svg logo图
│ ├── reportWebVitals.js 页面性能分析文件(需要web-vitals库的支持)
│ └── setupTests.js 组件单元测试的文件(需要jest-dom库的支持)
└── yarn.lock
public/index.html
React App
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
// render函数只执行一次,将App组件渲染出来
ReactDOM.render(
// React.StrictMode检查代码存在不合理的地方
,
document.getElementById('root')
);
// 页面性能检测
reportWebVitals();
ES7 React/Redux/GraphQL/React-Native snippets
可以用很多编写react文件时的快捷键。
每个组件有自己的样式文件(index.css),在一起引入同一个组件中时,同名样式会在合并时,被后引入的组件样式覆盖。
解决办法:
第一种:在样式外包裹一层样式名,这样就通过增加层级,避免了重名问题。
第二种:将样式文件重命名为index.module.css
,再引入和使用(使用较少)。
import React,{Component} from 'react'
import hello from './index.module.css'
export default class Hello extends Component{
render(){
return Hello,React!
}
}
/*
随机生成字符串的第三方库uuid,nanoid。其中uuid偏大。
安装方法
*/
npm i uuid/nanoid
yarn add uuid/nanoid
// 引入方法
import {nanoid} from 'nanoid'
//调用方法,返回随机字符串
nanoid()
// 鼠标移入移除效果
export default class Item extends Component{
state = {mouse:false}
handleMouse = (flag) =>{
return ()=>{
this.setState({mouse:flag})
}
}
render() {
const {name,done} = this.props
const {mouse} = this.state
return (
)
}
}
// 安装react的props添加类型和必要性限制
yarn add prop-types
// 引入
import PropTypes from 'prop-types'
// 使用
static propTypes = {
addTodo: PropTypes.func.isRequired
}
1.拆分组件、实现静态组件,注意:className、style的写法
2.动态初始化列表,如何确定将数据放在哪个组件的state中?
——某个组件使用:放在其自身的state中
——某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
3.关于父子之间通信:
1.【父组件】给【子组件】传递数据:通过props传递
2.【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
4.注意defaultChecked 和 checked的区别,类似的还有:defaultValue 和 value
5.状态在哪里,操作状态的方法就在哪里
https://github.com/axios/axios
// 安装
yarn add axios
跨域问题:跨域问题的本质是同源策略的限制
在package.json中追加如下配置
"proxy":"http://localhost:5000"
说明:
第一步:创建代理配置文件
在src下创建配置文件:src/setupProxy.js
编写setupProxy.js配置具体代理规则:
// 代码使用的是CJS的语法不是ES6的语法。因为是给webpack使用的,用的node
// react脚手架预下载的中间件
const 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': ''}
})
)
}
说明:
const this = { keyWordElement : {value:'javascript'} }
// 结构且重命名
const { keyWordElement : { value : keyWord}}=this
console.log(keyWord)
后端解决跨域的一劳永逸的方法,只需要加上特殊的响应头。所有的网站都能访问你。
import React, { Component } from 'react'
import Search from './components/Search'
import List from './components/List'
export default class App extends Component {
state = { //初始化状态
users:[], //users初始值为数组
isFirst:true, //是否为第一次打开页面
isLoading:false,//标识是否处于加载中
err:'',//存储请求相关的错误信息
}
//更新App的state
updateAppState = (stateObj)=>{
this.setState(stateObj)
}
render() {
return (
)
}
}
采用主流的PubSubJS
npm install pubsub-js --save
// 或者
yarn add pubsub-js
import PubSub from 'pubsub-js'
// or when using CommonJS
const PubSub = require('pubsub-js');
// create a function to subscribe to topics
var mySubscriber = function (msg, data) {
console.log( msg, data );
};
// add the function to the list of subscribers for a particular topic
// we're keeping the returned token, in order to be able to unsubscribe
// from the topic later on
var token = PubSub.subscribe('MY TOPIC', mySubscriber);
// publish a topic asynchronously
PubSub.publish('MY TOPIC', 'hello world!');
// publish a topic synchronously, which is faster in some environments,
// but will get confusing when one topic triggers new topics in the
// same execution chain
// USE WITH CAUTION, HERE BE DRAGONS!!!
PubSub.publishSync('MY TOPIC', 'hello world!');
// 组件A 订阅
componentDidMount(){
this.token = PubSub.subscribe('atguigu',(_,stateObj)=>{
this.setState(stateObj)
})
}
componentWillUnmount(){
PubSub.unsubscribe(this.token)
}
// 组件B 发布
search = ()=>{
//获取用户的输入(连续解构赋值+重命名)
const {keyWordElement:{value:keyWord}} = this
//发送请求前通知List更新状态
PubSub.publish('atguigu',{isFirst:false,isLoading:true})
//发送网络请求
axios.get(`/api1/search/users?q=${keyWord}`).then(
response => {
//请求成功后通知List更新状态
PubSub.publish('atguigu',{isLoading:false,users:response.data.items})
},
error => {
//请求失败后通知App更新状态
PubSub.publish('atguigu',{isLoading:false,err:error.message})
}
)
}
jQuey
和axios
都是基于XMLHttpRequest的api封装的。fetch和xhr是一个级别的。内置的方法,直接可用,promise风格。
search = async()=>{
//获取用户的输入(连续解构赋值+重命名)
const {keyWordElement:{value:keyWord}} = this
//发送请求前通知List更新状态
PubSub.publish('atguigu',{isFirst:false,isLoading:true})
//发送网络请求---使用fetch发送(未优化)
/* fetch(`/api1/search/users2?q=${keyWord}`).then(
response => {
console.log('联系服务器成功了');
return response.json()
},
error => {
console.log('联系服务器失败了',error);
return new Promise(()=>{})
}
).then(
response => {console.log('获取数据成功了',response);},
error => {console.log('获取数据失败了',error);}
) */
//发送网络请求---使用fetch发送(优化)
try {
const response= await fetch(`/api1/search/users2?q=${keyWord}`)
const data = await response.json()
console.log(data);
PubSub.publish('atguigu',{isLoading:false,users:data.items})
} catch (error) {
console.log('请求出错',error);
PubSub.publish('atguigu',{isLoading:false,err:error.message})
}
}
let obj = {a:{b:1}}
const {a} = obj; //传统解构赋值
const {a:{b}} = obj; //连续解构赋值
const {a:{b:value}} = obj; //连续解构赋值+重命名
try {
const response= await fetch(`/api1/search/users2?q=${keyWord}`)
const data = await response.json()
console.log(data);
} catch (error) {
console.log('请求出错',error);
}
// 安装
yarn add react-router-dom
1. 写法不同:
一般组件:
路由组件:
2. 存放位置不同:
一般组件:components
路由组件:pages
3. 接收到的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"
// 或者
querystring
—处理url的库。
//编码类型urlencoded
key=value&key=value
1.params参数
路由链接(携带参数):详情
注册路由(声明接收):
接收参数:this.props.match.params
2.search参数
路由链接(携带参数):详情
注册路由(无需声明,正常注册即可):
接收参数:this.props.location.search
备注:获取到的search是urlencoded编码字符串,需要借助querystring解析
3.state参数
路由链接(携带参数):详情
注册路由(无需声明,正常注册即可):
接收参数:this.props.location.state
备注:刷新也可以保留住参数
借助this.prosp.history对象上的API对操作路由跳转、前进、后退
// withRouter的使用
import React, { Component } from 'react'
import {withRouter} from 'react-router-dom'
class Header extends Component {
back = ()=>{
this.props.history.goBack()
}
forward = ()=>{
this.props.history.goForward()
}
go = ()=>{
this.props.history.go(-2)
}
render() {
console.log('Header组件收到的props是',this.props);
return (
React Router Demo
)
}
}
export default withRouter(Header)
//withRouter可以加工一般组件,让一般组件具备路由组件所特有的API
//withRouter的返回值是一个新组件
前端路由的原理:点击路由链接引起路径的变化,然后被路由器监测到,多次改变路径是能够产生历史记录的。
1. 底层原理不一样:
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
HashRouter使用的是URL的哈希值。类比于锚点,#后面的路由不会发送给服务器,但是会形成历史记录。
2. path表现形式不一样
BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
HashRouter的路径包含#,例如:localhost:3000/#/demo/test
3. 刷新后对路由state参数的影响
(1).BrowserRouter没有任何影响,因为state保存在history对象中。
(2).HashRouter刷新后会导致路由state参数的丢失!!!(原因,没有基于history的API,无法保存记录)
4. 备注:HashRouter可以用于解决一些路径错误相关的问题。
// 安装组件库(包)
yarn add antd
1. 安装依赖:yarn add react-app-rewired customize-cra babel-plugin-import less less-loader
2. 修改package.json
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
3. 根目录下创建config-overrides.js
//配置具体的修改规则
const { override, fixBabelImports,addLessLoader} = require('customize-cra');
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: true,
}),
addLessLoader({
lessOptions:{
javascriptEnabled: true,
modifyVars: { '@primary-color': 'green' },
}
}),
);
4. 备注:不用在组件里亲自引入样式了,即:import 'antd/dist/antd.css'应该删掉
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zTRWQp3A-1648792072215)(https://note.youdao.com/yws/api/personal/file/0B98D5DE780645138E579F2C08766E83?method=download&shareKey=3c3523c607f325185c49ada7d59e7a7c)]
作用:创建包含指定reducer的store对象
作用:应用上基于redux的中间件(插件库)
作用:合并多个reducer函数
(1).去除Count组件自身的状态
(2).src下建立:
-redux
-store.js
-count_reducer.js
(3).store.js:
1).引入redux中的createStore函数,创建一个store
2).createStore调用时要传入一个为其服务的reducer
3).记得暴露store对象
(4).count_reducer.js:
1).reducer的本质是一个函数,接收:preState,action,返回加工后的状态
2).reducer有两个作用:初始化状态,加工状态
3).reducer被第一次调用时,是store自动触发的,
传递的preState是undefined,
传递的action是:{type:'@@REDUX/INIT_a.2.b.4'}
(5).在index.js中监测store中状态的改变,一旦发生改变重新渲染
备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。
新增文件:
1.count_action.js 专门用于创建action对象
2.constant.js 放置容易写错的type值
(1).明确:延迟的动作不想交给组件自身,想交给action
(2).何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。
(3).具体编码:
1).yarn add redux-thunk,并配置在store中
2).创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
3).异步任务有结果后,分发一个同步的action去真正操作数据。
(4).备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uvdCTVYU-1648792072216)(https://note.youdao.com/yws/api/personal/file/279755EB2F07473686C118705734B4CF?method=download&shareKey=08a9a701e6af33bcc02c14582fcdc092)]
connect(mapStateToProps,mapDispatchToProps)(UI组件)
//使用connect()()创建并暴露一个Count的容器组件
export default connect(
state => ({count:state}),
//mapDispatchToProps的一般写法
/* dispatch => ({
jia:number => dispatch(createIncrementAction(number)),
jian:number => dispatch(createDecrementAction(number)),
jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
}) */
//mapDispatchToProps的简写
{
jia:createIncrementAction,
jian:createDecrementAction,
jiaAsync:createIncrementAsyncAction,
}
)(Count)
// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'
import {Provider} from 'react-redux'
// 批量给应用中的所有容器组件传递store对象
ReactDOM.render(
,
document.getElementById('root')
)
定义好UI组件—不暴露
引入connect生成一个容器组件,并暴露,写法如下:
connect(
state => ({key:value}), //映射状态
{key:xxxxxAction} //映射操作状态的方法
)(UI组件)
在UI组件中通过this.props.xxxxxxx读取和操作状态
yarn add redux-devtools-extension
//在redux核心文件store.js中修改
import {composeWithDevTools} from 'redux-devtools-extension'
const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
npm i serve -g
serve -s build/serve build
(1). setState(stateChange, [callback])------对象式的setState
1.stateChange为状态改变对象(该对象可以体现出状态的更改)
2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
(2). setState(updater, [callback])------函数式的setState
1.updater为返回stateChange对象的函数。
2.updater可以接收到state和props。
4.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
总结:
1.对象式的setState是函数式的setState的简写方式(语法糖)
2.使用原则:
(1).如果新状态不依赖于原状态 ===> 使用对象方式
(2).如果新状态依赖于原状态 ===> 使用函数方式
(3).如果需要在setState()执行后获取最新的状态数据,
要在第二个callback函数中读取
//1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
const Login = lazy(()=>import('@/pages/Login'))
//2.通过指定在加载得到路由打包文件前显示一个自定义loading界面
<Suspense fallback={<h1>loading.....</h1>}>
<Switch>
<Route path="/xxx" component={Xxxx}/>
<Redirect to="/login"/>
</Switch>
</Suspense>
(1). Hook是React 16.8.0版本增加的新特性/新语法
(2). 可以让你在函数组件中使用 state 以及其他的 React 特性
(1). State Hook: React.useState()
(2). Effect Hook: React.useEffect()
(3). Ref Hook: React.useRef()
(1). State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
(2). 语法: const [xxx, setXxx] = React.useState(initValue)
(3). useState()说明:
参数: 第一次初始化指定的值在内部作缓存
返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
(4). setXxx()2种写法:
setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
(1). Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
(2). React中的副作用操作:
发ajax请求数据获取
设置订阅 / 启动定时器
手动更改真实DOM
(3). 语法和说明:
useEffect(() => {
// 在此可以执行任何带副作用操作
return () => { // 在组件卸载前执行
// 在此做一些收尾工作, 比如清除定时器/取消订阅等
}
}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
(4). 可以把 useEffect Hook 看做如下三个函数的组合
componentDidMount()
componentDidUpdate()
componentWillUnmount()
(1). Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
(2). 语法: const refContainer = useRef()
(3). 作用:保存标签对象,功能与React.createRef()一样
<>>
可以不用必须有一个真实的DOM根标签了
一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信
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插件
只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低
只当前组件重新render(), 就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低
只有当组件的state或props数据发生改变时才重新render()
Component中的shouldComponentUpdate()总是返回true
办法1:
重写shouldComponentUpdate()方法
比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false
办法2:
使用PureComponent
PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
注意:
只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false
不要直接修改state数据, 而是要产生新数据
项目中一般使用PureComponent来优化
Vue中:
使用slot技术, 也就是通过组件标签体传入结构
React中:
使用children props: 通过组件标签体传入结构
使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性
xxxx
{this.props.children}
问题: 如果B组件需要A组件内的数据, ==> 做不到
}>
A组件: {this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示 {this.props.data}
错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面
只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误
getDerivedStateFromError配合componentDidCatch
// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {
console.log(error);
// 在render之前触发
// 返回新的state
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// 统计页面的错误。发送请求发送到后台去
console.log(error, info);
}
1.props:
(1).children props
(2).render props
2.消息订阅-发布:
pubs-sub、event等等
3.集中式管理:
redux、dva等等
4.conText:
生产者-消费者模式
父子组件:props
兄弟组件:消息订阅-发布、集中式管理
祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)