一、概览
为什么要学react
- 原生JS操作DOM频繁,频率低(DOM-API操作UI)
- 使用JS直接操作DOM,浏览器会进行大量的重绘重排
- 原生JS没有组件化编码方案,复用率低
React优点
- 采用组件化模式、声明式编码,提高开发效率及组件复用率。
- 在React Native中可以使用React语法进行移动端开发。
- 使用虚拟DOM+优秀的Diffing算法,尽量减少与真实DOM的交互
文件构成
按需引入
写的是jsx,所以type='text/babel'
关于虚拟DOM
- 1.本质是Object类型的对象(一般对象)
- 2.虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。
- 3.虚拟DOM最终会被React转化成真实DOM,呈现在页面上
jsx语法规则
- 1.定义虚拟DOM时,不要写引号
- 标签中混入JS表达式要用{}
- 类名指定不用class,要用className
- 内联样式,要用style={{key:value}}
- 只有一个根标签
- 标签必须闭合
- 标签首字母
- (1)若小写字母开头,则将标签转为html中同名元素,若html中无该标签对应的同名元素则报错。
- (2)若大写字母开头,react就去渲染对应的组件,若组件无定义,则报错。
二、react面向组件编程
组件
函数式--简单组件
用不了state,refs
函数式组件使用props
类式组件--复杂组件
回归类的基础知识
类的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
类中可以直接写赋值语句,let a = 1报错,这也是初始化的一种方法,可以不用写在构造器中。
super--继承父类的构造器,子类如果写了构造器,必须写super(必须写第一行),不然报错。
类中所定义的方法,都是放在了类的原型对象上,供实例去使用。
定义类式组件
类式组件中的props
类中的构造器几乎不用写。构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props。(不接收不传递,可以在访问器中访问props,不能访问this.props)
绑定点击事件
之前的原始事件都 要写成驼峰命名法。绑定的回调函数不要写()
类里边定义方法,自动开启局部严格模式
组件实例三大核心
state
- 构造器调用一次,render调用n+1一次(n是状态更新的次数,1是初始化),changeWeather点几次调用几次。
- 使用bind修改this指向为实例
- 想要修改state的值,需要通过重要的API--setState进行更新
简化版--赋值语句+箭头函数解决this丢失问题
总结:state值是对象(可以包含多个key-value组合);组件被称为状态机,通过更新组件的state来更新对应的页面显示
setState的两种形式
setState更改状态的动作是异步的,如果想获取更改后状态的值,需要写在第二个参数--回调函数中。
props
基本使用--在标签内写key-value。年龄应该写age={19},这样就是number类型,不然是字符串类型
基础回顾
{...对象} 一层的对象,就是属于深拷贝,多层才是浅拷贝。{...对象,属性:值},复制并更改属性值/增加
引入PropTypes
15.5版本以后,直接使用PropTypes.XXX,之前使用react.PropTypes,defaultProps是默认。
函数类型要写func。
props是只读的
简写
总结:每个组件对象都有props属性,组件标签的所有属性都保存在props中。
作用:通过标签属性从组件外部向组件内传递变化的数据。
注意:组件内部不要修改props数据。
refs
字符串形式的refs
不被推荐
回调形式的refs
ref中自动传递参数:当前节点c,然后通过this.input1=c把它挂载到组件实例上
第一行是内联模式,第二行是绑定函数的方式
create Ref
适应几个ref就要创建几个ref(createRef)
事件处理
- 1.通过onXxx指定事件处理函数。
- a.react使用的是二次封装后的事件,不是原生DOM事件--为了更好的兼容性
- b.事件是通过事件委托方式处理的(委托给组件最外层的元素)--为了高效
- 2.通过event.target得到发生事件的DOM元素对象--不要过度使用ref
如果触发事件的元素和发生改变的元素是同一个,则可以使用event.target
收集表单数据
非受控组件
现用现取
受控组件
现在state中设置初始状态(Vue中的双向数据绑定)
优化
高阶函数
- 常见的高阶函数:Promise/setTimeout/arr.map()等等。
- 如果一个函数符合2个规范中的任何一个,那么该函数就是高阶函数
- 若A函数,接受的参数是一个函数
- 若A函数,调用的返回值依然是个函数
补充:对象[变量名]==>变量值:属性值;折叠不起来就写#region和#endregion
在标签体中绑定事件写了小括号,这个绑定事件的回调是其返回值。把return写成函数形式,可以接收到event,在return后的函数体中写事件逻辑。
函数的柯里化
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
高阶函数那个案例也是柯里化。
不使用柯里化的效果
生命周期
引出
卸载组件API
旧版
走setState那条路就是正常更新,shouldComponentUpdate是控制组件更新的阀门,返回布尔值,默认是true。forceUpdate()强制更新:直接就改,不管阀门shouldComponentUpdate()的false
- componentDidMount():一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息。
- componentWillUnmount():一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息。
新版
- will(除了WillUnmount)要加unsafe_前缀,因为未来要异步渲染,可能会出现矛盾。
- getDerivedStateFromProps适用于罕见的用例,即state值在任何时候都取决于props。
- getSnapshotBeforeUpdate
key
三、脚手架
安装
全局安装脚手架npm i create-react-app -g
创建xxx文件夹create-react-app xxx
安装yarnnpm install -g yarn
降级npm install react@17.x react-dom@17.x --save
18的creatRoot只是和Compont一样是react上的属性,creatRoot是React的属性,可以直接导入也可以像导入Component一样直接导入creatRoot
文件
四、tolist组件学到的点
- 组件拆分 class拆成className,style内联样式拆成style={{}}
- props传递参数时,直接用展开运算符接(两行是一个作用)
- 默认勾不勾选,只能执行一次(有很多问题)
- 子传父,在父组件中写回调函数拿到传递的数据
在子组件
- 鼠标移动改变样式
- 基础:把b的值改成3
- 勾选状态数据流HTML->state,孙给父组件传,孙->子->父。在父组件中写回调:
在子组件中写回调,不要写回调:
在孙组件:
- 📢注意:状态在哪里,操作状态的方法就在哪里
- 限制props传递参数的类型
- 下载库
- 引入
- 在子组件中
- 下载库
- reduce使用 filter过滤,reduce统计
五、Ajax与react
前置
- react本身只关注于页面,并不包含发送Ajax请求的代码
- 前端需要通过Ajax请求与后台交互(json数据)
- react应用中需要第三方Ajax库、或者自己封装
- 常见Ajax请求库:jQuery(比较重),axios(promise风格,可以在浏览器端和node服务器端使用,轻量级)
安装
- 安装axios
跨域
跨域的本质是Ajax引擎
解决:
- 在package.json中配置代理(只能配置一个)优先匹配前端资源,没有再发请求
- 推荐:在src下配置文件setupProxy.js可以配置多个代理,灵活控制请求是否走代理。
这里注意一下,高版本的 http-proxy-middleware const {createProxyMiddleware} = require('http-proxy-middleware')
const proxy = require('http-proxy-middleware')
const {createProxyMiddleware} = require('http-proxy-middleware')
module.exports = function (app) {
app.use(
createProxyMiddleware('/api1',{
target:'http://localhost:5000',
changeOrigin:true,
pathRewrite:{'^/api1':''}
}),
createProxyMiddleware('/api2',{
target:'http://localhost:5001',
changeOrigin:true,
pathRewrite:{'^/api2':''}
})
)
}
六、github组件学到的点
- 解构幅值的连续写法--获取this.keyWordElement.value
连续解构幅值并重命名
- 没有跨域问题,已经用cors解决了
- 子组件改变父组件的很多状态,父组件写一个函数,子组件通过解构幅值传递改变。
子组件:
父组件:
- 三元表达式连写
消息订阅与发布--兄弟组件间通信
下载及引入
谁需要谁订阅:
取消订阅:
发布('发布名',发布内容):
fetch
不用下载,原生函数
七、react路由
SPA:单页面多组件。
什么是路由?
路由就是一个key-value映射关系,key为路径,value可能是function或component
路由分类
- 后端路由:
- value是function,用来处理客户端提交的请求
- 注册路由:router.get(path,function(req,res))
- 工作过程:当node接收到一个请求时,根据请求路径找到匹配的路由,调用路由中的函数来处理请求,返回响应数据
- 前端路由:
- 浏览器路由,value是component,
- 注册路由
<Route path='/test' component={Test}> - 工作过程:当浏览器的path变为/test时,当前路由组件就会变成Test组件
- 前端路由实现的基石:BOM 身上的history
安装和引入
5版本npm i react-router-dom@5
使用
在index里包裹 <BrowserRouter>
在APP中
PS:也可以使用带#的HashRouter
路由组件和一般组件
路由组件的三个固定属性
点击加类名高亮
引入
默认加active,所以active时可以省略activeClassName
封装MyNavLink
标签体内容其实也通过props传递过去了
封装的MyNavLink
Switch
引入Switch
刷新样式丢失问题
1.使用绝对路径
2.把.给删掉(常用)
模糊匹配和严格匹配
默认开启模糊匹配
顺序也要一致
严格匹配exact
可直接写exact,也可以写exact={true}====>尽量不要开启
重定向Redirect
引入
使用:写在所有路由的最下方,所有路由匹配不上时,就去重定向的to
push模式和replace模式
push模式默认开启,即有痕模式。replace模式写replace,无痕模式
多级路由/嵌套路由/二级路由
要带着前面的父级路由
参数类型
ajax:
- query
- params
- body
- urlencode
- json
路由组件传参
传递params参数
导航区
路由子组件
传递search参数
qs
路由子组件import qs from 'qs'老版本import qs from 'querystring'
对象和urlencoded相互转换
使用
导航区
路由子组件
传递state参数
传递的参数地址栏中隐藏
导航区
路由子组件
总结
state清除浏览器缓存会丢掉参数
编程式路由导航
一般组件使用路由组件的API
引入
使用
BrowserRouter和HashRouter
路由的懒加载
八、UI组件库
🔗
下载及引入
引入(自己找一下路径,不同版本都不一样)
按需引入
跟着官网指示
九、redux
是什么?
什么时候使用?
原理图
三个核心概念
应用
下载4.0.5版本
精简版
提示createStore弃用的,改用import { legacy_createStore as createStore} from 'redux' 就没有提示了。
在组件中 引入
可以在组件写subscribe
也可以在index中写
总结
完整版
ES6补充
想要返回一个对象,不能直接写=>{...},这样会默认是函数,返回undefined,要用小括号包裹
应用
文件构成
防止写错单词
同步和异步action
返回object类型是同步,返回function类型的是异步。
中间件下载
在store中引入
store的dispatch方法会判断:传入值是函数还是对象,如果是函数,那就给这个函数传参数,参数是store的dispatch方法并且执行这个函数。在action文件中:异步action调用同步action:
组件中:
总结:
十、react-redux
原理图
下载及引入
下载
APP中,引入容器
UI组件
容器组件
connect和谁做关联就传什么UI,第一个参数传递状态,第二个参数传递方法
总结
优化
从代码角度优化
从API角度优化
mapDispatchToProps可以返回一个函数,也可以返回一个对象。对象值是action,react-redux自动帮你dispatch
index不用开启检测,connect自动检测。使用provider可以不用在APP中一个一个的传store。
APP
从文件角度优化
把UI组件和容器组件合成一个jsx,UI组件直接定义类,然后在容器组件中使用。
文件构成
总结
多组件共享状态
文件构成
store中汇总,合并后的状态是个对象!!!
count组件内用person的状态
注意:如果用preState.push方法,redux检测不到变化,因为是浅比较,引用地址值没变(比较的是两个对象的存储位置,也就是浅比较法,所以,当我们 reducer 直接返回旧的 state 对象时,Redux 认为没有任何改变,从而导致页面没有更新。)
多个组件时文件构成
都在store中引入reducer太多了,新建一个index文件汇总,KV一致省略
store
纯函数
回顾一下高阶函数
redux 开发者工具
下载
在store中
十一、打包
小服务器npm i serve -g
启动
以xxx文件为根目录启动一台服务器,serve xxx
十二、hooks
16.8版本之后,函数式组件新出的功能。
Sate Hook
调用1+n(状态更新的次数)次。
effect Hook
不写[]就是检测所有状态,相当于didmount,didupdate.[]写谁就检测谁,不写就是didmount
useRef Hook
专人专用
十三、Fragment
类似vue中的template.也可以直接写空标签<></>(不能写任何属性)。fragment可以写key属性。
十四、context
一种组件间通信的方式,常用于祖组件与后代组件间通信
使用
一般不用,一般都用它封装的插件
十五、组件优化
component
引用地址没有变,PureComponent是浅对比,检测不到,同理unshift之类操作原数组的也检测不到。
十六、render props
回顾:标签体内容传递到chilren里,直接写不显示
就是Vue的插槽技术
十七、Error Boundary
写在父组件中
十八、组件间通信总结
十九、React Router6
一些改变
navigate
自定义类名高亮不能直接写了
路由表
在src下创建一个文件夹routers/index.js
二级路由
end
传参
params参数
路由表中占位
传参
接收参数--新hook
也可以用useMatch(url)获得
search
不需要占位
传参
接收参数--新hook:注意获得的是数组类型,第一个参数.get解构幅值,第二个参数负责更新数据(用的很少)
也可以用useLocation()获得
state
不需要占位
传参
接收参数--useLocation(),解构幅值的连续写法
编程式导航
只能写state,params和search不能写
前进和后退