react 开发者工具调试 chrome网上应用店
React Developer Tools
jsx 语法规则
react 引入的东西分别是

react 中定义组件
函数式(简单)组件

类式(复杂)组件

组件实例的三大核心属性 (类式组件才有实例,所以函数式组件没有这个)
三大核心属性state

基本使用

如何绑定方法


state的更改 => setState()
标准写法(state如何使用)


简写方式 (开发中使用的写法)

三大核心属性props(是只读的)
总结

基本使用

简写使用(注意:在ES6展开运算符不能直接展开对象, {...xxx} 复制一个对象却可以,在react中 这里 是 ...xxx 对象的意思,但这不是{...xxx},而是react中定义的)

-
类型限制
- React.PropTypes.string(16版本之前)


props 的简写方式 (开发中的写法)

注意点
官网描述
函数式的 props(没有this,所以state、refs都没有,但在hooks中可使用这2个)

- 限制

三大核心属性refs
基本使用
-
string类型的ref(react已经不推荐使用这种类型了,有可能以后被删掉)
- 效率不高

- 效率不高
-
函数回调形式的ref
- ref里的箭头函数是被执行了,参数是其本身的dom节点
- 内联函数的方式定义的话会在更新过程中被调用2次,可以class的绑定函数的方式避免(其实无关紧要的)
- 回调函数(1.自己定义2.自己没调用3.被别人调了)


- createRef API 形式的refs (react 最推荐使用的形式, 开发中使用)
React.createRef() 调用后可以返回一个容器,该容器可以存储被ref所标识的节点(vue3.0类似)该容器是“专人专用”的.其中current不能更改也不能省略

事件处理

非受控组件
现用现取

受控组件(推荐写这种)
随着输入维护状态

高阶函数、函数的柯里化


高阶函数、函数的柯里化在react中的应用

不用高阶函数、不用函数的柯里化(扩展知识)

组件生命周期
新的生命周期


旧的生命周期



新旧对比


dom的Diffing算法
react 脚手架

react 脚手架的文件目录


react脚手架配置代理总结
方法一
在package.json中追加如下配置
"proxy":"http://localhost:5000"
说明:
- 优点:配置简单,前端请求资源时可以不加任何前缀。
- 缺点:不能配置多个代理。
- 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)
方法二
- 第一步:创建代理配置文件
在src下创建配置文件:src/setupProxy.js
- 编写setupProxy.js配置具体代理规则:
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': ''}
})
)
}
说明:
- 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
- 缺点:配置繁琐,前端请求资源时必须加前缀。
todoList案例相关知识点
- 拆分组件、实现静态组件,注意:
className、style的写法 - 动态初始化列表,如何确定将数据放在哪个组件的
state中?- 某个组件使用:放在其自身的
state中 - 某些组件使用:放在他们共同的父组件
state中(官方称此操作为:状态提升)
- 某个组件使用:放在其自身的
- 关于父子之间通信:
- 【父组件】给【子组件】传递数据:通过
props传递 - 【子组件】给【父组件】传递数据:通过
props传递,要求父提前给子传递一个函数
- 【父组件】给【子组件】传递数据:通过
- 注意
defaultChecked和checked的区别,类似的还有:defaultValue和value - 状态在哪里,操作状态的方法就在哪里
github搜索案例相关知识点
- 设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办。
- ES6小知识点:解构赋值+重命名
let obj = {a:{b:1}}
const {a} = obj; //传统解构赋值
const {a:{b}} = obj; //连续解构赋值
const {a:{b:value}} = obj; //连续解构赋值+重命名
- 消息订阅与发布机制
- 先订阅,再发布(理解:有一种隔空对话的感觉)
- 适用于任意组件间通信
- 要在组件的
componentWillUnmount中取消订阅
- fetch发送请求(关注分离的设计思想)
try {
const response= await fetch(`/api1/search/users2?q=${keyWord}`)
const data = await response.json()
console.log(data);
} catch (error) {
console.log('请求出错',error);
}
react router
官方网站
安装依赖方式
- react-router-dom
yarn add react-router-dom
路由的基本使用
- 明确好界面中的导航区、展示区
- 导航区的a标签改为Link标签
<Link to="/xxxxx">Demo</Link> - 展示区写Route标签进行路径的匹配
<Route path='/xxxx' component={Demo}/> <App>的最外侧包裹了一个<BrowserRouter>或<HashRouter>
路由组件与一般组件
- 写法不同:
- 一般组件:
<Demo/> - 路由组件:
<Route path="/demo" component={Demo}/>
- 一般组件:
- 存放位置不同:
- 一般组件:
components - 路由组件:
pages
- 一般组件:
- 接收到的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"
NavLink与封装NavLink
NavLink可以实现路由链接的高亮,通过activeClassName指定样式名
Switch的使用
- 通常情况下,
path和component是一一对应的关系。 Switch可以提高路由匹配效率(单一匹配)。
解决多级路径刷新页面样式丢失的问题
public/index.html中 引入样式时不写./写/(常用)public/index.html中 引入样式时不写./写%PUBLIC_URL%(常用)- 使用
HashRouter
路由的严格匹配与模糊匹配
- 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
- 开启严格匹配:
<Route exact={true} path="/about" component={About}/> - 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
Redirect的使用
- 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由
- 具体编码:
<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Redirect to="/about"/>
</Switch>
嵌套路由
- 注册子路由时要写上父路由的path值
- 路由的匹配是按照注册路由的顺序进行的
向路由组件传递参数
- params参数
- 路由链接(携带参数):
<Link to='/demo/test/tom/18'}>详情</Link> - 注册路由(声明接收):
<Route path="/demo/test/:name/:age" component={Test}/> - 接收参数:
this.props.match.params
- 路由链接(携带参数):
- search参数
- 路由链接(携带参数):
<Link to='/demo/test?name=tom&age=18'}>详情</Link> - 注册路由(无需声明,正常注册即可):
<Route path="/demo/test" component={Test}/> - 接收参数:
this.props.location.search
备注:获取到的
search是urlencoded编码字符串,需要借助querystring解析 - 路由链接(携带参数):
- state参数
- 路由链接(携带参数):
<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link> - 注册路由(无需声明,正常注册即可):
<Route path="/demo/test" component={Test}/> - 接收参数:
this.props.location.state
备注:刷新也可以保留住参数
- 路由链接(携带参数):
withRouter
编程式路由导航
- 借助
this.prosp.history对象上的API对操作路由跳转、前进、后退this.prosp.history.push()this.prosp.history.replace()this.prosp.history.goBack()this.prosp.history.goForward()this.prosp.history.go()
BrowserRouter与HashRouter的区别
- 底层原理不一样:
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。HashRouter使用的是URL的哈希值。
- path表现形式不一样
BrowserRouter的路径中没有#,例如:localhost:3000/demo/test- HashRouter的路径包含#,例如:
localhost:3000/#/demo/test
- 刷新后对路由state参数的影响
- BrowserRouter没有任何影响,因为state保存在history对象中。
- HashRouter刷新后会导致路由state参数的丢失!!!
备注:HashRouter可以用于解决一些路径错误相关的问题。
react redux
求和案例_redux精简版
-
去除Count组件自身的状态
-
src下建立:
redux -store.js -count_reducer.js -
store.js:
- 引入redux中的createStore函数,创建一个store
- createStore调用时要传入一个为其服务的reducer
- 记得暴露store对象
-
count_reducer.js
- reducer的本质是一个函数,接收:
preState,action,返回加工后的状态 - reducer有两个作用:初始化状态,加工状态
- reducer被第一次调用时,是store自动触发的,
- 传递的
preState是undefined, - 传递的
action是:{type:'@@REDUX/INIT_a.2.b.4}
- 传递的
- reducer的本质是一个函数,接收:
-
在index.js中监测store中状态的改变,一旦发生改变重新渲染
<App/>
备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。
2.求和案例_redux完整版
- 新增文件:
- count_action.js 专门用于创建action对象
- constant.js 放置容易写错的type值
3.求和案例_redux异步action版
- 明确:延迟的动作不想交给组件自身,想交给
action - 何时需要异步
action:想要对状态进行操作,但是具体的数据靠异步任务返回。 - 具体编码:
yarn add redux-thunk,并配置在store中- 创建
action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。 - 异步任务有结果后,分发一个同步的
action去真正操作数据。
备注:异步
action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。
4.求和案例_react-redux基本使用
- 明确两个概念:
- UI组件:不能使用任何redux的api,只负责页面的呈现、交互等。
- 容器组件:负责和redux通信,将结果交给UI组件。
- 如何创建一个容器组件>靠react-redux 的 connect函数
- connect(mapStateToProps,mapDispatchToProps)(UI组件)
- mapStateToProps:映射状态,返回值是一个对象
- mapDispatchToProps:映射操作状态的方法,返回值是一个对象
- connect(mapStateToProps,mapDispatchToProps)(UI组件)
- 备注:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
- 备注:mapDispatchToProps,也可以是一个对象
求和案例_react-redux优化
- 容器组件和UI组件整合一个文件
- 无需自己给容器组件传递store,给
<App/>包裹一个<Provider store={store}>即可。 - 使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。
mapDispatchToProps也可以简单的写成一个对象- 一个组件要和redux“打交道”要经过哪几步?
-
定义好UI组件---不暴露
-
引入connect生成一个容器组件,并暴露,写法如下:
connect( state => ({key:value}), //映射状态 {key:xxxxxAction} //映射操作状态的方法 )(UI组件) -
在UI组件中通过this.props.xxxxxxx读取和操作状态
-
求和案例_react-redux数据共享版
- 定义一个Pserson组件,和Count组件通过redux共享数据。
- 为Person组件编写:reducer、action,配置constant常量。
- 重点:Person的reducer和Count的Reducer要使用combineReducers进行合并,合并后的总状态是一个对象!!!
- 交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”。
求和案例_react-redux开发者工具的使用
yarn add redux-devtools-extension- store中进行配置
import {composeWithDevTools} from 'redux-devtools-extension'
const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
求和案例_react-redux最终版
- 所有变量名字要规范,尽量触发对象的简写形式。
- reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer
antd
antd的按需引入+自定主题
- 安装依赖:
yarn add craco-less @craco/craco babel-plugin-import
- 修改package.json
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject"
}
- 根目录下创建
config-overrides.js
// 配置具体的修改规则
const CracoLessPlugin = require('craco-less');
module.exports = {
babel: { // 支持装饰器
plugins: [[
"import", {
"libraryName": "antd",
"libraryDirectory": "es",
"style": true //设置为true即是less
}
]],
},
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
lessOptions: {
modifyVars: { '@primary-color': '#1DA57A' },
javascriptEnabled: true,
},
},
},
},
],
};
备注:不用在组件里亲自引入样式了,即:import 'antd/dist/antd.css'应该删掉
react 进阶
setState更新状态的2种写法
-
setState(stateChange, [callback])------对象式的setStatestateChange为状态改变对象(该对象可以体现出状态的更改)callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
-
setState(updater, [callback])------函数式的setStateupdater为返回stateChange对象的函数。updater可以接收到state和props。callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。 总结:
- 对象式的setState是函数式的setState的简写方式(语法糖)
- 使用原则:
- 如果新状态不依赖于原状态 ===> 使用对象方式
- 如果新状态依赖于原状态 ===> 使用函数方式
- 如果需要在setState()执行后获取最新的状态数据, 要在第二个callback函数中读取
路由组件的lazyLoad
-
通过React的lazy函数配合
import()函数动态加载路由组件 ===> 路由组件代码会被分开打包const Login = lazy(()=>import('@/pages/Login'))
-
通过
<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面
<Suspense fallback={<h1>loading.....</h1>}>
<Switch>
<Route path="/xxx" component={Xxxx}/>
<Redirect to="/login"/>
</Switch>
</Suspense>
React Hook/Hooks
- Hook是React 16.8.0版本增加的新特性/新语法
- 可以让你在函数组件中使用 state 以及其他的 React 特性
三个常用的Hook
State Hook: React.useState()Effect Hook: React.useEffect()Ref Hook: React.useRef()
3. State Hook
- State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
- 语法:
const [xxx, setXxx] = React.useState(initValue) useState()说明:- 参数: 第一次初始化指定的值在内部作缓存
- 返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
- setXxx()2种写法:
setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
4. Effect Hook
- Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
- React中的副作用操作:
- 发ajax请求数据获取
- 设置订阅 / 启动定时器
- 手动更改真实DOM
- 语法和说明:
useEffect(() => { // 在此可以执行任何带副作用操作 return () => { // 在组件卸载前执行 // 在此做一些收尾工作, 比如清除定时器/取消订阅等 } }, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行 - 可以把 useEffect Hook 看做如下三个函数的组合
componentDidMount()componentDidUpdate()componentWillUnmount()
Ref Hook
- Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
- 语法:
const refContainer = useRef() - 作用:保存标签对象,功能与
React.createRef()一样
Fragment
-
使用
<Fragment><Fragment><></>
-
作用
- 可以不用必须有一个真实的DOM根标签了
Context
注意:在应用开发中一般不用context, 一般都它的封装react插件
-
理解
- 一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信
-
使用
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>
组件优化
Component的2个问题
-
只要执行
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来优化
render props
如何向组件内部动态传入带内容的结构(标签)?
- Vue中:
- 使用slot技术, 也就是通过组件标签体传入结构
<AA><BB/></AA>
- 使用slot技术, 也就是通过组件标签体传入结构
- React中:
- 使用
children props: 通过组件标签体传入结构 - 使用
render props: 通过组件标签属性传入结构, 一般用render函数属性
- 使用
children props
<A>
<B>xxxx</B>
</A>
{this.props.children}
问题: 如果B组件需要A组件内的数据, ==> 做不到
render props
<A render={(data) => <C data={data}></C>}></A>
A组件: {this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示 {this.props.data}
错误边界
理解:
错误边界:用来捕获后代组件错误,渲染出备用页面
特点:
只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误
使用方式:
getDerivedStateFromError配合componentDidCatch
// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {
console.log(error);
// 在render之前触发
// 返回新的state
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// 统计页面的错误。发送请求发送到后台去
console.log(error, info);
}
组件通信方式总结
方式:
- props:
children propsrender props
- 消息订阅-发布:
pubs-sub、event等等
- 集中式管理:
redux、dva等等
- conText:
- 生产者-消费者模式
组件间的关系
- 父子组件:
props - 兄弟组件(非嵌套组件):消息订阅-发布、集中式管理
- 祖孙组件(跨级组件):消息订阅-发布、集中式管理、
conText(用的少)