JSX
与 Element
对象
过程:JSX
经过Babel
转译为createElement
,createElement
执行得到ReactElement
对象
- 一个
ReactElement
对象
const ReactElement={
$$typeof:REACT_ELEMENT_TYPES, // Element元素类型
type:type, // 是标签还是组件
key:key, // 有没有key
ref:ref, // 有没有ref
props:props // 组件或标签有哪些属性,哪些后代元素/子组件
.....
}
组件
- 类组件
export default class App extends React.Component{
rnder(){
return<div>类组件</div>
}
}
- 函数组件
export default function App(){
return(
<div>函数组件</div>
)
}
- 静态属性
function Form(){}
Form.Item=function(){}
<Form>
<Form.Item></Form.Item>
</Form>
组件状态 State
setSate
处于同步逻辑中 异步更新状态
const [username,setUserName]=useState('aaaa')
function handleClick(){
// setUserName 处于同步逻辑中,它是异步更新状态
setUserName('bbb')
console.log(username) // aaaa
}
-
setState
处于异步的逻辑中 同步更新状态 -
如果状态变更如何拿到最新状态
- 在类组件中
this.setState
可以接收一个回调函数,监听到状态
state={username:'husa'}
handleClick=()=>{
// 在回调中可以拿到最新状态
this.setState({username:'bus'},()=>{console.log('更新后的状态',this.state.username)})
}
- 在函数组件中
useState
没有回调函数,它没有办法感知到状态改变
需要借助useEffect
监听状态
const [username,setUserName]=useState('aaaa')
function handleClick(){
// setUserName 处于同步逻辑中,它是异步更新状态
setUserName('bbb')
console.log(username) // aaaa
}
useEffect(()=>{
console.log('获取新的状态',username) // bbb
},username)
this.setState({count:this.state.count+1},()=>console.log('更新完成'))
注意事项:setState
可以自动合并更新对象(传部分数据,内部自动合并),useState
不会合并更新对象,在使用useState
你需要传新的完整的数据。
state={userName:'sdd',phone:231}
setState({phone:323}) //ok
useState({phone:322}) //no
useState({...state,phone:323}) //ok
多次执行相同的setState
会多次执行更新,多次的相同的useState
只更新一次
onRefrese=()=>{
this.setState({num:this.state.num}) // 状态并没有发生改变但仍然会重新渲染
}
组件的通信方式
props
和callback
父子间通信ref
Redux
,Mobx
状态管理context
event bus
发布订阅模式
class eventBus {
event = {}
// 绑定事件
on(eventName, cb) {
if (this.event[eventName]) {
this.event[eventName].push(cb)
} else {
this.event[eventName] = cb
}
}
// 触发事件
emit(eventName, ...params) {
if (this.event[eventName]) {
this.event[eventName].forEach((cb) => {
cb(...params)
})
}
}
// 取消事件
off(eventName) {
if (this.event[eventName]) {
delete this.event[eventName]
}
}
}
export default eventBus
HOC 高阶组件
输入一个组件,输出一个新组件,可以解决代码复用和逻辑复用问题
- 高阶组件代理属性
包装一个组件给这个组件注入新的属性,返回一个新的组件,与cloneElement
很作用类似
给原始组件包裹一层高阶组件,以达到组件功能增强的效果
- 使用
cloneElement
function HOC({children}){
const newPerson=React.cloneElement(children,{email:'yourEmail'})
return <div>{newPerson}</div>
}
function Person(props){
console.log(props)
return <div>Person</div>
}
export default class App extends Component {
render() {
return (
<div style={styles}>
<HOC>
{/* Person 只有一个name属性,要用cloneElement给它注入新属性*/}
{/* 在Person外面包裹一层HOC */}
<Person name='yourName'></Person>
</HOC>
</div>
)
}
}
- 不使用
cloneElement
function HigherOrderComponent(TargetComponent){
return class NewComponent extends Component{
state={
prop:'一个新属性'
}
render(){
return <div><TargetComponent {...this.props} newProp={this.state.prop}/></div>
}
}
}
function Person(){
return <div>Person</div>
}
// 使用高阶函数更新组件
const NewPerson=HigherOrderComponent(Person)
export default class App extends Component {
render() {
return (
<div style={styles}>
{/* 使用高阶函数去渲染一个新的组件 */}
<NewPerson name='yourName'></NewPerson>
</div>
)
}
}
hooks
useEffect()
useEffect(()=>{},[])
useEffect开始的时候会执行一次,后面每次依赖发生改变都会再次执行
useEffect
在页面渲染完成后执行
useLayoutEffect
在dom
树构建完成之后,页面渲染之前执行。也就是说useLayoutEffect
可嫩会阻塞页面渲染。如果有dom
操作应该放在useLayoutEffect
中,可以减少重排和重绘次数。
useState()
缓存状态
useState
可以缓存状态,及state更新,组件渲染,状态并不会重置。
useCallback
记忆函数
每次更新都会导致组件重新渲染,组件内部的一些局部变量都可能会被重新创建。
使用useCallback
可以缓存函数,防止组件重新渲染,引发函数重新创建,提高性能
useCallback(()=>{},[])
只有依赖发生改变函数才会再次被声明,如果依赖项为空,表示一直使用旧值
const [number,setNumber]=useState(0)
let num=0
const callback=()=>{
setUser(number+1) // 每次更新,callback就会重新创建,永远都只能打印0
console.log(num++)
}
const usecallback=useCallback(()=>{
setUser(number+1) // 使用useCallback,组件渲染不会重新声明函数,num正常增加
console.log(num++)
},[num])
useMemo
useCallback(fn,[])
=>useMemo(()=>fn,[])
useCallback不会执行函数,useMemo会执行函数。
useRef
useRef
不仅可以获取dom还可以缓存变量
let num=useRef(0)
const handleClick=()=>{
num.current++ // 每次执行num.current都会增加,组件更新num不会被重置
}
let number=0
const handelClick1=()=>{
number++ // 每次执行number都会增加,但状态改变,组件更新number就会被重置
}
react-router
react-router
将路径与页面进行关联
Routes
Routes
指定了路由表中的组件应该在哪个位置显示
当我们点击link
它会改变路由,Routes
映射表就会查找路由对应的组件,并将它放到Routes
所在的位置
Route
Route
表示路由和组件的映射关系
<Route path="/" element={<Home/>}/>
<Route path="/about" element={<About/>}/>
<Route path="*" element={<NotFound/>}/>
其它会被映射到NotFound
export default function App() {
return (
<div className="app">
<Header></Header>
<div>
<Routes>
<Route path="" element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
</Routes>
</div>
</div>
)
}
function Header() {
return (
<>
<Link to={"/"}>home</Link>
<Link to={"/about"}>about</Link>
</>
)
}
改变路由的方法
Link
to targetNavLink
to targetNavigate
to targetuseNavigate
const navigate = useNavigate()
navigate('跳转的路径',{'你要传递的信息'})
前面是3个是声明式的方式,都有to
属性改变路由,因为都是组件,所以可以向后传递属性(状态)
最后一个是hooks
命令式的方式,navigate
接收的参数就是你要去的地方
接收路由信息
我们知到像Link
,NavLink
,Navigate
这些组件,路由跳转时可以携带状态(能让我们知到到底是从哪里跳转过来的),
那么怎么接收这些状态
useLoaction
可以获取这些状态
About
组件有一个Link
链接,当我们点击这个链接,就会跳转到Home
组件,Home
使用location
就能拿到对应的信息,这样Home
就知到是谁跳过来的了。
function Header() {
return (
<>
<Link to={"/"} state={"我是header里面的跳转"}>
home
</Link>
<Link to={"/about"}>about</Link>
</>
)
}
function Home() {
const location = useLocation()
return (
<>
<p>我是主页</p>
<p>{location.state}</p>
</>
)
}
动态路由(any路由)
<Route path="/images/:any" element={<image />} />
在images后面跟任何东西都会匹配到image
组件
可以使用useParams
拿到URL
中动态变化的部分
const params=useParams()
使用params.any
拿到url的参数,然后向后端发请求
嵌套路由
- 第一种写法
- 第二种写法
跨域
npm install http-proxy-middleware
- 配置
setupProxy.js
const { createProxyMiddleware } = require("http-proxy-middleware")
module.exports = function (app) {
app.use(
"api",
createProxyMiddleware({
target: "https://xxx.com/",
changeOrigin: true
})
)
}
- 请求数据
fetch(api/xxxxx)
.then((response) => response.json())
.then((data) => console.log(data))
注意:虽然服务器之间没有跨域限制,但别人的服务器都有防爬措施。
Redux
Flux
redux 工作流
状态由reducer
修改,reducer
决定每个action
应该怎么去修改store
(state, action) => newState
视图提供action
,reducer
会根据action
计算新的状态。
reducer
相当于是状态和行为的映射表
store.dispatch(action)
方法用来更新 store
里的 state
store.getState
方法用来获取 store
里的 state
- 先编写
reducer
- 创建
store
const store=configureStore(reducer)
- 更新状态
- 获取状态
react 项目
项目配置
- 配置目录别名
在vite.config.ts中
import path from 'path'
resolve:{
alias:{
'@':path.resolve(__dirname,'./src')
}
}
在tsconfig.json中
"baseUrl": "./",
"paths": {
"@/*": [
"src/*"
]
},