一、TODOList案例相关知识点
- 拆分组件、实现静态组件。注意:className、style的写法
- 动态初始化列表,如何确定将数据放在哪个组件的state中?
- 某个组件使用:放在自身的state中
- 某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
- 关于父子之间通信:
- 【父组件】给【子组件】传递数据:通过props传递
- 【子组件】给【父组件】传递数据:通过props传递,要求父组件提前给子组件传递一个函数
- 注意defaultChecked和checked的区别,类似的还有defaultValue和value
- 状态在哪里,操作状态的方法就在哪里
二、react脚手架配置代理总结
方法一
在package.json中追加如下配置
"proxy": "http://localhost:5000"
说明:
- 优点:配置简单,前端请求
资源时可以不加任何前缀 - 缺点:不能配置多个代理
- 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000(优先匹配前端资源)
方法二
1、第一步:创建代理配置文件
在src下创建配置文件:src/setupProxy.js
2、编写setupProxy.js配置具体代理规则
// Common.js语法
const { createProxyMiddleware } = require('http-proxy-middleware')
module.exports = function(app) {
app.use(
createProxyMiddleware('/api1', { // api1是需要转发的请求(所有带api1前缀的请求都会转发给5000)
target: 'http://localhost:5000', // 配置转发目标地址(能返回数据的服务器地址)
changeOrigin: true, // 控制服务器收到的请求头中host字段的值
/*
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:500
changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
changeOrigin默认值为false,但我们一般将changeOrigin值设为true
*/
pathRewrite: { '/api1': ''} // 去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
}),
createProxyMiddleware('/api2', {
target: 'http://localhost:5001',
changeOrigin: true,
pathRewrite: { '/api2': ''}
})
)
}
说明:
- 优点:可以配置多个代理,可以灵活的控制请求是否走代理
- 缺点:配置繁琐,前端请求资源时必须加前缀
三、github搜索案例相关知识点
- 设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办
- ES6小知识点:解构赋值+重命名
let obj = {a: {b: 1}}
const {a} = obj // 传统解构赋值
const {a: {b}} = obj // 连续解构赋值
const {a:{b:{value}}} = obj // 连续解构赋值+重命名
3, 消息订阅与发布机制
- 先订阅、再发布(理解:有一种隔空对话的感觉)
- 适用于任意组件间的通信
- 要在组件的componentWillUnmount中取消订阅
- fetch发送请求(关注分离的设计思想)
try {
const response = await fetch(`/api1/search/users?q=${keyword}`)
const data = await response.json()
console.log(data)
} catch (error) {
console.log('请求出错',error)
}
四、路由的基本使用
- 明确好界面中的导航区、展示区
- 导航区的a标签改为Link标签:
<Link to="xxx">Demo</Link> - 展示区写Route标签进行路径的匹配:
<Route path="/xxx" component={Demo}>(component的c是小写) <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指定样式名
- 标签体内容是一个特殊的标签属性
- 通过this.props.children可以获取标签体内容
七、Switch的使用
- 通常情况下,path和component是一一对应关系
- Switch可以提高路由匹配效率(单一匹配)
八、解决多级路径刷新页面样式丢失的问题
- public/index.html中引入样式时不写 ./ 写 / (常用)
- public/index.html中引入样式时不写 ./ 写 %PUBLIC_URL% (常用)---限于react脚手架中这样写
- 使用HashRouter
九、路由的严格匹配和模糊匹配
- 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
- 开启严格匹配,
<Route exact path='/about' component={About}></Route> - 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
十、Redirect的使用
- 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由
- 具体编码
<Switch>
<Route path='/about' component={About}></Route>
<Route path='/home' component={Home}></Route>
<Redirect to="/about"></Redirect>
</Switch>
十一、嵌套路由
- 注册路由时要写上父路由的path值
- 路由的匹配时按照注册路由的顺序进行的
十二、向路由组件传递参数
- params参数
路由链接(携带参数):<Link to="/demo/test/tom/18">详情</Link>
注册路由(声明接收):<Route to="/demo/test/:name/:age" component={Test}></Route>
接收参数:const { name, age } = this.props.match.params - search参数
路由链接(携带参数):<Link to="/demo/test?name=tom&age=18">详情</Link>
注册路由(无需参数,正常注册即可):<Route to="/demo/test" component={Test}></Route>
接收参数:this.props.location.search
备注:获取到的search是urlencoded编码字符串,需要借助query-string解析 - state参数
路由链接(携带参数):<Link to={{pathname: "/demo/test", state: {name:tom,age:18}}}>详情</Link>
注册路由(无需参数,正常注册即可):<Route to="/demo/test" component={Test}></Route>
接收参数:this.props.location.state
备注:当前页面(标签页)刷新也可以保留住参数
十三、编程式路由导航
借助this.props.history对象上的API对操作路由跳转、前进、后退
this.props.history.push()
this.props.history.replace()
this.props.history.goBack()
this.props.history.goForward()
this.props.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可以用于解决一些路径错误相关的问题
十五、antd的按需引入+自定主题
- 安装依赖:yarn add react-app-rewired customize-cra babel-plugin-import less less-loader
- 修改package.json
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
- 根目录下创建config-overrides.js
//配置具体的修改规则
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'应该删掉
十六、redux
1、什么是redux
- redux是一个专门用于做状态管理的JS库(不是react插件库)。
- 它可以用在react, angular, vue等项目中, 但基本与react配合使用。
- 作用: 集中式管理react应用中多个组件共享的状态。
2、redux的三个核心概念
2-1、action
- 动作的对象
- 包含2个属性
- type:标识属性, 值为字符串, 唯一, 必要属性
- data:数据属性, 值类型任意, 可选属性
- 例子:{ type: 'ADD_STUDENT',data:{name: 'tom',age:18} }
2-2、reducer
- 用于初始化状态、加工状态。
- 加工时,根据旧的state和action, 产生新的state的纯函数。
2-3、store
- 将state、action、reducer联系在一起的对象
- 如何得到此对象?
- import {createStore} from 'redux'
- import reducer from './reducers'
- const store = createStore(reducer)
- 此对象的功能?
- getState(): 得到state
- dispatch(action): 分发action, 触发reducer调用, 产生新的state
- subscribe(listener): 注册监听, 当产生了新的state时, 自动调用
十七、求和案例
redux精简版
- 去除Count组件自身的状态
- src下建立
- src
- redux
- store.js
- count_reducer.js
- store.js
- 引入redux中的createStore函数,创建一个store
- cretaStore调用时要传入一个为其服务的reducer
- 记得暴露store对象
- count_reducer.js
- reducer的本质是一个函数,接收:preState、action,返回加工后的状态
- reducer有两个作用:初始化状态,加工状态
- reducer被第一次调用时,是store自动触发的,传递的preState是undefined,传递的action是:{type:'@@REDUX?INIT_a.2.b.4}
- 在index.js中监测store中状态的改变,一旦发生改变重新渲染 备注:redux只负责管理状态,至于状态的改变驱动着页面的改变,要靠我们自己写
redux完整版
新增文件:
- count_action.js 专门用于创建action对象
- constant.js 放置容易写错的type值
redux异步action版
- 明确:延迟的动作不想交给组件自身,想交给action
- 何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回
- 具体编码:
- npm install redux-thunk,并配置在store中
- 创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务
- 异步任务有结果后,分发一个同步的action去真正操作数据
- 备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action
react-redux的基本使用
- 明确两个概念:
- UI组件:不能使用任何redux的api,只负责页面的呈现、交互等
- 容器组件:负责和redux通信,将结果交给UI组件
- 如何创建一个容器组件--靠react-redux的connect函数
connect(mapStateToProps, mapDispatchToProps)(UI组件)
- mapStateToProps:映射状态,返回值是一个对象
- mapDispatchToProps:映射操作状态的方法,返回值是一个对象
3. 备注1:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入 4. 备注2:mapDispatchToProps也可以是一个对象
react-redux优化
- 容器组件与UI组件混成一个文件
- 无需自己给容器组件传递store,给
<App>包裹一个<Provider store={store}>即可 - 使用了react-redux后也不用再自己监测redux中状态的改变了,容器组件可以自动完成这个工作
- mapDispatchToProps也可以简单的写成一个对象
- 一个组件要和redux"打交道"要经过哪几步?
- 定义好UI组件--不暴露
- 引入connect生成一个容器组件,暴露,写法如下:
connect(
state => ({ key: value }),
{
key: xxxAction
}
)(UI组件)
- 在UI组件中通过this.props.xxxx读取和操作状态
react-redux数据共享版
- 定义一个Person组件,和Count组件提供redux共享数据
- 为Person组件编写:reducer、action,配置constant常量
- 重点:Person的reducer和Count的reducer要使用combineReducers进行合并,合并后的总状态是一个对象
- 交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”
react-redux开发者工具的使用
- npm install redux-devtools-extension
- store中进行配置
import { composeWithDevTools } from 'redux-devtools-extension'
const store = createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)))
react-redux的最终版
- 所有变量名字要规范,尽量触发对象简写形式
- reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer