React基础
1.jsx语法规则:
- 定义虚拟DOM时,不要写引号。
- 标签中混入JS表达式时要用{}。
- 样式的类名指定不要用class,要用className。
- 内联样式,要用
style={{key : value}}的形式去写。 - 只有一个根标签
- 标签必须闭合
- 标签首字母
(1). 若小写字母开头,则将改标签转为html中同名元素,若html中无该标签对应的同名元素,则报错。
(2). 若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
2.函数式组件
1 | //1.创建函数式组件function |
3.类式组件
- 创建类式组件
1
2
3
4
5
6
7class MyComponent extends React.Component {
render(){
//render是放在哪里的?-- MyComponent的原型对象上,供实例使用。
//render中的this是谁?— MyComponent的实例对象〈=>MyComponent组件实例对象。console.log( ' render中的this : ' ,this);
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
} - 渲染组件到页面
1
2
3
4
5
6
7ReactDOM.render(<MyComponent/>,document.getElementById( 'test'))
/*
执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
*/
4.生命周期函数
旧
- 初始化阶段:由ReactDOM.render()触发—初次渲染
- constructor()
- componentwillMount()
- render()
- componentDidMount()
- 更新阶段:由组件内部this.setSate()或父组件重新render触发
- shouldComponentUpdate()
- componentwillUpdate()
- render()
- componentDidUpdate()
- 卸载组件:由ReactDOM.unmountComponentAtNode()触发
- componentwi11Unmount()
新
- 初始化阶段:由ReactDOM.render()触发—初次渲染
- constructor()
- getDerivedStateFromProps()—-当state完全取决于props时使用
- render()
- componentDidMount()
- 更新阶段:由组件内部this.setSate()或父组件重新render触发
- getDerivedStateFromProps()
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate()—–更新之前获取快照
- componentDidUpdate()
- 卸载组件:由ReactDOM.unmountComponentAtNode()触发
1.componentwi11Unmount()
5.动态初始化列表,如何确定将数据放在哪个组件的state中?
- 某个组件使用:放在自身的state中
- 某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
6.关于父子之间通信:
- 【父组件】给【子组件】传递数据:通过props传递
- 【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
7.编写setupProxy.js配置具体代理规则:
1 | const proxy = require('http-proxy-middleware ') |
8.消息订阅与发布机制
npm install pubsub-js
1.先订阅,再发布(理解:有一种隔空对话的感觉)
2.适用于任意组件间通信
3.要在组件的componentwillUnmount中取消订阅
9.路由的基本使用
- 路由跳转标签改为Link标签
<Link to="/xxxxx" >Demo</Link><NavLink activeClassName="myActiveClassName" to="/xxxxx" >Demo</NavLink> - 展示区写Route标签进行路径的匹配
<Route path='/xxxx' component={ Demo}/> 的最外侧包裹了一个 或
10.路由组件与一般组件
- 写法不同:
一般组件:<Demo/>
路由组件:<Route path="/demo" component={Demo}/> - 存放位置不同:
一般组件: components
路由组件: pages - 接收到的props不同:
一般组件:写组件标签时传递了什么,就能收到什么
路由组件:接收到三个固定的属性1
2
3
4
5
6
7
8
9
10
11
12
13
14history:
go: f go(n)
goBack: f goBack()
goForward: f goForward()
push: f push(path, state)
replace: f replace(path,state)
location:
pathname: "/ about"
search: ""
state: undefined
match:
params: {}
path: "/about"
ur1: "/ about"
11.NavLink与封装NavLink
- NavLink可以实现路由链接的高亮,通过activeclassName指定样式名
- 标签体内容是一个特殊的标签属性
- 通过this.props.children可以获取标签体内容
12.Switch的使用
- 通常情况下,path和component是一一对应的关系。
- Switch可以提高路由匹配效率(单一匹配)。
1
2
3
4
5
6
7
8
9import {Switch, Route} from 'react-router-dom'
import About from '../pages/About'
import Home from '../pages/Home'
<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
</Switch>
13.history路由模式下解决多级路径刷新页面样式丢失的问题
- public/index.htm1 中引入样式时不写./ 写/(常用)
- public/index.htm1 中引入样式时不写﹒/写%PUBLIC_URL%(常用)
- 使用HashRouter
14.路由严格匹配(慎用)
<Route path="/home" component={Home}/>
路由默认开启模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)<Link to="/home/a/b" >Demo</Link>
http://localhost:3000/home/a/b未匹配到a或b的情况下默认给到/home页面
页面没有问题一般不开启严格匹配,如果出现由于模糊匹配引发的问题,这种情况再开启严格匹配。有些时候开启会导致无法继续匹配二级路由
用法<Route exact path="/home" component={Home}/>
15.Redirect的使用
1.一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由
2.具体编码:
1 | <Switch> |
16.嵌套路由
- 注册子路山时要写上父路山的path值
- 路山的匹配是按照注册路由的顺序进行的
17.向路由组件传递参数
params参数
路由链接(携带参数):<Link to='/demo/test/tom/18'}>详情</Link>
注册路由(声明接收):<Route path="/demo/test/:name/:age" component={Test}/>
接收参数:this.props.match.paramssearch参数
路由链接(携带参数):<Link to=' /demo/test?name=tom&age=18'}>诈情</Link>
注册路由(无需声明,正常注册即可):<Route path=" /demo/test" component={Test}/>
接收参数:this.props.location.search
备注:获取到的search是urlencoded编码字符串,需要借助querystring解析1
2
3import qs from 'querystring'
qs.parse('id=1&name=tom') //{id:'1',name:'tom'}
qs.stringfy({id:'1',name:'tom'}) //id=1&name=tomstate参数
路由链接(携带参数):<Link to={{path: '/demo/test' ,state:{name : 'tom' ,age:18]}}>详情</Link>
注册路由(无需声明,正常注册即可):<Route path=" /demo/test" component={Test}/>
接收参数:this.props.location.state
备注:刷新也可以保留住参数
18.路由push模式和replace模式
<Link replace to={{path:'/demo/test' ,state:{name : 'tom' ,age:18]}}>详情</Link>
19.编程式路由导航
借助this.prosp.history对象上的API对操作路由跳转、前进、后退
- this.prosp.history.push()
- this.prosp.history.replace()
- this.prosp.history.goBack()
- this.prosp.history.goForward()
- this.prosp.history.go()
1 | this.propps.histroy.push('/xxx/id/name') |
20.withRouter的使用
1 | import React, { Component } from 'react' |
21.BrowserRouter、HashRouter的区别
- 底层原理不一样:
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。HashRouter使用的是URL的哈希值。 - ur1表现形式不一样
BrowserRouter的路径中没有#,例如: localhost:3000/demo/testHashRouter的路径包含#,例如: localhost:3000/#/demo/test - 刷新后对路由state参数的影响
(1). BrowserRouter没有任何影响,因为state保存在history对象中。
(2). HashRouter刷新后会导致路由state参数的丢失。 - 备注: HashRouter可以用于解决一些路径错误相关的问题。
22.antd的按需引入+自定主题
- 安装依赖:
yarn add react-app-rewired customize-cra babel-plugin-import less less-loader - 修改package.json
1
2
3
4
5
6"scripts": {
"start" :"react-app-rewired start",
"build" :"react-app-rewired build",
"test" : "react-app-rewired test",
"eject" : "react-scripts eject"
}, - 根目录下创建config-overrides.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//配置具体的修改规则
const { override,fixBabelImports,addLessLoader} = require( ' customize-cra');
module.exports = override(
fixBabelImports('import , {
libraryName: 'antd',
libraryDirectory: 'es',
style: true,
}),
addLessLoader({
lessoptions:{
javascriptEnabled: true,
modifyVars: {'@primary-color' : 'green'},
}
})
}) - 备注:不用在组件里亲自引入样式了,即: import ‘antd/dist/antd.css’应该删掉
23.redux精简版
(1). 去除Count组件自身的状态
(2). src下建立:
-redux
-store.js
-count_reducer.js
(3). store.js:
1).引入redux中的createStore函数,创建一个store
2).createstore调用时要传入一个为其服务的reducer
3).记得暴露store对象
(4). count_reducer.js:
1).reducer的本质是一个函数,接收: preState,action,返回加工后的状态
2).reducer有两个作用:初始化状态,加工状态
3).reducer被第一次调用时,是store自动触发的,传递的preState是undefined,传递的action是:{type : ‘@@REDUX/ INIT_a.2.b.4}
(5). 在index.js中监测store中状态的改变,一旦发生改变重新渲染<App/>
备注: redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。
24.redux完整版
新增文件:
- count_action.js 专门用于创建action对象
- constant.js放置容易写错的type值工

25.redux异步action版
- 明确:延迟的动作不想交给组件自身,想交给action
- 何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。
- 具体编码:
- yarn add redux-thunk,并配置在store中
- 创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
- 异步任务有结果后,分发一个同步的action去真正操作数据
- 备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。
26.react-redux基本使用
- 明确两个概念:
1). UI组件:不能使用任何redux的api,只负责页面的呈现、交互等。
2). 容器组件:负责和redux通信,将结果交给UI组件。
2. 如何创建一个容器组件–—靠react-redux的connect函数
connect(mapStateToProps,mapDispatchToProps)(UI组件)
-mapstateToProps:映射状态,返回值是一个对象
-mapDispatchToProps:映射操作状态的方法,返回值是一个对象
3. 备注:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
4. 备注2: mapDispatchToProps,也可以是一个对象
27.react-redux优化
- 容器组件和UI组件混成一个文件
- 无需自己给容器组件传递store,给
<App/>包裹一个<Provider store={store}>即可。 - 使用了react-redux后也不用再自己检测redux中状态的改变了,
容器组件可以自动完成这个工作。 - mapDispatchToProps也可以简单的写成一个对象
- 一个组件要和redux“打交道”要经过那几步?
- (1).定义好UI组件—不暴露
- (2).引入connect生成一个容器组件,并暴露,写法如下:
1
2
3
4connect(
state =>({key:value}), // 映射状态
{key : xxxxXAction} // 映射操作状态的方法
)(UI组件) - (3).在UI组件中通过this.props.xxxxxxx读取和操作状态
28.react-redux数据共享
- 定义一个Pserson组件,和lCount组件通过redux共享数据。
- 为Person组件编写: reducer、action,配置constant常量。
- 重点:Person的reducer和Count的Reducer要使用combineReducers进行合并,合并后的总状态是一个对象!
- 交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”。
29.react-redux开发者工具的使用
- yarn add redux-devtools-extension
- store中进行配置
1
2import {composewithDevTools} from 'redux-devtools-extension'
const store = createStore(allReducer,composewithDevTools(applyMiddleware(thunk)))
30.react-redux代码优化
- 所有变量名字要规范,尽量触发对象的简写形式。
- reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer
React扩展
1. setState
setState更新状态的2种写法
- setState(statechange,[callback])——对象式的setState
- 1.stateChange为状态改变对象(该对象可以体现出状态的更改)
- 2.callback是可选的回调函数,它在状态更新完毕、界面也更新后(render调用后)才被调用
- setState(updater,[callback])——函数式的setState
- 1.updater为返回stateChange对象的函数。
- 2.updater可以接收到state和props.
- 3.callback是可选的回调函数,它在状态更新、界面也更新后(render调用后)才被调用。
总结:
- 对象式的setstate是函数式的setstate的简写方式(语法糖)
- 使用原则:
- (1).如果新状态不依赖于原状态===>使用对象方式
- (2).如果新状态依赖于原状态===≥使用函数方式
- (3).如果需要在setstate()执行后获取最新的状态数据,要在第二个callback函数中读取
2.lazyLoad
路由组件的lazyLoad
1 | //1.通过React的lazy函数配合import()函数动态加载路由组件===>路由组件代码会被分开打包 |
3.Hooks
1. React Hook/Hooks是什么?
- Hook是React 16.8.0版本增加的新特性/新语法
- 可以让你在函数组件中使用state以及其他的React特性
2.三个常用的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个为更新状态值的函数 - setXx×()2种写法:
setXxx(newvalue):参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值
setxxx(value => newvalue):参数为函数,接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值
1 | import React from "react" |
4.Effect Hook
- Effect Hook可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
- React中的副作用操作:
发ajax请求数据
获取设置订阅/启动定时器
手动更改真实DOM - 语法和说明:
1
2
3
4
5useEffect(( ={
//在此可以执行任何带副作用操作
return ( = {[//在组件卸载前执行
//在此做一些收尾工作,比如清除定时器/取消订阅等
},[statevalue])//如果指定的是[],回调函数只会在第一次render()后执行 - 可以把useEffect Hook看做如下三个函数的组合
componentDidMount()
componentDidupdate()
componentwillUnmount()
5.Ref Hook
- Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
- 语法: const refcontainer = useRef(O
- 作用:保存标签对象,功能与React.createRef()一样
4.Fragment
使用:
1 | <Fragment><Fragment> |
作用:
可以不用必须有一个真实的DOM根标签了
5.Context
理解
—种组件间通信方式,常用于【祖组件】与【后代组件】间通信使用
- 创建context容器对象:
1
const xxxContext = React.createContext()
- 渲染子组时,外面包裹xxxContext.Provider,通过value属性给后代组件传递数据:
1
2
3<XXXContext.Provider value={数据}>
子组件
</xxxContext.Provider> - 后代组件读取数据:
1
2
3
4
5
6
7
8
9//第一种方式:仅适用于类组件
static contextType = xxxContext//声明接收context
this.context //读取context中的value数据
//第二种方式:丞数组件与类组件都可以
<XXXContext.consumer>
{
value =>( /* value就是context中的value数据要显示的内容 */ )
}
</xxxcontext.Consumer>
示例:
1 | import React, { Component } from 'react' |
6.组件优化
Component的2个问题
- 只要执行setState(),即使不改变状态数据,组件也会重新render() ==>效率低
- 只当前组件重新render(),就会自动重新render子组件,纵使子组件没有用到父组件的任何数据==>效率低
效率高的做法只有当组件的state或props数据发生改变时才重新render()
原因
Component中的shouldComponentUpdate()总是返回true
- 解决办法1:
重写shouldComponentUpdate()方法
比较新旧state或props数据,如果有变化才返回true,如果没有返回fa1se - 办法2:
使用PureComponent
PureComponent重写了shouldComponentUpdate(),只有state或props数据有变化才返回true注意:
只是进行state和props数据的浅比较,如果只是数据对象内部数据变了,返回false不要直接修改state数据,而是要产生新数据
项目中一般使用PureComponent来优化
7.render props(相当于vue中的插槽)
如何向组件内部动态传入带内容的结构(标签)?
vue中:
使用slot技术,也就是通过组件标签体传入结构
React中:
使用chi1dren props :通过组件标签体传入结构
使用render props:通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性
选择语言
children props
1 | <A> |
render props
1 | <A render={(data) =><c data={data}></C>}></A> |
示例:
1 | import React, { Component } from 'react' |
8.错误边界
理解:
错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面
特点:
只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误
使用方式:
getDerivedStateFromError配合componentDidCatch
1 | //生命周期函数,一旦后台组件报错,就会触发 |
示例代码:
1 | export default class Parent extends Component { |
9. 组件通信方式总结
组件间的关系:
- 父子组件
- 兄弟组件(非嵌套组件)
- 祖孙组件(跨级组件)
几种通信方式:
- props:
(1).children props
(2).render props - 消息订阅-发布:
pubs-sub、event等等 - 集中式管理:
redux、dva等等 - conText:
生产者-消费者模式
比较好的搭配方式:
父子组件: props
兄弟组件:消息订阅-发布、集中式管理
祖孙组件(跨级组件)∶消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)
React Router 6快速上手
概述
- React Router以三个不同的包发布到npm上,它们分别为:
- react-router:路由的核心库,提供了很多的:组件、钩子。
- react-router-dom:包含react-router所有内容,并添加一些专门用于DOM的组件,例如
<BrowserRouter>等。 - react-router-native:包括react-router所有内容,并添加一些专门用于ReactNative的API,例如:
<NativeRouter>等。
- 与React Router 5.x版本相比,改变了什么?
- 内置组件的变化:移除
<Switch/>,新增<Routes/>等。 - 语法的变化:
component={About}变为element={<About/>}等。 - 新增多个hook:
useParams、useNavigate、useMatch等。 - 官方明确推荐函数式组件了! ! !
…
- 内置组件的变化:移除
1.<BrowserRouter>
- 说明:
<BrowserRouter>用于包裹整个应用。 - 示例代码:
1
2
3
4
5
6
7
8
9import React from "react";
import ReactDOM from "react-dom" ;
import { BrowserRouter } from "react-router-dom" ;
ReactDOM.render(
<BrowserRouter>
{/*整体结构(通常为App组件)*/}
</BrowserRouter>,root
);
2. <HashRouter>
- 说明:作用与
<BrowserRouter>一样,但<HashRouter>修改的是地址栏的hash值。 - 备注:6.x版本中
<HashRouter>、<BrowserRouter>的用法与5.x相同。
3.<Routes/> 与<Route/>
- v6版本中移除了先前的
<Switch>,引入了新的替代者:<Routes>。 <Routes>和<Route>要配合使用,且必须要用<Routes>包裹<Route>。<Route>相当于一个if语句,如果其路径与当前URL匹配,则呈现其对应的组件。<Route caseSensitive>属性用于指定:匹配时是否区分大小写(默认为false)。- 当URL发生变化时,
<Routes>都会查看其所有子<Route>元素以找到最佳匹配并呈现组件。 <Route>也可以嵌套使用,且可配合useRoutes()配置“路由表”,但需要通过<outlet>组件来渲染其子路由。- 示例代码:
1
2
3
4
5
6
7
8
9
10
11<Routes>
/*path属性用于定义路径,element属性用于定义当前路径所对应的组件*/
<Route path="/login" element={<Login />}></Route>
/*用于定义嵌套路由,home是一级路由,对应的路径/home*/
<Route path="home" element=f<Home />}>
/*test1和 test2是二级路由,对应的路径是/home/test1或/home/test2*/
<Route path="test1" element={<Test />}></Route>
<Route path="test2" element={<Test2 />}></Route>
</Route>
</Routes>
4. <Navigate>
- 作用:只要
<Navigate>组件被渲染,就会修改路径,切换视图。 - replace属性用于控制跳转模式(push 或 replace,默认是push)。
- 示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import React, {useState} from 'react'
import {Navigate} from 'react-router-dom'
export default function About () {
const [num, setNum] = useState(0)
function add(params) {
setNum(num + params)
}
return (
<div className='content'>about
{num !== 4 ? <h2>num的值为{num}</h2> : <Navigate to="/home" replace={true}/> }
<button onClick={() => add(2)}>点击+2</button>
</div>
)
}
NavLink 高亮
1 | export default function App () { |
useRoutes 路由表的使用
在src下新建一个文件夹routes,routes文件夹下新建文件index.js
1 | import About from '../About' |
注册路由
1 | import {NavLink, useRoutes} from 'react-router-dom' |
嵌套路由
路由表:
1 | import About from '../About' |
1 | import React from 'react' |
1 | <NavLink to='/home' className={computedActiveName} end>去home</NavLink> |
备注:
<Outlet />相当于vue中的<router-view />- 父路由NavLink添加end属性,点击子路由,父路由的高亮消失
路由传参
params
路由表:
1 | { |
父路由:
1 | import React, {useState} from 'react' |
子路由
1 | import React from 'react' |
search
父路由:
1 | <li key={item.id}> |
子路由:
1 | import React from 'react' |
state
父路由:
1 | <ul> |
子路由:
1 | import React from 'react' |
函数式路由跳转
案例1:
1 | import { Link, Outlet, useNavigate } from 'react-router-dom' |
案例2:
1 | import { useNavigate } from 'react-router-dom' |
7.uselnRouterContext()
作用:如果组件在<Router>的上下文中呈现,则useInRouterContext钩子返回true,否则返回false。
译:当前组件是否被<BrowserRouter></BrowserRouter>或者<HashRouter></HashRouter>包裹
8. useNavigationType()
- 作用:返回当前的导航类型(用户是如何来到当前页面的)。
- 返回值:POP、PUSH、 REPLACE。
- 备注:POP是指在浏览器中直接打开了这个路由组件(刷新页面)。
9.useOutlet()
- 作用:用来呈现当前组件中渲染的嵌套路由。
- 示例代码:
1
2
3
4const result = useoutlet()
console.log(result)
//如果嵌套路由没有挂载,则result为null
//如果嵌套路由已经挂载,则展示嵌套的路由对象
10.useResolvedPath()
1.作用:给定一个URL值,解析其中的: path、search、hash值。