react学习记录(三)

146 阅读9分钟

一、TODOList案例相关知识点

  1. 拆分组件、实现静态组件。注意:className、style的写法
  2. 动态初始化列表,如何确定将数据放在哪个组件的state中?
  • 某个组件使用:放在自身的state中
  • 某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
  1. 关于父子之间通信:
  • 【父组件】给【子组件】传递数据:通过props传递
  • 【子组件】给【父组件】传递数据:通过props传递,要求父组件提前给子组件传递一个函数
  1. 注意defaultChecked和checked的区别,类似的还有defaultValue和value
  2. 状态在哪里,操作状态的方法就在哪里

二、react脚手架配置代理总结

方法一

package.json中追加如下配置

"proxy": "http://localhost:5000"

说明:

  1. 优点:配置简单,前端请求资源时可以不加任何前缀
  2. 缺点:不能配置多个代理
  3. 工作方式:上述方式配置代理,当请求了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': ''}
    })
  )
}

说明:

  1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理
  2. 缺点:配置繁琐,前端请求资源时必须加前缀

三、github搜索案例相关知识点

  1. 设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办
  2. ES6小知识点:解构赋值+重命名
let obj = {a: {b: 1}}
const {a} = obj // 传统解构赋值
const {a: {b}} = obj // 连续解构赋值
const {a:{b:{value}}} = obj // 连续解构赋值+重命名

3, 消息订阅与发布机制

  • 先订阅、再发布(理解:有一种隔空对话的感觉)
  • 适用于任意组件间的通信
  • 要在组件的componentWillUnmount中取消订阅
  1. fetch发送请求(关注分离的设计思想)
try {
  const response = await fetch(`/api1/search/users?q=${keyword}`)
  const data = await response.json()
  console.log(data)
} catch (error) {
  console.log('请求出错',error)
}

四、路由的基本使用

  1. 明确好界面中的导航区、展示区
  2. 导航区的a标签改为Link标签:<Link to="xxx">Demo</Link>
  3. 展示区写Route标签进行路径的匹配:<Route path="/xxx" component={Demo}>(component的c是小写)
  4. <App>的最外侧包裹了一个<BrowserRouter><HashRouter>

五、路由组件和一般组件

  1. 写法不同: 一般组件:<Demo/> 路由组件:<Route path="/demo" component={Demo}/>
  2. 存放位置不同: 一般组件:components 路由组件:pages
  3. 接收到的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

  1. NavLink可以实现路由链接的高亮,通过activeClassName指定样式名
  2. 标签体内容是一个特殊的标签属性
  3. 通过this.props.children可以获取标签体内容

七、Switch的使用

  1. 通常情况下,path和component是一一对应关系
  2. Switch可以提高路由匹配效率(单一匹配)

八、解决多级路径刷新页面样式丢失的问题

  1. public/index.html中引入样式时不写 ./ 写 / (常用)
  2. public/index.html中引入样式时不写 ./ 写 %PUBLIC_URL% (常用)---限于react脚手架中这样写
  3. 使用HashRouter

九、路由的严格匹配和模糊匹配

  1. 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
  2. 开启严格匹配,<Route exact path='/about' component={About}></Route>
  3. 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

十、Redirect的使用

  1. 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由
  2. 具体编码
<Switch>
  <Route path='/about' component={About}></Route>
  <Route path='/home' component={Home}></Route>
  <Redirect to="/about"></Redirect>
</Switch>

十一、嵌套路由

  1. 注册路由时要写上父路由的path值
  2. 路由的匹配时按照注册路由的顺序进行的

十二、向路由组件传递参数

  1. params参数
    路由链接(携带参数):<Link to="/demo/test/tom/18">详情</Link>
    注册路由(声明接收):<Route to="/demo/test/:name/:age" component={Test}></Route>
    接收参数:const { name, age } = this.props.match.params
  2. 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解析
  3. 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的区别

  1. 底层原理不同
    BrowserRouter使用的是H5的history API,不兼容IE9及以下版本
    HashRouter使用的是URL的哈希值
  2. path表现形式
    BrowserRouter的路径中没有#。例如:localhost:3000/demo/test
    HashRouter的路径中包含#,例如:localhost:3000/#/demo/test
  3. 刷新后对路由state参数的影响
    BrowserRouter没有任何影响,因为state报错在history对象中
    HashRouter刷新后会导致路由state参数的丢失
  4. 备注:HashRouter可以用于解决一些路径错误相关的问题

十五、antd的按需引入+自定主题

  1. 安装依赖:yarn add react-app-rewired customize-cra babel-plugin-import less less-loader
  2. 修改package.json
  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-scripts eject"
  },
  1. 根目录下创建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' },
    }
  }),
);
  1. 备注:不用在组件里亲自引入样式了,即:import 'antd/dist/antd.css'应该删掉

十六、redux

1、什么是redux

  1. redux是一个专门用于做状态管理的JS库(不是react插件库)。
  2. 它可以用在react, angular, vue等项目中, 但基本与react配合使用。
  3. 作用: 集中式管理react应用中多个组件共享的状态。

2、redux的三个核心概念

2-1、action

  1. 动作的对象
  2. 包含2个属性
  • type:标识属性, 值为字符串, 唯一, 必要属性
  • data:数据属性, 值类型任意, 可选属性
  1. 例子:{ type: 'ADD_STUDENT',data:{name: 'tom',age:18} }

2-2、reducer

  1. 用于初始化状态、加工状态。
  2. 加工时,根据旧的state和action, 产生新的state的纯函数。

2-3、store

  1. 将state、action、reducer联系在一起的对象
  2. 如何得到此对象?
    1. import {createStore} from 'redux'
    2. import reducer from './reducers'
    3. const store = createStore(reducer)
  3. 此对象的功能?
    1. getState(): 得到state
    2. dispatch(action): 分发action, 触发reducer调用, 产生新的state
    3. subscribe(listener): 注册监听, 当产生了新的state时, 自动调用

十七、求和案例

redux精简版

  1. 去除Count组件自身的状态
  2. src下建立
- src
    - redux
      - store.js
      - count_reducer.js
  1. store.js
  • 引入redux中的createStore函数,创建一个store
  • cretaStore调用时要传入一个为其服务的reducer
  • 记得暴露store对象
  1. count_reducer.js
  • reducer的本质是一个函数,接收:preState、action,返回加工后的状态
  • reducer有两个作用:初始化状态,加工状态
  • reducer被第一次调用时,是store自动触发的,传递的preState是undefined,传递的action是:{type:'@@REDUX?INIT_a.2.b.4}
  1. 在index.js中监测store中状态的改变,一旦发生改变重新渲染 备注:redux只负责管理状态,至于状态的改变驱动着页面的改变,要靠我们自己写

redux完整版

新增文件:

  1. count_action.js 专门用于创建action对象
  2. constant.js 放置容易写错的type值

redux异步action版

  1. 明确:延迟的动作不想交给组件自身,想交给action
  2. 何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回
  3. 具体编码:
  • npm install redux-thunk,并配置在store中
  • 创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务
  • 异步任务有结果后,分发一个同步的action去真正操作数据
  1. 备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action

react-redux的基本使用

  1. 明确两个概念:
  • UI组件:不能使用任何redux的api,只负责页面的呈现、交互等
  • 容器组件:负责和redux通信,将结果交给UI组件
  1. 如何创建一个容器组件--靠react-redux的connect函数
connect(mapStateToProps, mapDispatchToProps)(UI组件)
- mapStateToProps:映射状态,返回值是一个对象
- mapDispatchToProps:映射操作状态的方法,返回值是一个对象

3. 备注1:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入 4. 备注2:mapDispatchToProps也可以是一个对象

react-redux优化

  1. 容器组件与UI组件混成一个文件
  2. 无需自己给容器组件传递store,给<App>包裹一个<Provider store={store}>即可
  3. 使用了react-redux后也不用再自己监测redux中状态的改变了,容器组件可以自动完成这个工作
  4. mapDispatchToProps也可以简单的写成一个对象
  5. 一个组件要和redux"打交道"要经过哪几步?
  • 定义好UI组件--不暴露
  • 引入connect生成一个容器组件,暴露,写法如下:
connect(
  state => ({ key: value }),
  {
    key: xxxAction
  }
)(UI组件)
  • 在UI组件中通过this.props.xxxx读取和操作状态

react-redux数据共享版

  1. 定义一个Person组件,和Count组件提供redux共享数据
  2. 为Person组件编写:reducer、action,配置constant常量
  3. 重点:Person的reducer和Count的reducer要使用combineReducers进行合并,合并后的总状态是一个对象
  4. 交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”

react-redux开发者工具的使用

  1. npm install redux-devtools-extension
  2. store中进行配置
import { composeWithDevTools } from 'redux-devtools-extension'
const store = createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)))

react-redux的最终版

  1. 所有变量名字要规范,尽量触发对象简写形式
  2. reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer