Jacleklm's Blog

React Router & Redux

2020/01/04

React Router

路由原理

前端路由的本质是监听 URL 的变化,然后匹配路由规则,显示相应的页面,并且无须刷新页面,核心是改变视图的同时不会向后端发出请求。目前前端使用的路由就只有两种实现方式:
Hash 模式和 History 模式

Hash 模式

Vue-router 默认是 hash 模式

  • www.test.com/#/ 就是 Hash URL(有 /#/ 就是),当 # 后面的哈希值发生变化时
  • hash 的修改不会导致浏览器刷新,因为 window.location 处理哈希的改变时不会重新渲染页面,而是当作新页面加到历史记录中
  • 所以我们可以通过 hashchange 事件来监听到 URL 的变化,写一些逻辑进行组件替换实现更新页面的效果;同时浏览器监听到 hash 变化,会把更新历史记录,并且按后退键能回到上个位置
  • *无论哈希值如何变化,服务端接收到的 URL 请求永远是 www.test.com
1
2
3
window.addEventListener('hashchange', () => {
// ... 具体逻辑
})

Hash 模式优点:

  • 简单,兼容性也更好(ie8)
  • 不需要服务器端进行任何设置和开发
  • 除了资源加载和ajax请求以外,不会发起其他请求

缺点:

  • 不太美观
  • 对于部分需要重定向的操作,后端无法获取hash部分内容,导致后台无法取得url中的数据,典型的例子就是微信公众号的oauth验证
  • 服务器端无法准确跟踪前端路由信息
  • 对于需要锚点功能的需求会与目前路由机制冲突

History 模式

History 模式是 HTML5 新推出的功能,主要使用 history.pushState 和 history.replaceState 改变 URL。
通过 History 模式改变 URL 同样不会引起页面的刷新,只会更新浏览器的历史记录

1
2
3
4
// 新增历史记录
history.pushState(stateObject, title, URL) // 前两个参数可以写成null,url必须是同源的
// 替换当前历史记录
history.replaceState(stateObject, title, URL)

当用户做出浏览器动作时,比如点击后退按钮时会触发 popState 事件

1
2
3
4
window.addEventListener('popstate', e => {
// e.state 就是 pushState(stateObject) 中的 stateObject
console.log(e.state)
})

两种模式对比

history 的优点:

  • pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL;而 hash 只可修改 # 后面的部分,因此只能设置与当前 URL 同文档的 URL;
  • pushState() 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash 设置的新值必须与原来不一样才会触发动作将记录添加到栈中;
  • pushState() 通过 stateObject 参数可以添加任意类型的数据到记录中;而 hash 只可添加短字符串;
  • pushState() 可额外设置 title 属性供后续使用

history 的缺点:一是兼容性,二是需要后端的支持,当真正想输入 url 发起 http 请求的时候,eg. 用户手动输入 URL 后回车

  • hash 模式下,仅 # 之前的内容会被包含在请求中,所以对后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误
  • history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.abc.com/book/id。如果后端缺少对 /book/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”

功能

感觉 React 的路由比 Vue 的路由更复杂些
exect 的区别
<Route>,<Router>,<Route>, https://juejin.im/post/5d53e885f265da03bc1270ee 。应该确实是组件被包裹和 withRouter 是等价的
props.history.push()

Redux

工作流程

  • Store:保存数据的地方,你可以把它看成一个容器,整个应用只能有一个 Store。
  • State:Store 对象包含所有数据,如果想得到某个时点的数据,就要对 Store 生成快照,这种时点的数据集合,就叫做 State。
  • Action:State 的变化,会导致 View 的变化。但是,用户接触不到 State,只能接触到 View。所以,State 的变化必须是 View 导致的。Action 就是 View 发出的通知,表示 State 应该要发生变化了。
  • Action Creator:View 要发送多少种消息,就会有多少种 Action。如果都手写,会很麻烦,所以我们定义一个函数来生成 Action,这个函数就叫 Action Creator。
  • Reducer:Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
  • dispatch:是 View 发出 Action 的唯一方法。

然后我们过下整个工作流程:

  • 首先,用户(通过 View)发出 Action,发出方式就用到了 dispatch 方法。
  • 然后,Store 自动调用 Reducer,并且传入两个参数:当前 State 和收到的 Action,Reducer 会返回新的 State
  • State 一旦有变化,Store 就会调用监听函数,来更新 View。

到这儿为止,一次用户交互流程结束。可以看到,在整个流程中数据都是单向流动的,这种方式保证了流程的清晰。

react-redux 工作流程

  • Provider: Provider 的作用是从最外部封装了整个应用,并向 connect 模块传递 store
  • connect: 负责连接 React 和 Redux
    • 获取 state: connect 通过 context 获取 Provider 中的 store,通过 store.getState()获取整个 store tree 上所有 state
    • 包装原组件: 将 state 和 action 通过 props 的方式传入到原组件内部 wrapWithConnect 返回一个 ReactComponent 对象 Connect,Connect 重新 render 外部传入的原组件 WrappedComponent,并把 connect 中传入的 mapStateToProps, mapDispatchToProps 与组件上原有的 props 合并后,通过属性的方式传给 WrappedComponent
    • 监听 store tree 变化: connect 缓存了 store tree 中 state 的状态,通过当前 state 状态和变更前 state 状态进行比较,从而确定是否调用 this.setState()方法触发 Connect 及其子组件的重新渲染

Redux VS Mobx

两者对比:

  • redux 将数据保存在单一的 store 中,mobx 将数据保存在分散的多个 store 中
  • redux 使用 plain object 保存数据,需要手动处理变化后的操作;mobx 适用 observable 保存数据,数据变化后自动处理响应的操作
  • redux 使用不可变状态,这意味着状态是只读的,不能直接去修改它,而是应该返回一个新的状态,同时使用纯函数;mobx 中的状态是可变的,可以直接对其进行修改
  • mobx 相对来说比较简单,在其中有很多的抽象,mobx 更多的使用面向对象的编程思维;redux 会比较复杂,因为其中的函数式编程思想掌握起来不是那么容易,同时需要借助一系列的中间件来处理异步和副作用
  • mobx 中有更多的抽象和封装,调试会比较困难,同时结果也难以预测;而 redux 提供能够进行时间回溯的开发工具,同时其纯函数以及更少的抽象,让调试变得更加的容易

场景辨析:

  • mobx 更适合数据不复杂的应用: mobx 难以调试,很多状态无法回溯,面对复杂度高的应用时,往往力不从心.mobx 适合短平快的项目: mobx 上手简单,样板代码少,可以很大程度上提高开发效率.
  • redux 适合有回溯需求的应用: 比如一个画板应用、一个表格应用,很多时候需要撤销、重做等操作,由于 redux 不可变的特性,天然支持这些操作.

参考资料
高频 React 面试题及详解

CATALOG
  1. 1. React Router
    1. 1.1. 路由原理
      1. 1.1.1. Hash 模式
      2. 1.1.2. History 模式
      3. 1.1.3. 两种模式对比
    2. 1.2. 功能
  2. 2. Redux
    1. 2.1. 工作流程
    2. 2.2. react-redux 工作流程
    3. 2.3. Redux VS Mobx