npm run build
可以将public的代码打包到build文件夹下面
- 单页应用:整个项目只有唯一的一个 html 文件,所有页面都做成组件的样子,被添加到这个 html 文件中进行渲染
前言:每个开发者都该知道的React生存指南
想象一下:你刚接手一个新项目,发现代码库里全是类组件,维护起来像在解鲁班锁。这时候你可能会想:"要是能用函数组件+Hooks重构该多好!"——别担心,这本指南就是你的React生存手册,帮你从类组件的泥潭里爬出来,拥抱现代React的最佳实践。
组件化思维:把UI拆成乐高积木
组件
- class 组件
- function 组件
建议:
除非维护旧项目,否则优先用函数组件。就像用智能手机替代老式功能机——更轻便、更智能
hooks(钩子函数)
- 由 react 官方封装好的一系列函数,它们的用法和作用
-
useState 状态 定义一个响应式的变量,并提供专门的方法修改这个变量值
-
useEffec--副作用函数(可以放异步函数)(附加作用)
- 组件每次加载(挂载),都会触发
- useEffect(fn, []),第二个参数是一个空数组时,只会在初次渲染(挂载)时触发
- useEffect(fn, [state]),第二个参数是一个数组,数组中是一个 state 时,该变量每次修改值时,都会带来 useEfect 的重新触发
- useEffect 第一个参数是函数,该函数内部会返回出来一个新的函数,新函数会在当前整个组件不展示(卸载)时触发
import { useEffect, useState } from "react";
async function queryData() {
const data = await new Promise((resolve) => {setTimeout(() => resolve(100), 1000);});
return data;
}
function App() {
const [num, setNum] = useState(1);
// 当num只改变时,浏览器的useLayoutEffect中的effect函数执行完毕再渲染,effect会阻塞很久,超过500毫秒会导致掉帧
useEffect(() => {
console.log("useLayoutEffect");
queryData().then((data) => {
setNum(data);
});
}, [num]);
return (
<div>
<button
onClick={() => {
setNum(num + 1);
}}
>
{num}
</button>
</div>
);
}
export default App;
- useLayoutEffect 中的 effect(这里的回调函数)函数作为同步函数来执行,useEffect作为异步来执行
import { useLayoutEffect, useState } from "react";
function App() {
const [num, setNum] = useState(1);
useLayoutEffect(() => {
setNum(100);
}, [num]);
return (
<div>
<button
onClick={() => {
setNum(num + 1);
}}
>
{num}
</button>
</div>
);
}
export default App;
好处是可以防止闪动,坏处是这里useLayoutEffect的执行时间超过500ms(长任务)会阻塞后续代码的渲染,造成掉帧。
- useReducer--当修改 state 的逻辑比较复杂时,用 useReducer(相比较于useState的优点)
- 传入的 Reducer 函数中不能直接修改原 state,必须返回一个新的对象(缺点)
- useReducer +immer(第三方工具)
const [res,dispatch]=useReducer(reducer,{result:0}), useReducer接受一个函数体和一个初始值, 这里useReducer接受的第一个参数是一个函数,第二个参数是一个对象,调用dispatch传入一个对象作为useReducer第一个参数(函数体中),而参数(函数体)里面有两个参数,第一个参数是useReducer里面的第二个参数,函数的第二个参数是dispatch传入的参数 (对象)
import { useReducer } from "react";
import {produce} from 'immer'
function Reducer(state, action){
switch(action.type){
case "add":
// return {
// result:state.result+action.num
// }
return produce(state,(state)=>{
state.result+=action.num
})
case "minus":
return {
result:state.result-action.num
}
default:
return state
}
}
function App() {
//useReducer接收的第二个的参数作为Reducer的第一个参数,
//dispatch接收的参数作为Reducer的第二个参数
const [res,dispatch] = useReducer(Reducer,{result:0,a:{b:{c:1}}})
return (
<div>
<h3>{res.result}</h3>
<button onClick={() => dispatch({type:"add",num:2})}>+</button>
<button onClick={() => dispatch({type:"minus",num:2})}>-</button>
</div>
);
}
export default App;
produce()帮我们对原对象进行深拷贝
- useRef--获取 DOM 结构
在标签上添加ref={xxx}来获取
- useContext--跨多层组件进行数据传递(数据传递也可以通过标签添加属性逐级传递) 缺点只能父往子方向传递,不能子向父传递(可以建立仓库redux实现任意组件传递)
import { useContext,createContext } from "react"
function Child1(){
const count = useContext(AppContext)
return (
<div>
<h2>子组件{count}</h2>
<Child2/>
</div>
)
}
function Child2(){
const count = useContext(AppContext)
return(<h3>孙子组件{count}</h3>)
}
const AppContext = createContext()
function App(){
const num = '数据'
return (
<AppContext.Provider value={num}>
<h1>父组件</h1>
<Child1/>
</AppContext.Provider>
)
}
export default App
总结:
🔹 组件化思维
- 函数组件 vs 类组件的选择原则
- 组件拆分的"单一职责"原则(就像厨房里刀、锅、炉具各司其职)
🔹 Hooks 实战技巧
useState管理简单状态(计数器、模态框开关)useEffect处理数据请求(学会用清理函数避免内存泄漏)useLayoutEffect优化视觉更新(告别恼人的闪屏问题)
🔹 状态管理进阶
- 复杂逻辑用
useReducer(就像给状态机编写操作手册) - Immer 库让不可变数据操作变得简单(深拷贝从此不再头疼)
🔹 跨组件通信
useContext实现多层传参(就像在公司内部发通知)