react速通-2

二十七、脚手架配置代理

一、背景:为什么要配置代理?

在 React 开发中,前端默认运行在 localhost:3000,后端服务运行在 localhost:5000,由于浏览器的同源策略,前端直接请求后端会出现跨域问题。

配置代理的目的,就是让前端请求 "假装" 是同源请求,从而绕过跨域限制。


二、方法一:package.json 配置代理

1. 配置方式

在项目根目录的 package.json 中,直接添加一行配置:

js 复制代码
{
  "proxy": "http://localhost:5000"
}

2. 核心说明

  • 工作原理 :当请求 localhost:3000 上不存在的资源时,会自动转发给配置的目标地址(localhost:5000),优先匹配前端资源,匹配不到再走代理。
  • 优点:配置极简,前端请求时不需要加任何前缀,直接写接口路径即可。
  • 缺点 :只能配置单个代理,无法同时代理多个后端服务。

三、方法二:setupProxy.js 配置多代理(http-proxy-middleware

1. 适用场景

需要同时代理多个后端服务(比如同时对接 localhost:5000localhost:5001),需要更灵活的控制。

2. 配置步骤

步骤 1:创建配置文件

src/ 目录下新建 setupProxy.js 文件。

步骤 2:编写代理规则
js 复制代码
const proxy = require('http-proxy-middleware')

module.exports = function(app) {
  // 代理 /api1 开头的请求到 localhost:5000
  app.use(
    proxy('/api1', {
      target: 'http://localhost:5000', // 目标后端地址
      changeOrigin: true, // 修改请求头的host字段
      pathRewrite: {'^/api1': ''} // 去掉请求前缀,保证后端收到正常路径
    })
  )

  // 代理 /api2 开头的请求到 localhost:5001
  app.use(
    proxy('/api2', {
      target: 'http://localhost:5001',
      changeOrigin: true,
      pathRewrite: {'^/api2': ''}
    })
  )
}

3. 关键配置项详解

配置项 作用说明
target 后端服务的目标地址,代理会把请求转发到这里
changeOrigin 控制请求头中的 host 字段- true:服务器收到的 host 为目标地址(localhost:5000)- false:服务器收到的 host 为前端地址(localhost:3000建议始终设为 true,避免部分服务器拒绝非同源 host 的请求
pathRewrite 重写请求路径,比如把 /api1/user 转为 /user,去掉代理前缀

4. 优缺点

  • 优点

    • 支持配置多个代理,可以同时对接多个后端服务。
    • 可以灵活控制哪些请求走代理,哪些不走。
  • 缺点 :配置相对繁琐,前端请求接口时必须加上代理前缀(如 /api1/xxx)。

二十八、消息订阅与发布


订阅消息

js 复制代码
const token = PubSub.subscribe('消息名', (msg, data) => {})

发布消息

js 复制代码
PubSub.publish('消息名', 数据)

取消订阅

js 复制代码
PubSub.unsubscribe(token)  // 推荐
PubSub.unsubscribe('消息名')

二十九、路由的基本使用

一、React 路由的基本使用(v5 风格)

这是早期 React Router v5 的标准流程,也是很多教材里的经典写法:

  1. 明确页面结构

    把界面拆成「导航区」和「展示区」,导航区用来跳转,展示区用来渲染对应页面。

  2. <Link> 标签替代 <a> 标签

    复制代码
    // 原生 a 标签会刷新页面,Link 只会更新路由,不刷新
    <Link to="/xxxx">Demo</Link>
  3. <Route> 标签做路径匹配

    复制代码
    // path 对应 to 的路径,component 指定要渲染的组件
    <Route path="/xxxx" component={Demo} />
  4. 根组件必须用路由容器包裹

    App 最外层,必须用 <BrowserRouter><HashRouter> 包裹,否则路由无法工作。

    • <BrowserRouter>:使用 history 模式,URL 干净无 #
    • <HashRouter>:使用 hash 模式,URL 带 #,兼容性更好

二、路由组件 vs 一般组件

这是 React Router 里一个很关键的区分,面试常考:

对比维度 一般组件 路由组件
写法 直接写组件标签:<Demo /> 配置在 <Route> 里:<Route path="/demo" component={Demo} />
存放位置 通常放在 src/components(通用可复用组件) 通常放在 src/pages/views(页面级组件)
接收到的 props 只有写标签时传入的属性 自动接收到 3 个固定属性:historylocationmatch

三、路由组件特有的 3 个 props

这是路由组件最核心的特性,也是编程式导航的基础:

1. history(控制路由跳转)

包含 5 个常用方法:

  • go(n):前进 / 后退 n 步(go(1) 前进,go(-1) 后退)
  • goBack():后退一步
  • goForward():前进一步
  • push(path, state):跳转到指定路径,会留下历史记录(可回退)
  • replace(path, state):跳转到指定路径,不留下历史记录(无法回退)

2. location(当前路由信息)

包含当前页面的路径、参数等信息:

  • pathname:当前路径(如 /about
  • search:URL 查询参数(如 ?id=1
  • state:路由跳转时传递的状态数据

3. match(路由匹配信息)

包含路由的匹配结果,常用于获取动态参数:

  • params:动态路由参数(如 /user/:id 中的 id
  • path:路由配置里的 path 值(如 /about
  • url:当前匹配到的完整 URL(如 /about
  1. NavLink可以实现路由链接的高亮,通过activeClassName指定样式名

  2. 标签体内容是一个特殊的标签属性

  3. 通过this.props.children可以获取标签体内容

五、Switch的使用

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

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

  1. public/index.html 中 引入样式时不写 ./ 写 / (常用)
  2. public/index.html 中 引入样式时不写 ./ 写 %PUBLIC_URL% (常用)
  3. 使用HashRouter

七、路由的严格匹配与模糊匹配

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

八、Redirect的使用

  1. 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由

  2. 具体编码:

    jsx 复制代码
     <Switch>  <Route path="/about" component={About}/>  <Route path="/home" component={Home}/>  <Redirect to="/about"/> </Switch>

九、嵌套路由

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

十、向路由组件传递参数

  1. params参数

    • 路由链接(携带参数):<Link to='/demo/test/tom/18'>详情</Link>
    • 注册路由(声明接收):<Route path="/demo/test/:name/:age" component={Test}/>
    • 接收参数:const {name,age} = this.props.match.params
  2. search参数 - 路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'>详情</Link>

    • 注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>
    • 接收参数:const {search} = this.props.location
    • 备注:获取到的search是urlencoded编码字符串,需要借助querystring解析
  3. state参数

    • 路由链接(携带参数):<Link to={``{path:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>
    • 注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>
    • 接收参数: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()

十二、withRouter

jsx 复制代码
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'

class Header extends Component {
  handleGoHome = () => {
    // 被 withRouter 包装后,组件就有了 this.props.history
    this.props.history.push('/home')
  }

  render() {
    return (
      <div>
        <button onClick={this.handleGoHome}>返回首页</button>
      </div>
    )
  }
}

// 导出时用 withRouter 包装组件
export default withRouter(Header)
  1. 关键说明

    • withRouter 是一个高阶组件(HOC),接收一个组件,返回一个新的增强组件。
    • 被包装后的组件,会额外获得 historylocationmatch 三个属性,和路由组件的 props 完全一致。
    • 它是 React Router v5 的 API,在 v6 中已被废弃,改用 useNavigateuseLocation 等 Hooks 实现相同功能。

十三、BrowserRouter和HashRouter的区别

一、最直观区别

  1. BrowserRouter

    路径不带 #

    例:http://xxx/home

  2. HashRouter

    路径带 #

    例:http://xxx/#/home


二、底层原理

  • BrowserRouter :使用 H5 history APIpushStatereplaceState
  • HashRouter :使用 URL 哈希值(hash)# 后面的内容不会发给服务器

三、刷新页面是否会报错

  • BrowserRouter刷新会 404,需要后端配置 nginx 兜底
  • HashRouter刷新不会报错,兼容性最好

四、是否向服务器发送请求

  • BrowserRouter:会
  • HashRouter# 后面的内容不会发给服务器

五、兼容性

  • BrowserRouter:支持 IE10+
  • HashRouter :支持所有浏览器,兼容性最好

六、项目推荐使用

  • 正式项目、追求美观 :用 BrowserRouter
  • 本地开发、不想配后端 :用 HashRouter
  • 前端静态博客、纯前端项目HashRouter 最稳

三十、redux的工作流程

  1. 组件通过 dispatch 发送一个 Action
  2. Reducer 接收 Action,根据类型计算新 State
  3. Store 更新 State
  4. 所有用到该 State 的组件自动重新渲染

三十一、求和案例

1.求和案例_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中状态的改变,一旦发生改变重新渲染

!CAUTION

备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。

2.求和案例_redux完整版

新增文件:
  1. count_action.js 专门用于创建action对象

  2. constant.js 放置防止由于编码疏忽写错action中的type

3.求和案例_redux异步action版

(1). 明确:延迟的动作不想交给组件自身,想交给action

(2). 何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回(非必须)。

(3). 具体编码:

1). yarn add redux-thunk,并配置在store中

2). 创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。

3). 异步任务有结果后,分发一个同步的action去真正操作数据。

(4). 备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。

三十二、对react-reduct的理解

4.求和案例_react-redux基本使用

(1). 明确两个概念:

1). UI组件:不能使用任何redux的api,只负责页面的呈现、交互等。

2). 容器组件:负责和redux通信,将结果交给UI组件。

(2). 如何创建一个容器组件------靠react-redux 的 connect函数 connect(mapStateToProps, mapDispatchToProps)(UI组件)

  • mapStateToProps:映射状态,返回值是一个对象
  • mapDispatchToProps:映射操作状态的方法,返回值是一个对象

(3). 备注1:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入

(4). 备注2:mapDispatchToProps也可以是一个对象

5.求和案例_react-redux优化

(1). 容器组件和UI组件整合一个文件

(2). 无需自己给容器组件传递store,给<App/>包裹一个<Provider store={store}>即可。

(3). 使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。

(4). mapDispatchToProps也可以简单的写成一个对象

(5). 一个组件要和redux"打交道"要经过那几步?

​ (1). 定义好UI组件---不暴露

​ (2). 引入connect生成一个容器组件,并暴露,写法如下:

jsx 复制代码
 connect(      
     state => ({key:value}), // 映射状态    
     {key:xxxxxAction} // 映射操作状态的方法   
 		)(UI组件)

​ (3)在 UI 组件中通过this.props.xxxxxxx读取和操作状态

三十二、添加用户案例和求和案例放在同一个页面的做法

1.reducer文件夹里的代码

jsx 复制代码
import { ADD_PERSON } from '../constant'

// 初始化人的列表
const initState = [{id:'001',name:'tom',age:18}]

export default function personReducer(preState=initState,action){
    const {type,data} = action
    switch (type) {
        case ADD_PERSON: // 若是添加一个人
            return [data,...preState]
        default:
            return preState
    }
}

2.store文件里的代码

3.count组件里的代码

4.person组件里的代码

6.求和案例_react-redux数据共享版

(1). 定义一个Person组件,和Count组件通过redux共享数据。

(2). 为Person组件编写:reducer、action,配置constant常量。

(3). 重点:Person的reducer和Count的Reducer要使用combineReducers进行合并,合并后的总状态是一个对象!!!

(4). 交给store的是总reducer,最后注意在组件中取出状态的时候,记得"取到位"。

三十三、纯函数 知识点


1. 定义

一类特殊的函数:只要输入(实参)相同,就必定得到相同的输出(返回值)

2. 必须遵守的约束

  1. 不得改写参数数据

    不能直接修改传入的参数,必须返回新的数据副本。

  2. 不会产生任何副作用

    不包含网络请求、文件读写、输入输出设备操作等会改变外部状态的行为。

  3. 不能调用不纯的方法

    不能使用

    jsx 复制代码
    Date.now()
    jsx 复制代码
    Math.random()

    等会导致输出不确定的方法。

3. 与 Redux 的关联

Redux 中的 reducer 函数必须是一个纯函数,这样才能保证状态的变化是可预测、可追溯的。

相关推荐
糯米团子7492 小时前
react速通-3
javascript·react.js·前端框架
心连欣2 小时前
从静态页面到动态交互:DOM操作的核心API解析
前端·javascript·api
橙某人2 小时前
SSR页面上的按钮点不了?Nuxt 懒加载水合揭秘💧
前端·vue.js·nuxt.js
PursuitofHappiness2 小时前
2 tree-cli 的使用方法
前端
不做超级小白2 小时前
把图片压小,但不糊:reduceUrImgs项目关键点拆解
前端·开源·node.js
耀耀切克闹灬2 小时前
Eruda 移动端调试工具使用指南
前端
王二端茶倒水2 小时前
现在AI Agent 已经能够代替程序员的工作了,作为一个程序员的我该如何规划以后的职业,请认真思考后给我最靠谱可行的建议。
前端·后端·面试
CyrusCJA2 小时前
毛玻璃效果
前端·css·css3
光影少年2 小时前
Monorepo架构是什么,如何学习Monorepo架构?
前端·学习·架构·前端框架