react
框架的选择
低代码、BI
前瞻性
bs 架构,网页 客户端去使用
react就是用来代替DOM的,dom操作,构建前端界面的
react-native 直接开发 ios,安卓,原生应用
虚拟dom,操作react,影响dom,中间人
现在的dom 和 当前的dom做比较看哪个发生了变化,做最小的修改
1、虚拟dom
2、兼容性
3、性能好,避免做一些多余的操作
声明式编程:结果为导向
命令式编程:过程为导向
基于组件开发,组件为最小单位 降低代码的耦合
支持服务器渲染,seo优化
快速,简单,易学
html的声明式模板写法,js的可编程能力
组件就是首字母大写的函数
react.js:核心库 react-dom.js:dom库
const div = React.createElement('div',{},'内容') //用来创建一个React元素html
const root = ReactDOM.createRoot()
root.render(div)
createElement() 用来创建一个React元素
参数1:html标签必须是小写
参数2:标签的属性,id,type,设置事件的时候要修改成驼峰命名法,()=>{} className
参数3:内容
createRoot 获取根元素,根元素就是React元素要插入的位置
render 用来将react元素渲染到根元素
根元素中所有的内容都会被删除,被react元素所替换
当重复调用render时,会将两次的渲染结果进行比较
它会确保只修改那些发生变化的元素,对DOM做最少的修改
script type="text/babel" js代码被babel代理 babel.min.js
命令式编程
声明式编程:结果导向的编程 jsx需要被翻译成js代码,是React.createElement()的语法糖
jsx注意事项:
1、jsx不是字符串,不用加引号
2、jsx中html标签应该小写,react组件应该大写开头
3、jsx中有且只有一个根标签
4、jsx标签必须正确结束(自结束标签必须写 /)
5、在jsx中可以使用{}嵌入表达式,有值的语句就是表达式,在语句中是可以操作jsx中的
6、如果表达式是空值,布尔值,undefined,将不会显示
7、在jsx中,属性可以直接在标签中设置
注意:class -> className
style = {{'background':'red'}}
在react中我们操作的元素被称为react元素,并不是真正的原生DOM元素
每当我们调用root.render时,页面就会发生重新渲染
diff算法比较新元素和旧元素,并只对发生变化的元素进行修改
注意:
开发中一般会采用数据的id作为key
尽量不要使用元素的index作为key,当元素的顺序不会发生变化时,用索引做key也没有什么问题
react-scripts:webpack,项目打包,测试服务器,
约定优于配置
public/index.html是首页的模板,webpack在编译文件时,会以index.html为模板生成index.html
import ReactDOM from 'react-dom/client'
npx react-scripts build
npx react-scripts start
函数式组件:
函数组件就是一个返回jsx的普通函数
组件的首字母必须是大写
类组件:
类组件必须要继承react.component
必须添加一个render方法
jsx的return写在一行是可以省略括号的,如果多行还是需要加上括号
事件:通过属性需要使用驼峰命名法,属性值需要用回调函数
在react中,无法通过return false 取消默认事件,可以通过
event.preventDefault(取消默认行为)
event.stopPropagation(取消事件的冒泡)
props 传递属性 是只读的,不能修改 上级向下级修改
父组件-》子组件 props
props.children:表示组件的标签体
tolocaleString('zh-CN',{month:'long'}) //2021/11/6 返回的是中文的十一月
state 组件状态
在react中,当组件渲染完毕之后再修改组件中的变量,不会使组件重新渲染
使用一个特殊变量state,当变量修改时,组件会自动重新渲染
state相当于一个变量,这个变量在react中进行了注册
react会监控这个变量的变化,当state发生变化时,会自动触发组件的重新渲染
当state的值是一个对象时,修改时是使用新的对象去替换已有对象,直接修改旧的state对象,由于对象还是那个对象,所以不会生效
在函数组件中,需要使用钩子函数useState 创建state
useState函数:返回一个数组,
数组中的第一个元素是初始值,直接修改不会触发组件的重新渲染
数组中的第二个元素是一个函数通常命名为setXxx,调用其修改state后会触发组件的重新渲染,并使用函数中的值作为新的state值
setState() 修改的是组件下一次渲染时的state值,它是异步的,合并执行
当调用setState()需要用旧的state值时,一定要注意可能出现计算错误的情况,可通过setState()传递回调函数的形式来修改state的值
setState之后发生了什么?
react会将传入的参数对象和组件当前的状态合并,触发调和过程(reconciliation),经过调和,会根据状态构建react元素树,重新渲染整个UI界面,自动计算新树和老树的差异,进行最小化重新渲染
const [count,setCount] = useState(1)
react中获取原生的DOM对象 useRef():创建一个容器,将容器设置为想要获取DOM对象元素的ref属性
钩子函数的注意事项:
react的钩子函数只能用于函数组件或自定义钩子
钩子函数只能直接在函数组件中调用
useRef() 返回的是一个普通的js对象 {current:null}
我们创建的对象,组件每次重新渲染都会创建一个新对象
useRef()创建的对象,可以确保每次渲染获取到的都是同一个对象
当你需要一个对象不会因为组件的重新渲染而改变时,就可以使用useRef()
const h1Ref = useRef()
类组件
this.setState() 修改state时,只会修改设置的属性
获取DOM对象:
React.createRef() 创建属性存储DOM的对象
类组件的方法用箭头函数
portal:传送门
默认会作为父组件的后代渲染到页面中
可以将组件渲染到页面中的指定位置
使用方法:
1、index.html添加一个元素
2、修改组件的渲染方式 ReactDOM.createPortal() 创建元素
参数 1、jsx (修改前return后的代码)
2、目标位置(DOM元素)
模块化css
1、XXX.module.css
2、组件中引入css import classes from './App.module.css'
3、通过classes来设置类
className = {classes.p1}
css模块可以动态的设置唯一的class值
设置移动端的适配
除以几视口的宽度就是多少rem,我们设置视口的总宽度是750rem
引入fortawesome
1、引入组件
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
2、引入图标
import {faPlus} from "@fortawesome/free-solid-svg-icons";
3、使用组件
context:存储数据,相当于一个公共的存储空间
可以将多个组件中都需要访问的数据统一存储到一个context中。无需通过props逐层传递,即可访问这些数据
React.createContext()
使用方法一:
1、引入context
2、使用xxx.consumer组件来创建元素
consumer 标签体需要一个回调函数,通过参数可访问context中存储的数据
使用方法二:
useContext()
1、导入context
2、使用钩子函数useContext()获取到context
useContext()需要一个context作为参数
*Xxx.Provider,表示生产者,可以使用它来指定context中的数据,通过value来指定context中存储的数据
当通过context访问数据时,会读取离他最近的Provider中的数据,如果没有provider,则读取context中的默认数据
副作用:Effect
React.strictMode:自动检查react组件是否会产生副作用
React Developer Tool
当我们直接在函数体中调用setState时,就会触发上述错误
setState()的执行流程
setCount() --> dispatchSetDate()
--> 会先判断,组件当前处于什么阶段
如果处于渲染阶段 -> 不会检查state值是否相同,会一直处于重复渲染
如果不是处于渲染阶段 -> 会检查state值是否相同
--> 如果不相同,会进行重新渲染
如果相同,不重新渲染
--> 如果值相同,react在一些情况下会继续执行当前组件的渲染,但是这个渲染不会触发其子组件的渲染,这次渲染不会产生实际的效果,这种情况通常发生在值第一次相同时
useEffect() 是一个钩子函数,需要一个函数作为参数
这个作为参数的函数,将会在组件渲染完毕后执行
可以将那些会产生副作用的代码编写到useEffect的回调函数中
可以传递第二个参数,是一个数组,在数组中可以指定effect的依赖项
通常会将effect中使用的所有局部变量设置为依赖项,依赖项发生变化时,会触发effect的执行
useEffect(()=>{},[ctx])
如果依赖项设置了一个空数组,则意味着effect只会在组件初始化时触发一次
第一次点击按钮 count -> 1 执行
第二次点击按钮 count -> 1 执行
第三次点击按钮 count -> 1 不执行
effect的回调函数,可以指定一个函数作为返回值
这个函数可以将其作为清理函数,它会在下次effect执行调用
清除副作用:useEffect(()=>{
return ()=>{
//清除副作用
}
},[])
reducer: 整合器,对state的一个整合
useReducer:钩子函数 useReducer(reducer,initialArg,init)
reducer执行时,会收到两个参数
1、state:当前最新的state
2、action:需要一个对象,会存储dispatch所发出的指令
参数:
reducer:函数
initialArg:初始值,作用和useState里的值一样
返回值:
数组:
第一个参数:state 用来获取state的值
第二个参数:state修改的派发器,通过派发器可以发送操作state的命令,具体的修改行为将会由另外一个函数执行
const [count,countDispatch] = useReducer(()=>{},1)
countDispatch({type:'INC'})
为避免reducer会重复创建,会把reducer放在组件外部
React.memo 对于没有state等的组件,避免组件重复渲染 Object.is()方法判断两个值是否是相同的值。比较新值和老值
是一个高阶组件,接收另一个组件作为参数,并且会返回一个包装过的新组件
包装过的组件就会具有缓存功能
包装过后,只有组件的props发生变化才会触发组件的重新渲染,否则总是返回缓存中的结果
useCallback:钩子函数,用来创建react中的回调函数
useCallback 创建的回调函数不会总在组件重新渲染时重新创建,多次渲染中缓存函数
参数1:回调函数
参数2:依赖数组
当依赖数组中的变量发生变化时,回调函数才会重新渲染
strapi: api 本地服务
fetch: fetch Api 向服务器发送请求加载数据,是ajax的升级版,参数1、请求地址,2、请求信息
return res.json() 将响应的json直接转换为js对象
多层传递数据 context
钩子函数只能在函数组件或者自定义钩子函数中
自定义钩子:当我们需要将react中钩子函数提取到一个公共区域时,就可以使用钩子函数
自定义钩子是一个普通函数,它的名字需要使用use开头 useFun
redux:可预期的状态管理器,reducer和context的结合
redux.js 核心包
网页中使用步骤:
1、引入核心包
2、创建reducer整合函数
3、通过reducer对象创建store Redux.createStore(reducer,initArg)
4、将store中的state进行订阅 store.subscribe
5、通过dispatch派发state的操作指令
用redux带来的问题:
1、如果state过于复杂,难于维护
--state分组,创建多个reducer,然后合并成一个
2、state每次操作,都需要对state进行复制,然后再去修改
3、case后边的常量维护起来很麻烦
从而引入了RTK
RTK:Redux Toolkit
yarn add react-redux @reduxjs/toolkit
createSlice 创建reducer的切片
const stuSlice = createSlice({
name:'',用来自动生成action中的type
initialState:{
},//state的初始值
reducers:{
setName(state,action){
//可以通过不同的方法来指定对state的不同操作
//两个参数,state 是一个代理对象
}
}//指定state的各种操作,直接在对象中添加方法
})
切片对象会自动帮助我们生成action,actions中存储的是slice自动生成的action创建器,调用函数后会自动创建action对象
action对象的结构 {type:name/函数名,payload:函数的参数}
const {setName} = stuSlice.actions
创建store对象,需要一个配置对象作为参数
const store = configureStore({
reducer:{
student:stuSlice.reducer
}
})
useSelector(state=>state.student) 用来加载state中的数据
useDispatch() 来获取派发器对象
RTKQ: 创建api对象,减少请求发送的次数 keepUnusedDataFor,单位秒,默认是60s
createApi() 用来创建RTKQ中的API对象
const studentApi = createApi({
reducerPath:'', api的标识,不能和其他的api或reducer重复
baseQuery:fetchBaseQuery({
baseUrl:''
}),//指定查询的基础信息,发送请求使用的工具
endpoints(build){
return {
getStudents:build.query({
query(){
return
}
})
}
}//用来指定api中的各种功能,是一个方法,需要一个对象作为返回值
})
useQuery 参数:
可提供一个对象作为第二个参数,通过该对象可以对请求进行配置
{
selectFromResult: 用来指定返回的结果
skip: true 是否跳过当前请求
}
reactRouter:客户端路由 version 5
使用步骤:
1、引入react-router-dom包
2、在index.js中引入BrowserRouter组件
3、将BrowserRouter设置为根组件
使用Route来映射地址和组件
注意:当Route的路径被访问,其对应的组件就会自动挂载,默认情况不是严格匹配,只要url地址的头部和path一致,组件就会挂载
exact 是严格匹配
BrowserRouter 直接通过url地址进行组件的跳转
HashRouter 会通过url地址中的hash值来对地址进行匹配
当我们通过点击Link构建的链接进行跳转时,跳转并没有经过服务器,所以没有问题
* 但是当我们刷新页面,或通过普通链接进行跳转时,会向服务器发送请加载数据
* 这时的请求并没有经过react router 所以会返回404
* 解决方案:
* 1.使用HashRouter,服务器不会去判断hash值,
* 所以使用HashRouter后请求将会由React Router处理
* 2.修改服务器的配置,将所有请求都转发到index.html
* location / {
root html;
#index index.html index.htm;
try_files $uri /index.html;
}
//render是指定要挂载的组件,需要一个回调函数作为参数,返回值最终被挂载
props:
标签体:
当使用react-router时,一定不能使用a标签来创建链接
link:
navLink
Prompt组件:弹窗提示
Redirect组件:用于跳转页面
Outlet组件:表示嵌套路由中的组件,当嵌套路由中的路径匹配成功了,Outlet则表示嵌套路由中的组件
当嵌套路由中的路径没有匹配成功,Outlet就什么都不是
Navigate:用来跳转到指定的位置,默认使用push
router version6
Routes 是v6中新增加的组件
在v6中,Route的component render children都变了
useParams: 可以使用useParams()来获取参数
useLocation: 获取当前的地址信息
useMatch: 用来检查当前url是否匹配某个路由 如果路径匹配,则返回一个对象,不匹配则返回null
useNavigate: 获取一个用于条件页面的函数,可以实现跳转的功能,默认是push,会产生历史记录 useNavigate('/about',{replace:true})则不会产生历史记录
useMemo:每次重新渲染的时候能够缓存计算的结果,在组件顶层或者自定义hook中调用,数据二次处理(消耗非常大的计算的时候用此钩子函数)
const someEle = useMemo(()=>{
return
},[])
useImperativeHandle: 能自定义由ref暴露出来的句柄,场景:1、向父组件暴露子组件的方法 2、控制子组件的动画或转换效果 3、集成第三方库的方法 4、封装原生 DOM 元素的方法
无法直接获取react组件的dom对象,react也不知道要给你谁
React.forwardRef() ref={ref} 外层组件可以通过 ref 直接控制内层组件或元素的行为
useImperativeHandle(ref,()=>{
retun changeInpVal(val){}
})
useEffect:绘制屏幕之后,组件渲染之后执行,其余情况用useeffext
useLayoutEffect:dom改变,组件渲染之前执行,修改样式可以用
useInsertionEffect:dom改变之前执行,添加元素可以用
useDebugValue: React 开发工具 中为自定义 Hook 添加标签
useDeferredValue:延迟更新 UI 的某些部分,当设置了延迟值后,每次state修改时都会触发了两次重新的渲染,这两次执行对于其他部分没有区别,但是延迟值两次执行的值是不同的,第一次执行时 延迟值是state的旧值,第二次执行时,延迟值是state的新值,延迟值总会比原版的state值慢一步更新
当多个组件使用同一个state时,组件可能会互相影响,一个组件卡顿,会导致另一个组件卡顿
useTransition:是一个帮助你在不阻塞 UI 的情况下更新状态的 React Hook。
startTransition 的回调函数中设置setState会在其他的setState生效后执行
const [isPending,startTransitions] = useTransition
useId:可以生成传递给无障碍属性的唯一 ID
lodash:数据处理排序库
classnames: 优化类名控制
父 -> 子通信:props
子 -> 父:在子组件中调用父组件的函数并传递参数
兄弟组件通信:子A -> 父 父 -> 子B
跨级组件:context createContext provider useContext
json-server 数据服务
redux devtools
normalize.css 初始化css
路径解析配置 插件craco 别名@
source-map-explorer 包体积分析 source-map-explorer "build/static/js/*.js"
主题定制:全局定制和局部定制
:root:root{
--adm-color-primary:#dddd
}
.purple-theme{
--adm-colot-primary:#ddd
}
目录结构:
apis 接口
assets 静态资源
components 通用组件
pages 页面级组件
router 路由
store redux状态
hooks 自定义的钩子函数
utils 工具函数 axios的封装处理
useSearchParamms()
setFieldsValue() -> 简单json
打包:
1、项目打包:npm run build
2、本地预览:在本地通过静态服务器模拟生产服务器运行项目的过程
npm run build
npm install -g serve
serve -s build
路由懒加载: 目的是为了优化项目首次打开的时间
1、把路由修改为React提供的lazy函数进行动态导入
2、使用React内置的suspense组件包裹路由中element选项对应的组件
zustand:极简的状态管理工具 set
切片模式:create((...a)=>{}) 功能比较大的时候需要做拆分
React + Ts vite npm init vite@latest my-vue-app -- --template react-ts
1、语法: useState
2、children:
type Props = {
className:string,
children:React.ReactNode //支持多种类型,是react的内置类型
}
3、事件prop
type Props = {
onGetMsg?:(msg:string)=>void
}
4、useRef
dangerouslySetInnerHTML = {{
__html:detail?.content
}}
next.js:服务端渲染 ssr
render(reconcile的过程) + commit(执行增删改dom和effect、生命周期函数的执行,ref的更新等)
render 阶段实现 vdom 转 fiber 的 reconcile
commit 阶段执行增删改 dom,更新 ref、调用 effect 回调和生命周期函数等。