# Redux、Router、性能优化等

# Flux

Flux 将一个应用分成四个部分:

  • View: 视图层
  • Action(动作):视图层发出的消息(比如 mouseClick)
  • Dispatcher(派发器):用来接收 Actions、执行回调函数
  • Store(数据层):用来存放应用的状态,一旦发生变动,就提醒 Views 要更新页面

Flux

Flux 是一种强制单向数据流的架构模式(MVC)。

它控制派生数据,并使用具有所有数据权限的中心 store 实现多个组件之间的通信。整个应用中的数据更新必须只能在此处进行。

Flux 为应用提供稳定性并减少运行时的错误。

# Redux

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。Redux 由以下组件组成:

  • Action – 这是一个用来描述发生了什么事情的对象。
  • Reducer – 这是一个确定状态将如何变化的地方。
  • Store – 整个程序的状态/对象树保存在 Store 中。
  • View – 只显示 Store 提供的数据。

Redux 数据流动

Redux 遵循的三个原则:

单一事实来源:整个应用的状态存储在单个 store 中的对象/状态树里。单一状态树可以更容易地跟踪随时间的变化,并调试或检查应用程序。

状态是只读的:改变状态的唯一方法是去触发一个动作。动作是描述变化的普通 JS 对象。就像 state 是数据的最小表示一样,该操作是对数据更改的最小表示。

使用纯函数进行更改:为了指定状态树如何通过操作进行转换,你需要纯函数。纯函数是那些返回值仅取决于其参数值的函数。

// Reducer 函数必须是“纯”的 —— 不能修改它的参数,也不能有副作用
const reducer = function(state = 0, action) {
  // 每一次调用 dispatch 最终都会调用 reducer!

  // State 是只读的,唯一修改它的方式是 actions
  switch (action.type) {
    case "INCREMENT":
      return state + 1;
    case "DECREMENT":
      return state - 1;
    default:
      return state;
  }
};

const store = Redux.createStore(reducer);

const unsubscribe = store.subscribe(() => console.log(store.getState()));

// 更新的唯一方式:dispatch(action) -> reducer -> new state。
store.dispatch({
  type: "INCREMENT",
});

store.dispatch({
  type: "INCREMENT",
});

// 停止监听 state 更新
unsubscribe();

简要描述:

  • redux 是的诞生是为了给 React 应用提供「可预测化的状态管理」机制。
  • Redux 会将整个应用状态(其实也就是数据)存储到到一个地方,称为 store
  • 这个 store 里面保存一棵状态树(state tree)
  • 组件改变 state 的唯一方法是通过调用 store 的 dispatch 方法,触发一个 action,这个 action 被对应的 reducer 处理,于是 state 完成更新
  • 组件可以派发(dispatch)行为(action)给 store,而不是直接通知其它组件
  • 其它组件可以通过订阅 store 中的状态(state)来刷新自己的视图

Demo 演示:

import { createStore } from "redux";

/* 创建reducer
 ** 可以使用单独的一个reducer,也可以将多个reducer合并为一个reducer,即:combineReducers()
 ** action发出命令后将state放入reucer加工函数中,返回新的state,对state进行加工处理
 */
const reducer = (state = { counter: 0 }, action) => {
  switch (action.type) {
    case "INCREASE":
      return { counter: state.counter + 1 };
    case "DECREASE":
      return { counter: state.counter - 1 };
    default:
      return state;
  }
};

/* 创建action
 ** 用户是接触不到state的,只能有view触发,所以,这个action可以理解为指令,需要发出多少动作就有多少指令
 ** action是一个对象,必须有一个叫type的参数,定义action类型
 */
const actions = {
  increase: () => ({ type: "INCREASE" }),
  decrease: () => ({ type: "DECREASE" }),
};

/* 创建的store,使用createStore方法
 ** store 可以理解为有多个加工机器的总工厂
 ** 提供subscribe,dispatch,getState这些方法。
 */
const store = createStore(reducer);

// 订阅
const unsubscribe = store.subscribe(() => console.log(store.getState()));

// 发起一系列 action
store.dispatch(actions.increase()); // {counter: 1}
store.dispatch(actions.increase()); // {counter: 2}
store.dispatch(actions.increase()); // {counter: 3}
store.dispatch(actions.decrease()); // {counter: 2}

// 停止监听 state 更新
unsubscribe();

Redux 与 Flux 有何不同?

Flux Redux
1. Store 包含状态和更改逻辑 1. Store 和更改逻辑是分开的
2. 有多个 Store 2. 只有一个 Store
3. 所有 Store 都互不影响且是平级的 3. 带有分层 reducer 的单一 Store
4. 有单一调度器 4. 没有调度器的概念
5. React 组件订阅 store 5. 容器组件是有联系的
6. 状态是可变 6. 状态是不可改变的

Redux 的优点如下:

  • 结果的可预测性 - 由于总是存在一个真实来源,即 store ,因此不存在如何将当前状态与动作和应用的其他部分同步的问题。
  • 可维护性 - 代码变得更容易维护,具有可预测的结果和严格的结构。
  • 服务器端渲染 - 你只需将服务器上创建的 store 传到客户端即可。这对初始渲染非常有用,并且可以优化应用性能,从而提供更好的用户体验。
  • 开发人员工具 - 从操作到状态更改,开发人员可以实时跟踪应用中发生的所有事情。
  • 社区和生态系统 - Redux 背后有一个巨大的社区,这使得它更加迷人。一个由才华横溢的人组成的大型社区为库的改进做出了贡献,并开发了各种应用。
  • 易于测试 - Redux 的代码主要是小巧、纯粹和独立的功能。这使代码可测试且独立。 组织 - Redux 准确地说明了代码的组织方式,这使得代码在团队使用时更加一致和简单。

关于 Redux 的资料:

# React-Redux

React Redux 将组件区分为 容器组件 和 UI 组件:

  • 前者会处理逻辑
  • 后者只负责显示和交互,内部不处理逻辑,状态完全由外部掌控

两个核心:

  • Provider 顶层组件,目的是让所有组件都能够访问到 Redux 中的数据

  • connect:connect(mapStateToProps, mapDispatchToProps)(MyComponent)

    • mapStateToProps - 把 Redux 中的数据映射到 React 中的 props 中去
    • mapDispatchToProps - 把各种 dispatch 变成了 props 让你可以直接使用

DEMO:实现计数器,完整 Demo 可以看这里

学习资料:

# Redux 中间件

我们使用 redux-thunk 在 React 中调用 API。因为 reduce 是纯函数,所以没有副作用,比如调用 API。

因此,我们必须使用 redux-thunk 从 Action creators 那里进行 API 调用。Action creator 派发一个 action,将来自 API 的数据放入 action 的 payload 中。Reducers 接收数据,其余的过程也是相同的。

redux-thunk 是一个中间件。一旦它被引入到项目中,每次派发一个 action 时,都会通过 thunk 传递。如果它是一个函数,它只是等待函数处理并返回响应。如果它不是一个函数,它只是正常处理。

举个栗子:sendEmailAPI 是从组件中调用的函数,它接受一个数据并返回一个函数,其中 dispatch 作为参数。我们使用 redux-thunk 调用 API apiservice,并等待收到响应。一旦接收到响应,我们就使用 payload 派发一个 action。

import apiservice from "../services/apiservice";

export function sendEmail(data) {
  return { type: "SEND_EMAIL", payload: data };
}

export function sendEmailAPI(email) {
  return function(dispatch) {
    return apiservice.callAPI(email).then((data) => {
      dispatch(sendEmail(data));
    });
  };
}

其他中间件:

  • redux-saga
  • redux-thunk
  • redux-promise

学习资料:

# MobX

学习资料:

# React Router

Router 会创建一个 history 对象,history 用来跟踪 URL,当 URL 发生变化时,Router 的后代组件会重新渲染。React Router 中提供的其他组件可以通过 context 获取 history 对象,所以,React Router 中的其他组件必须作为 Router 组件的后代组件使用。但 Router 中只能有唯一的一个子元素。

# 基本组件

React Router 中有三类组件:

  • router 组件(BrowserRouter,HashRouter)
  • route matching 组件(Route,Switch)
  • navigation 组件(Link)

使用 react-router-dom 之前,我们需要在工程路径下安装这个包

npm install react-router-dom

安装完成后,上面所列出的这些组件,我们可以通过 react-router-dom 得到。

import { BrowserRouter, Route, Link } from "react-router-dom";

# Route Rendering Props

<Route> 匹配时所显示的组件,有三种写法:

  • component
  • render
  • children
<Route path="/user/:username" component={User} />;
// convenient inline rendering
<Route path="/foo" render={(props) => (
  <Foo {...props} data={exactProps} />
)} />
<Route path="/foo" children={(props) => (
  <div className={props.match ? 'active' : ''}>
    <Foo />
  </div>
)} />

function User({ match }) {
  return <h1>Hello {match.params.username}!</h1>;
}
  • Link
  • Navlink
  • Redirect
<Link to="/">Home</Link>
// <a href='/'>Home</a>

// location = { pathname: '/react' }
<NavLink to="/react" activeClassName="hurray">
  React
</NavLink>
// <a href='/react' className='hurray'>React</a>

<Route exact path="/" render={() => (
  loggedIn ? (
    <Redirect to="/dashboard"/>
  ) : (
    <PublicHomePage/>
  )
)}/>

# withRoute

不是通过 Route 渲染出来的组件没有 match、location、history 三个属性,但是又想要使用这三个属性,那该怎么办呢,所以可以在外面套一层 Route 组件,从而得到这三个属性,这种做法叫高阶组件。

# 如何提高性能

我们可以通过多种方式提高应用性能,以下这些比较重要:

  • 适当地使用 shouldComponentUpdate 生命周期方法。它避免了子组件的不必要的渲染。如果树中有 100 个组件,则不重新渲染整个组件树来提高应用程序性能。
  • 使用 create-react-app 来构建项目,这会创建整个项目结构,并进行大量优化。
  • 不可变性是提高性能的关键。不要对数据进行修改,而是始终在现有集合的基础上创建新的集合,以保持尽可能少的复制,从而提高性能。
  • 在显示列表或表格时始终使用 Keys,这会让 React 的更新速度更快
  • 代码分离是将代码插入到单独的文件中,只加载模块或部分所需的文件的技术。

# 如何在重新加载页面时保留数据

单页应用程序首先在 DOM 中加载 index.html,然后在用户浏览页面时加载内容,或者从同一 index.html 中的后端 API 获取任何数据。

如果通过点击浏览器中的重新加载按钮重新加载页面 index.html,整个 React 应用程序将重新加载,我们将丢失应用程序的状态。如何保留应用状态?

每当重新加载应用程序时,我们使用浏览器localstorage 来保存应用程序的状态。我们将整个存储数据保存在 localstorage 中,每当有页面刷新或重新加载时,我们从 localstorage 加载状态。

# React 性能优化

# React 源码解析

# 学习资料

# React

# Redux Mbox

# Router

Last Updated: 5/22/2020, 5:01:49 PM