本文复制了官网许多概念,稍加整合知识点使之变得更合理完整,以个人的认为比较好的学习路线循序渐进各个知识点,额外也会补充些许知识点。大家若有不解,还请多参阅官网。

本文示例背景(最常见的 Web 类示例): TodoList = Todo list + Add todo button + Show / Hide List

了解Redux

Redux 是 JavaScript 应用程序的状态容器,提供可预测的状态管理。

Redux产生缘由

随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态)。 这些 state 可能包括服务器响应、缓存数据、本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。

管理不断变化的 state 非常困难。如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,依次地,可能会引起另一个 view 的变化。直至你搞不清楚到底发生了什么。state 在什么时候,由于什么原因,如何变化已然不受控制。 当系统变得错综复杂的时候,想重现问题或者添加新功能就会变得举步维艰。

这里的复杂性很大程度上来自于:我们总是将两个难以理清的概念混淆在一起:变化和异步。如果把二者分开,能做的很好,但混到一起,就变得一团糟。

React中的State

一些库如 React 试图在视图层禁止异步和直接操作 DOM 来解决这个问题。美中不足的是,React 依旧把处理 state 中数据的问题留给了开发者,需要开发者自己setState()管理组件的状态。Redux就是用来对这些组件进行状态管理的。

现实需求

典型的Web应用程序通常由共享数据的多个UI组件组成。通常,多个组件的任务是负责展示同一对象的不同属性。这个对象表示可随时更改的状态。在多个组件之间保持状态的一致性会是一场噩梦,特别是如果有多个通道用于更新同一个对象。

举个🌰,一个带有购物车的网站。在顶部,我们用一个UI组件显示购物车中的商品数量。我们还可以用另一个UI组件,显示购物车中商品的总价。如果用户点击添加到购物车按钮,则这两个组件应立即更新当前的数据。如果用户从购物车中删除商品、更改数目、使用优惠券或者更改送货地点,则相关的UI组件都应该更新出正确的信息。
可以看到,随着功能范围的扩大,一个简单的购物车将会很难保持数据同步。

Redux使用场景

适用场景

多交互、多数据源。

  • 用户的使用方式复杂;
  • 不同身份的用户有不同的使用方式(比如普通用户和管理员);
  • 多个用户之间可以协作;
  • 与服务器大量交互,或者使用了WebSocket;
  • View需要从多个来源获取数据。

不适用场景

以下应用没有必要使用Redux,否则反而会增加项目的复杂程度,得不偿失。

  • UI层非常简单;
  • 用户使用方式非常简单;
  • 页面之间没有协作;
  • 与服务器没有大量交互。

Redux数据模型图

Redux基于简化版本的Flux框架,Flux是Facebook开发的一个框架。在标准的MVC框架中,数据可以在UI组件和存储之间双向流动,而Redux严格限制了数据只能在一个方向上流动。

Flux

起初由于Facebook公司业务庞大,代码也随着业务的复杂和增多变得非常庞大,代码由此变得脆弱及不可预测,尤其对于刚接触新业务的开发者来说,这是非常严重的问题。于是Facebook的工程师认为MVC架构无法满足他们对业务拓展的需求,于是开发了Flux。Flux是基于Dispatcher的前端应用架构模式,其名字来自拉丁文的Flow。

Flux的核心思想是利用单向数据流和逻辑单向流来应对MVC架构中出现状态混乱的问题。👉MVC构架模式的缺点Flux适合复杂项目,且组件数据相互共享的应用。

Flux模型图

在这里插入图片描述
Flux由3部分组成:Dispatcher、Store和View。其中,Dispatcher(分发器)用于分发事件;Store用于存储应用状态,同时响应事件并更新数据;View表示视图层,订阅来自Store的数据,渲染到页面。

Flux的核心是单向数据流,其运作方式是:Action -> Dispatcher -> Store -> View

整个流程如下:
(1)创建Action(提供给Dispatcher)。
(2)用户在View层交互(比如单击事件)去触发Action。
(3)Dispatcher收到Action,要求Store进行相应的更新。
(4)Store更新,通知View去更新。
(5)View收到通知,更新页面。

从上面的流程可以得知,Flux中的数据流向是单向的,不会发生双向流动的情况,从而保证了数据流向的清晰脉络。而MVC和MVVM中数据的流向是可以双向的,状态在Model和View之间来回“震荡”,很难追踪和预测数据的变化。

Flux的缺陷主要体现在增加了项目的代码量,使用Flux会让项目带入大量的概念和文件;单元测试难以进行,在Flux中,组件依赖Store等其他依赖,使得编写单元测试非常复杂。

Redux

Redux是Flux衍生的诸多“变种”中最出名的。Redux是一个“可预测的状态容器”,而实质也是Flux里面“单向数据流”的思想,但它充分利用函数式的特性,让整个实现更加优雅纯粹,使用起来也更简单。Redux是超越Flux的一次进化。

在这里插入图片描述
在Redux中,所有的数据(比如state)都是保存在store的容器中,一个应用也只能有一个store容器。store本质是一个状态树,保存了所有对象的状态。任何UI组件都可以直接通过store访问特定对象的状态。

要想更改对象的状态,需要分发一个action– (store.dispatch(action))。分发在这里意味着将可执行信息发送到store。当一个store接收到一个action,它将把这个action代理给相关的reducer。之后根 reducer 把多个子 reducer 输出合并成一个单一的 state 树,最终 store 保存了根 reducer 返回的完整 state 树。(目前看不明白没关系,下文Redux数据流会讲解)

Redux三大原则

单一数据源

整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。

console.log(store.getState())

优点:便于开发和调试

State 是只读的

唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。

store.dispatch({type: 'COMPLETE_TODO',index: 1
})store.dispatch({type: 'SET_VISIBILITY_FILTER',filter: 'SHOW_COMPLETED'
})

这样确保了视图和网络请求都不能直接修改 state,相反它们只能表达想要修改的意图。因为所有的修改都被集中化处理,且严格按照一个接一个的顺序执行,因此不用担心竞态条件(race condition)的出现。 Action 就是普通对象而已,因此它们可以被日志打印、序列化、储存、后期调试或测试时回放出来。

使用纯函数来执行修改

reducers用来描述 action 如何改变 state tree

function visibilityFilter(state = 'SHOW_ALL', action) {switch (action.type) {case 'SET_VISIBILITY_FILTER':return action.filterdefault:return state}
}

Reducer 只是一些纯函数,它接收先前的 stateaction,并返回新的 state

Redux核心概念

State

State (也称为 state tree) ,它表示了 Redux 应用的全部状态,通常为一个多层嵌套的对象,由 store 管理且由 getState() 方法获得。

type State = any  //type指的是变量声明的关键字 var/let/const any指的是任何数据类型

约定俗成,顶层 state 或为一个对象,或像 Map 那样的键-值集合,也可以是任意的数据类型。然而你应尽可能确保 state 可以被序列化,而且不要把什么数据都放进去,导致无法轻松地把 state 转换成 JSON。

为了保持状态的一致性,state对象的代码不能随意修改,只能通过action(行为活动)来更新state中的数据。

实例

例如,todo 应用(记事便签)的 state 可能长这样:

{todos: [{text: 'Eat food',completed: true}, {text: 'Exercise',completed: false}],visibilityFilter: 'SHOW_COMPLETED'
}

todos对象表示需要做的事情(text)及其完成状态(completed);visibilityFilter属性则表示需要显示何种状态的便签事项。

Action

Action 就是一个普通 JavaScript 对象用来描述发生了什么。表示即将要改变 state 的意图。

作用

Action 是用来更新 state 的数据的。
Action 是将数据放入 store 的唯一途径。

强制使用 action 来描述所有变化带来的好处是可以清晰地知道应用中到底发生了什么。如果一些东西改变了,就可以知道为什么变。action 就像是描述发生了什么的指示器。

Action 签名

type Action = Object

约定俗成,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作。多数情况下,type 会被定义成字符串常量。除了 type 之外,action 对象的结构其实完全取决于你自己。

{ type: 'ADD_TODO', text: 'Go to swimming pool' }
{ type: 'TOGGLE_TODO', index: 1 }
{ type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }

当实际项目通常对外隐藏实际变量

export const ADD_TODO = 'ADD_TODO'export function addTodo(text) {return { type: ADD_TODO, text }
}

Action 创建函数

Action 创建函数 就是生成 action 的方法。在 Redux 中的 action 创建函数只是简单的返回一个 action,这样做将使 action 创建函数更容易被移植和测试。

function addTodo(text) {return {type: ADD_TODO,text}
}

之后便可以直接通过store.dispatch(addTodo(text))发起一次dispatch 过程,也减少许多代码量。

Reducer

Reducer是用来把 action 和 state 串起来的纯函数。

纯函数:一个函数的返回结果只依赖于它的参数,相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,这个函数就是纯函数。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。

副作用操作:数据获取(发送网络请求),设置订阅以及手动更改 React 组件中的 DOM等。

Reducer 签名

Reducer (也称为 reducing function) 函数接受两个参数:分别是当前的 state 树和要处理的 action,返回新的 state 树。

type Reducer<S, A> = (state: S, action: A) => S
  • 禁止修改传入参数;

  • 禁止执行有副作用的操作,如 API 请求和路由跳转;

  • 禁止调用非纯函数,如 Date.now()Math.random()

  • 在 default 情况下返回旧的 state。遇到未知的 action 时,一定要返回旧的 state

返回一个新对象,常用的方法是Object.assign({},{a:'x'}),也可以使用对象展开符{ ...state, ...newState }等等

state 初始状态

Redux 首次执行时,stateundefined,此时我们可借机设置并返回应用的初始 state

import { VisibilityFilters } from './actions'const initialState = {visibilityFilter: VisibilityFilters.SHOW_ALL,todos: []
}function todoApp(state, action) {if (typeof state === 'undefined') {return initialState}// 这里暂不处理任何 action,// 仅返回传入的 state。return state
}

也可以使用ES6 参数默认值语法:

function todoApp(state = initialState, action) {// 这里暂不处理任何 action,// 仅返回传入的 state。return state
}

拆分与合并

对于大的应用来说,不大可能仅仅只写一个这样的函数,所以我们编写很多小函数来分别管理 state 的一部分,之后再将各个小函数绑在一起。

注意每个 reducer 只负责管理全局 state 中它负责的一部分。每个 reducer 的 state 参数都不同,分别对应它管理的那部分 state 数据。

function visibilityFilter(state = SHOW_ALL, action) {switch (action.type) {case SET_VISIBILITY_FILTER:return action.filterdefault:return state}
}function todos(state = [], action) {switch (action.type) {case ADD_TODO:return Object.assign({}, state, {todos: [...state.todos,{text: action.text,completed: false}]})default:return state}
}

合并这两个 reducer,进而来管理整个应用的 state

export default function todoApp(state = {}, action) {return {visibilityFilter: visibilityFilter(state.visibilityFilter, action),todos: todos(state.todos, action)}
}

Redux 提供了 combineReducers() 工具类合并reducer

import { combineReducers } from 'redux'const todoApp = combineReducers({visibilityFilter,todos
})

Store

store 维持着应用的 state tree 对象。 Redux 应用只有一个单一的 store。使用根reducer来构建store而不是创建多个 store

store用来将action,state,reducer联系到一起对象:

  • 维持应用的 state
  • 提供 getState() 方法获取 state;
  • 提供 dispatch(action) 方法更新 state;
  • 通过 subscribe(listener) 注册监听器;
  • 通过 subscribe(listener) 返回的函数注销监听器。
type Store = {dispatch: DispatchgetState: () => Statesubscribe: (listener: () => void) => () => voidreplaceReducer: (reducer: Reducer) => void
}
  • replaceReducer(nextReducer) 可用于热重载和代码分割。通常不需要用到这个 API。

注册和注销监听器

subscribe当store中数据发生变化就会触发。

// subscribe() 返回一个函数用来注销监听器
const unsubscribe = store.subscribe(() => console.log(store.getState()))// 停止监听 state 更新
unsubscribe()

Store Creator

创建一个 Redux store 来以存放应用中所有的 state

type StoreCreator = (reducer: Reducer, initialState: ?State) => Store

[preloadedState] (any): 初始时的 state。

还有一种写法:

type StoreCreator =createStore(reducer, [preloadedState], enhancer)

enhancer (Function): Store enhancer 是一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator。一般用于增加Store的功能,但不影响原有效果,具体看下文中间件部分。

import { createStore } from 'redux'function todos(state = [], action) {switch (action.type) {case 'ADD_TODO':return state.concat([action.text])default:return state}
}let store = createStore(todos, ['Use Redux'])store.dispatch({type: 'ADD_TODO',text: 'Read the docs'
})console.log(store.getState())
// [ 'Use Redux', 'Read the docs' ]

拓展:compose(…functions)

当需要把多个 store 增强器 依次执行的时候,需要用到它。从右到左来组合多个函数。

📌参数:
(arguments): 需要合成的多个函数。预计每个函数都接收一个参数。它的返回值将作为一个参数提供给它左边的函数,以此类推。例外是最右边的参数可以接受多个参数,因为它将为由此产生的函数提供签名。

compose(funcA, funcB, funcC) 实则 compose(funcA(funcB(funcC()))))

📌返回值

(Function): 从右到左把接收到的函数合成后的最终函数。

📌示例

import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import DevTools from './containers/DevTools'
import reducer from '../reducers'const store = createStore(reducer,compose(applyMiddleware(thunk),DevTools.instrument())
)

Hello Redux

虽然官方有推荐使用脚手架构建redux项目,但还是想试试自己手动创建的效果。

手动创建

1.初始化项目

mkdir first-redux
cd first-redux
npm init -y

2.安装redux

npm install redux

当然还有配套的软件包,暂时没用,先不装

npm install react-redux
npm install --save-dev redux-devtools

3.编码

//index.mjs
import { createStore } from "redux";//Reducer
function counter(state = 0, action) {switch (action.type) {case "INCREMENT":return state + 1;case "DECREMENT":return state - 1;default:return state;}
}//Store
const store=createStore(counter);//Action Listener [Optional]
store.subscribe(()=>console.log(store.getState()))//Dispatch Action
store.dispatch({type:'INCREMENT'})
store.dispatch({type:'INCREMENT'})
store.dispatch({type:'DECREMENT'})

代码说明:

  1. 从redux包引入createStore()方法
  2. 创建一个名为counter的纯函数作为Reducer。
  3. 创建一个名为store的Redux存储区,它只能使用Reducer作为参数来构造。
  4. 创建一个变化监听器。每当 dispatch action 的时候就会执行,观测状态树数据变化。
  5. 分发Action,同时可观测状态树数据变化

4.编译看结果

node index.mjs

在这里插入图片描述

脚手架集成搭建

1.安装项目

基于 React 和 Redux 创建一个新的应用程序的推荐方式是使用针对 Create React App 的 官方 Redux+JS 模板

npx create-react-app my-app --template redux

2.启动项目

npm run start

在这里插入图片描述

3.重构项目

目前的阶段,不建议去解读脚手架的源码,因为此时已经结合了react-redux,还是先由简入深。

(1)删除src文件夹中除index.js以外的所有文件。

(2)打开index.js,删除所有代码,键入以下内容:

import { createStore } from "redux";//Reducer
function counter(state = 0, action) {switch (action.type) {case "INCREMENT":return state + 1;case "DECREMENT":return state - 1;default:return state;}
}//Store
const store=createStore(counter);//Action Listener [Optional]
store.subscribe(()=>console.log(store.getState()))//Dispatch Action
store.dispatch({type:'INCREMENT'})
store.dispatch({type:'INCREMENT'})
store.dispatch({type:'DECREMENT'})

(3)启动项目,观察浏览器控制台

npm run start

使用Redux工具调试

1.在浏览器安装Redux Devtools插件
2.在项目中加入redux-devtools-extension包:npm i redux-devtools-extension
3.store引入redux-devtools的enhancer,确保在enhancer数组最后

import { createStore } from "redux";
import { composeWithDevTools } from 'redux-devtools-extension';
//Reducer
function counter(state = 0, action) {switch (action.type) {case "INCREMENT":return state + 1;case "DECREMENT":return state - 1;default:return state;}
}//Store
const store=createStore(counter,composeWithDevTools());//Action Listener [Optional]
//composeWithDevTools将getState()方法置于store.liftedStore对象了
store.subscribe(()=>console.log(store.liftedStore.getState()))//Dispatch Action
store.dispatch({type:'INCREMENT'})
store.dispatch({type:'INCREMENT'})
store.dispatch({type:'DECREMENT'})

4.启动项目,在浏览器右键进入Redux DexTools
在这里插入图片描述
在这里插入图片描述
Redux Devtools很强大。你可以在action, state和diff(方法差异)之间切换。选择左侧面板上的不同action,观察状态树的变化。你还可以通过进度条来播放actions序列。甚至可以通过工具直接分发操作信息。

进一步学习

Redux数据流(Redux 应用中数据的生命周期)

严格的单向数据流是 Redux 架构的设计核心。

Redux 应用中数据的生命周期遵循下面 4 个步骤:

1.派发行动 store.dispatch(action)

Action 就是一个描述“发生了什么”的普通对象。比如:

    { type: 'LIKE_ARTICLE', articleId: 42 }{ type: 'FETCH_USER_SUCCESS', response: { id: 3, name: 'Mary' } }{ type: 'ADD_TODO', text: 'Read the Redux docs.' }

你可以在任何地方调用 store.dispatch(action),包括组件中、XHR 回调中、甚至定时器中。

2.Redux store 调用相关的 reducer 处理 action

Store 会把两个参数传入 reducer: 当前的 state 树和 action。例如,在这个 todo 应用中,根 reducer 可能接收这样的数据:

// 当前应用的 state(todos 列表和选中的过滤器)
let previousState = {visibleTodoFilter: 'SHOW_ALL',todos: [{text: 'Read the docs.',complete: false}]
}// 将要执行的 action(添加一个 todo)
let action = {type: 'ADD_TODO',text: 'Understand the flow.'
}// reducer 返回处理后的应用状态
let nextState = todoApp(previousState, action)

3.根 reducer 应该把多个子 reducer 输出合并成一个单一的 state 树

Redux 原生提供combineReducers()辅助函数,来把根 reducer 拆分成多个函数,用于分别处理 state 树的一个分支。

下面演示 combineReducers() 如何使用。假如你有两个 reducer:一个是 todo 列表,另一个是当前选择的过滤器设置:

function todos(state = [], action) {// 省略处理逻辑...return nextState
}function visibleTodoFilter(state = 'SHOW_ALL', action) {// 省略处理逻辑...return nextState
}let todoApp = combineReducers({todos,visibleTodoFilter
})

当你触发 action 后,combineReducers 返回的 todoApp 会负责调用两个 reducer

let nextTodos = todos(state.todos, action)
let nextVisibleTodoFilter = visibleTodoFilter(state.visibleTodoFilter, action)

然后会把两个结果集合并成一个 state 树:

return {todos: nextTodos,visibleTodoFilter: nextVisibleTodoFilter
}

4.Redux store 保存了根 reducer 返回的完整 state 树

这个新的树就是应用的下一个 state!所有订阅 store.subscribe(listener) 的监听器都将被调用;监听器里可以调用 store.getState() 获得当前 state

现在,可以应用新的 state 来更新 UI。如果你使用了 React Redux 这类的绑定库,这时就应该调用 component.setState(newState) 来更新。

dispatch function

dispatch function是一个接收 action 或者异步 action的函数,该函数要么往 store 分发一个或多个 action,要么不分发任何 action

函数签名

type BaseDispatch = (a: Action) => Action
type Dispatch = (a: Action | AsyncAction) => any

最常见的dispatch function

let next = store.dispatch;//next 此时就是一个dispatch function

普通的dispatch function 和 在中间件中的Base dispatch function

Base dispatch function 总是同步地把 action 与上一次从 store 返回的 state 发往 reducer,然后计算出新的 state

Base dispatch function In Middleware ,事实上,Middleware 封装了 base dispatch function,允许 dispatch function 处理 action 之外的异步 action 。 Middleware 可以改变、延迟、忽略 action 或异步 action ,也可以在传递给下一个 middleware 之前对它们进行加工处理。

Middleware 中间件

Middleware 通过改写 store 的 dispatch 方法来扩展 Redux 。
Middleware 是一个组合 dispatch function 的高阶函数,返回一个新的 dispatch function。
Redux middleware 本身的实现就是一个 store enhancer。

Middleware使用场景

Middleware 最常见的使用场景是无需引用大量代码或依赖类似 Rx 的第三方库实现异步 actions。它可以让你像 dispatch 一般的 actions 那样 dispatch 异步 actions。处理副作用或异步操作,一般会用到 redux-thunk 和 redux-saga 这两个中间件之一。

Middleware 利用复合函数使其可以组合其他函数,可用于记录 action 日志、调用副作用行为,或将异步的 API 调用变为一组同步的 action。

Middleware执行时机

Middleware提供的是位于 action 被发起之后,到达 reducer 之前的扩展点。Redux 应用中数据的生命周期的第一步之后第二步之前的时机。
在这里插入图片描述

Middleware链式组合

Middleware 最优秀的特性就是可以被链式组合。多个 middleware 可以被组合到一起使用,形成 middleware 链。其中,每个 middleware 都不需要关心链中它前后的 middleware 的任何信息。你可以在一个项目中使用多个独立的第三方 Middleware。

applyMiddleware(...middleware)

…middleware (arguments): 每个 middleware接受 Store 的 dispatch 和 getState 函数作为命名参数,并返回一个函数。该函数会被传入被称为 next 的下一个middleware 的 dispatch 方法,并返回一个接收 action 的新函数,这个函数可以直接调用 next(action),或者在其他需要的时刻调用,甚至根本不去调用它。调用链中最后一个 middleware 会接受真实的 store 的dispatch 方法作为 next 参数,并借此结束调用链。所以,middleware 的函数签名是 ({ getState, dispatch }) => next => action。

type Middleware = ({ getState, dispatch }) => next => action。

{ getState, dispatch }其实就是 store

手写一个记录日志的Middleware

记录日志:当我们的应用中每一个 action 被发起以及每次新的 state 被计算完成时都将它们记录下来,岂不是很好?当程序出现问题时,我们可以通过查阅日志找出是哪个 action 导致了 state 不正确。

能记录日志的方式有很多种,为何最终选取以下这种形式的函数(Middleware)呢?因为目前而言这种最完美。。至于如何一步步递进到Middleware这种形式的,大家可以看下原文。

const logger = store => next => action => {console.log('dispatching', action)let result = next(action)console.log('next state', store.getState())return result
}

是不是有点懵?这其实是经过ES6 箭头函数 柯里化 后的样子,原来是这样的:

function logger(store) {return function wrapDispatchToAddLogging(next) {return function dispatchAndLog(action) {console.log('dispatching', action)let result = next(action)console.log('next state', store.getState())return result}}
}

调用中间将 applyMiddleware

import { createStore, combineReducers, applyMiddleware } from 'redux'function logger(store) {
...
}const todoApp = combineReducers(reducers)
const store = createStore(todoApp,// applyMiddleware() 告诉 createStore() 如何处理中间件applyMiddleware(logger)
)

现在任何被发送到 store 的 action 都会经过 logger 中间件 。

除了 applyMiddleware,如果你还用了其它 store enhancer,一定要把 applyMiddleware 放到组合链的前面,因为 middleware 可能会包含异步操作。比如,它应该在 redux-devtools 前面,否则 DevTools 就看不到 Promise middleware 里 dispatchaction 了。(上文compose(…functions)部分)

第三方Middleware

官网示例: 使用 Thunk Middleware 来做异步 Action

redux-logger是官方的日志中间件

//伪代码
import { createLogger } from 'redux-logger'
const loggerMiddleware = createLogger()
const store = createStore(rootReducer,applyMiddleware(loggerMiddleware // 一个很便捷的 middleware,用来打印 action 日志)
)

异步 Action

Redux store 仅支持同步数据流。同步操作,每当 dispatch action 时,state 会被立即更新。事实上,我们更多的是需要通过发起网络请求API来改变state

使用 redux-thunk等中间件可以帮助在 Redux 应用中实现异步性。可以将 thunk 看做 store 的 dispatch() 方法的封装器;我们可以使用 thunk action creator 派遣函数或 Promise,而不是返回 action 对象。

Action 状态

当调用异步 API 时,有两个非常关键的时刻:发起请求的时刻,和接收到响应的时刻(也可能是超时)。

这两个时刻都可能会更改应用的 state;一般情况下,每个 API 请求都需要 dispatch 至少三种 action:

  • 一种通知 reducer 请求开始的 action
    对于这种 actionreducer 可能会切换一下 state 中的 isFetching 标记。以此来告诉 UI 来显示加载界面。

  • 一种通知 reducer 请求成功的 action
    对于这种 actionreducer 可能会把接收到的新数据合并到 state 中,并重置 isFetching。UI 则会隐藏加载界面,并显示接收到的数据。

  • 一种通知 reducer 请求失败的 action
    对于这种 actionreducer 可能会重置 isFetching。另外,有些 reducer 会保存这些失败信息,并在 UI 里显示出来。

为了区分这三种 action,可能在 action 里添加一个专门的 status 字段作为标记位:

{ type: 'FETCH_POSTS' }
{ type: 'FETCH_POSTS', status: 'error', error: 'Oops' }
{ type: 'FETCH_POSTS', status: 'success', response: { ... } }

又或者为它们定义不同的 type:

{ type: 'FETCH_POSTS_REQUEST' }
{ type: 'FETCH_POSTS_FAILURE', error: 'Oops' }
{ type: 'FETCH_POSTS_SUCCESS', response: { ... } }

实例

起始页引入redux-thunk

//index.js 
//伪代码
import thunkMiddleware from 'redux-thunk'const store = createStore(rootReducer,applyMiddleware(thunkMiddleware, // 允许我们使用 封装过的dispatch() 函数)
)

之后我们调用dispatch()实际上就是封装过的dispatch()

在这里插入图片描述
代码图来自:tonytony

React+Redux

Redux是不依赖于React而存在的,它本身能支持React、Angular、Ember和jQuery等。要让其在React上运行,就得让二者绑定起来去建立连接。于是就有了react-redux,它能将Redux绑定到React上。

尽管如此,Redux 还是和 React 和 Deku 这类库搭配起来用最好,因为这类库允许你以 state 函数的形式来描述界面,Redux 通过 action 的形式来发起 state 变化。

安装 React 绑定库:npm install react-redux

实验环境

建议直接先通过React+Redux官方模板建立项目来练习,就不用引入一堆的库包,也免去许多配置,降低学习成本:

npx create-react-app my-app --template redux

然后将src目录下除index.js文件外的所有文件删除,下文案例以此结构展开编码。

启动项目命令:npm run start

Provider

react-redux 提供了两个重要的对象,Provider 和 connect,前者使 React 组件可被连接(connectable),后者把 React 组件和 Redux 的 store 真正连接起来。

先在最顶层创建一个Provider组件,用于将所有的React组件包裹起来,从而使React的所有组件都成为Provider的子组件。然后将创建好的Store作为Provider的属性传递给Provider。

<Provider store> 使组件层级中的 connect() 方法都能够获得 Redux store。

//index.js
import React from "react";
import { render } from 'react-dom';
import { createStore } from 'redux';
import { Provider } from "react-redux";
// 导入reducer
import reducer from './reducers';
// store 创建
const store = createStore(reducer
)
render(<Provider store={store}>                        // Provider需要包裹在整个应用组件的外部<App />                                       // React组件</Provider>,document.getElementById('root')

上述代码<App /> 组件尚未定义,大家可自行定义,定义后运行项目,也看不到什么效果,因为Provider 需要结合connect来使用的。

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

connect用来连接 React 组件与 Redux store,使得 React 组件能用上Redux 的 store 数据。

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])//参数都是可选的

连接操作不会改变原来的组件类,而是返回一个新的已与 Redux store 连接的组件类。怎么理解?

const Comp  = connect(...args);//`...args`是伪代码,表示各个`connect`的参数
console.log(Comp);//function wrapWithConnect(WrappedComponent) {...}

执行connect()返回的是一个与Redux store 连接的函数,WrappedComponent 表示传递到 connect() 函数的原始组件类,再看:

const MyComp=()=>{return(<div>Hello</div>)
}
const Comp  = connect(..args)(MyComp);
console.log(Comp);

看控制台属性:
在这里插入图片描述
可见connect(..args)(WrappedComponent) 返回来的才是我们要的绑定了Redux store的组件。接下来我们看下connect方法的几个参数:

mapStateToProps

mapStateToProps,见名知意:将 state 映射(map)到 props 中,将Redux store中的 state tree 附加到 React 组件 props 中 。

如果connect()定义了mapStateToProps参数,组件将会监听 Redux store 的变化。任何时候,只要 Redux store 发生改变,mapStateToProps 函数就会被调用。

[mapStateToProps(state, [ownProps]): stateProps] (Function)

mapStateToProps函数有两个参数,state(也就是Redux Store)和ownProps(组件自身的props),其中ownProps参数是可选的,该函数必须返回一个纯对象,这个对象会与组件的 props 合并然后下发给React组件使用。如果指定 ownProps,只要组件接收到新的 propsmapStateToProps 也会被调用。


单看概念感觉有点明白,但是却无从着手,下面做个简单的案例:

通过添加按钮触发添加Action,并在组件中实时回显state的数据变化。我们先看下大致效果图:
在这里插入图片描述

为此,我们需要建立四个文件:
在这里插入图片描述

1.actions.js

export const ADD_TODO='ADD_TODO';export function addTodo(text){return{type:ADD_TODO,text}
}

2.reducers.js

import { ADD_TODO } from "./actions";const initialState = {todos: [{text: "Eat food",completed: true,},{text: "Exercise",completed: false,},],
};export default function toDo(preloadStates = initialState, action) {switch (action.type) {case ADD_TODO:return Object.assign({}, preloadStates, {todos: [...preloadStates.todos,{ text: action.text, completed: false },],});default:return preloadStates;}
}

3.todolist.js

import React from "react";
import { connect } from "react-redux";const mapStateToProps = (state, ownProps) => {console.log({ state, ownProps });return state;
};const ToDoList = (state) => {console.log({ state });return (<><h3>Redux Store</h3>{state.todos.map((todo, index) => {return (<p key={index}>{index} {todo.text} {todo.completed ? "完成" : "未完成"}</p>);})}<h3>Component Props</h3><p>{state.name}</p></>);
};export default connect(mapStateToProps)(ToDoList);;

4.index.js

import React from "react";
import { render } from "react-dom";
import { createStore } from "redux";
import { Provider } from "react-redux";
import reducer from "./reducers";
import { addTodo } from "./actions";
import  ToDoList  from "./todolist";const store = createStore(reducer);render(<Provider store={store}><button onClick={()=>{store.dispatch(addTodo("Run"+Math.random().toFixed(3)));}}>模拟改变状态树(添加)</button><ToDoList name='hello'></ToDoList></Provider>,document.getElementById("root")
);

在这里插入图片描述

当然,你不必将 state 中的数据原封不动地传入组件,可以根据 state 中的数据,动态地输出组件需要的(最小)属性。

const mapStateToProps = (state, ownProps) => {return ownProps.type>1?state.xx:state.yy
};

mapDispatchToProps

connect 的第二个参数是 mapDispatchToProps,它的功能是,将 dispatch(action)作为 props 绑定到 组件上。mapDispatchToProps方法返回一个对象,这个对象通过 dispatch 函数与 action creator 以某种方式绑定在一起。

如果你省略connect 中这个 mapDispatchToProps 参数,那么dispatch 会默认注入到你的组件 props 中。如果指定了 ownProps,该参数的值会传递到组件的 props,而且只要组件接收到新 props,mapDispatchToProps 也会被调用。

mapDispatchToProps(dispatch, ownProps): dispatchProps

方式一:

//reducers.js
import React from "react";
import { connect } from "react-redux";
import { finishTodo } from "./actions";const mapDispatchToProps=(dispatch,ownProps)=>{return{finish:(text)=>dispatch(finishTodo(text))}
}const ToDoList = (state) => {console.log({ state });return (<><h3>Redux Store</h3>{state.todos.map((todo, index) => {return (<p key={index}>{index} {todo.text} {todo.completed ? "完成" : "未完成"} <button onClick={()=>state.finish(todo.text)}>点击完成</button></p>);})}<h3>Component Props</h3><p>{state.name}</p></>);
};export default connect(a=>a,mapDispatchToProps)(ToDoList);

由于 mapDispatchToProps 方法返回了具有finish属性的对象,这个属性也会成为 ToDoList 的 props

如上所示,调用 finishTodo() 只能得到一个 action 对象 {type:'FINISH_TODO'},要触发这个 action 必须在 store 上调用 dispatch 方法。diapatch 正是 mapDispatchToProps 的第一个参数。但是,为了不让 ToDoList 组件感知到 dispatch 的存在,我们需要将 finishTodo函数包装一下,使之成为直接可被调用的函数(即,调用该方法就会触发 dispatch)。

方式二

Redux 本身提供了 bindActionCreators 函数,来将 action 包装成直接可被调用的函数。效果和上文一致:

import { bindActionCreators } from "redux";const mapDispatchToProps=(dispatch,ownProps)=>{return bindActionCreators({finish:finishTodo},dispatch)
}

在这里插入图片描述
要想实现上面的效果,还需稍微改动:
1.actions.js

export const ADD_TODO='ADD_TODO';
export const FINISH_TODO='FINISH_TODO';export function addTodo(text){return{type:ADD_TODO,text}
}export function finishTodo(text){return{type:FINISH_TODO,text}
}

2.reducers.js

import { ADD_TODO,FINISH_TODO } from "./actions";...export default function toDo(preloadStates = initialState, action) {...case FINISH_TODO:return Object.assign({},preloadStates,{todos: preloadStates.todos.map(it=>{if(it.text===action.text){return { text: action.text, completed: true };}return it;})})...}
}

mergeProps

筛选特定值作为组件的props,默认是:Object.assign({}, ownProps, stateProps,dispatchProps) 的结果。

如果指定了这个参数,mapStateToProps() 与 mapDispatchToProps() 的执行结果和组件自身的 props将传入到这个回调函数中。该回调函数返回的对象将作为 props 传递到被包装的组件中。你也许可以用这个回调函数,根据组件的 props来筛选部分的 state 数据,或者把 props 中的某个特定变量与 action creator 绑定在一起。如果你省略这个参数,默认情况下返回 Object.assign({}, ownProps, stateProps,dispatchProps) 的结果。

[mergeProps(stateProps, dispatchProps, ownProps): props] (Function)
const mergeProps=(stateProps, dispatchProps, ownProps)=>{return Object.assign({}, ownProps, stateProps,dispatchProps);
}

options

[options] (Object) 如果指定这个参数,可以定制 connector 的行为。

  • [pure = true] (Boolean): 如果为 true,connector 将执行 shouldComponentUpdate 并且浅对比 mergeProps 的结果,避免不必要的更新,前提是当前组件是一个“纯”组件,它不依赖于任何的输入或 state 而只依赖于 props 和 Redux store 的 state。默认值为 true。
  • [withRef = false] (Boolean): 如果为 true,connector 会保存一个对被包装组件实例的引用,该引用通过 getWrappedInstance() 方法获得。默认值为 false。

=================================================================================================

行文至此,如有错误,还望指出;若还可以,多多支持。

=================================================================================================
参考文档:
Redux 中文文档
React+Redux前端开发实战
Redux入门教程(快速上手)
Building a Simple CRUD App with React + Redux

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 双眼皮手术左右不对称怎么修复

    ...

    2024/4/21 11:46:21
  2. React Redux 介绍

    Redux 介绍 文章目录Redux 介绍一.简介二.Redux 三大原则1.单一数据源2.state是只读的3.使用纯函数来执行修改3.1 纯函数三.Redux的组成1.Action1.1 actionType1.2 action Create2.Reducer2.1 combineReducers()3.Store3.1 createStore()四.React中Redux的使用1.react-redux2.P…...

    2024/4/28 4:33:56
  3. 前端技术 | 从Flux到Redux

    上一篇分析了Flux出现的背景和原理&#xff0c;最核心的思想就是“组件化单向数据流”。 但是&#xff0c;Flux在设计上并非完美&#xff0c;具体来说主要存在以下2个不足&#xff1a; 1. 多Store数据依赖 由于Flux采用多Store设计&#xff0c;各个Store之间可能存在数据依赖。…...

    2024/4/21 11:46:19
  4. Redux各知识点介绍

    继 Facebook 提出 Flux 架构来管理 React 数据流后&#xff0c;相关架构开始百花齐放&#xff0c;本文简单分析 React 中管理数据流的方式&#xff0c;以及对 Redux 进行较为仔细的介绍。 React " A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES " 在 React 中…...

    2024/4/28 14:48:08
  5. React:React-redux使用

    一. Redux简介 1. 什么是Redux Redux是一个用于JavaScript状态容器&#xff0c;提供可与预测化的状态管理。 Redux可以让你构建一致化的应用&#xff0c;运用于不同的环境&#xff08;客户端&#xff0c;服务器&#xff0c;原生应用&#xff09;&#xff0c;并且易于测试。 …...

    2024/4/28 12:10:02
  6. Redux基础学习

    文章目录介绍基础学习 Redux一、安装二、Action三、ReducerObject.assign 须知switch 和样板代码须知四、Store五、案例演示基础学习 React Redux一、安装二、Provider 标签三、connect函数1、mapStateToProps(state, ownProps) : stateProps2、mapDispatchToProps(dispatch, o…...

    2024/4/21 11:46:16
  7. 前端技术(二):从Flux到Redux

    什么是Redux&#xff1f; 首先我们要搞清楚&#xff0c;Redux解决了哪些问题&#xff1f;主要是以下3点&#xff1a; 1.如何在应用程序的整个生命周期内维持所有数据&#xff1f; Redux是一个“状态容器”。写过React或者ReactNative的同学可能会有感受&#xff0c;如果多个…...

    2024/4/21 11:46:15
  8. 双眼皮手术左右不对称

    ...

    2024/4/21 11:46:14
  9. 双眼皮手术肿了

    ...

    2024/4/21 11:46:13
  10. 双眼皮手术郑州医院好

    ...

    2024/4/21 11:46:12
  11. 双眼皮手术郑州医院比较好呢

    ...

    2024/4/21 11:46:12
  12. 双眼皮手术郑州医院

    ...

    2024/4/21 11:46:11
  13. AngularJs 的compile, preLink , postlink

    AngularJs 的compile, preLink , postlink 主要参考文章 【1】 http://www.jb51.net/article/58229.htm 文章中对这三个概念做了详细的解析&#xff0c; 如果你只写了一个link , 那么系统会默认是post link, 开发者都喜欢在post link 函数中增加相应的逻辑就是以为你可以保证…...

    2024/4/21 11:46:09
  14. AngularJS(五)

    今天接着学习指令 ng-style和ng-class一样也可用于动态设置CSS样式 ng-style属性通常被解析为一个js对象&#xff08;实际上就该算是css中定义的一个类&#xff09;&#xff0c;而昨天学的ng-class是可以根据不同情况解析为字符串和js对象的 解析为字符串的用法就是这样&#x…...

    2024/4/20 16:57:24
  15. 走进AngularJs(三)自定义指令-----(上)

    原文转自&#xff1a;http://www.cnblogs.com/lvdabao/p/3391634.html 一、有感而发的一些话 在学习ng之前有听前辈说过&#xff0c;angular上手比较难&#xff0c;初学者可能不太适应其语法以及思想。随着对ng探索的一步步深入&#xff0c;也确实感觉到了这一点&#xff0c;尤…...

    2024/4/20 16:55:51
  16. angularJs(一)指令

    1、调用 指令调用可以ng:bind, ng-bind, ng_bind, x-ng-bind , data-ng-bind这些方式 <!doctype html> <html ng-app><head><script src"http://code.angularjs.org/angular-1.1.0.min.js"></script><script src"script.js&q…...

    2024/4/20 16:55:50
  17. 双眼皮手术一个月不对称

    ...

    2024/4/20 16:55:49
  18. ionic2 目录

    首先 ionic2 暂时找不到中文文档。本人英语又很渣。无奈之下只能依赖于百度翻译。完全是已自己理解的方式运作 &#xff0c;可能里面会有一些偏差之类的 不过我都测试过代码是可以跑通的 只不过讲解的部分可能。。。。毕竟英语只有20多分的人。有不对的地方可以指出 共同学习。…...

    2024/4/20 12:44:57
  19. cartographer trajectory_builder_2d.lua参数解析

    概述&#xff1a;cartographer的地图由许多个submap构成&#xff0c;每个submap又由许多个Node构成&#xff0c;而每个Node是由一帧或多帧激光数据累积得到。 -- Copyright 2016 The Cartographer Authors -- -- Licensed under the Apache License, Version 2.0 (the "L…...

    2024/4/21 11:46:08
  20. 响应式布局

    媒体查询&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <title></title> <style> .a{ border: 1px solid #000000; width: 100px; height: 100px; background-color: #000000; } media only scre…...

    2024/4/21 11:46:07

最新文章

  1. 正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-1.3

    前言&#xff1a; 本文是根据哔哩哔哩网站上视频“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”的学习笔记&#xff0c;在这里会记录下正点原子Linux ARM MX6ULL 开发板根据配套的哔哩哔哩学习视频所作的实验和笔记内容。本文大量的引用了正点原子哔哔哩网…...

    2024/4/28 15:59:05
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. vscode为什么设置不了中文?

    VSCode中文插件安装 在VSCode中设置中文的首要步骤是安装“Chinese (Simplified) Language Pack for Visual Studio Code”扩展插件。这一过程十分简单&#xff0c;只需打开VSCode&#xff0c;进入扩展市场&#xff0c;搜索“ Chinese (Simplified) Language Pack ”然后点击…...

    2024/4/28 13:37:22
  4. 【LeetCode热题100】【二叉树】二叉树的中序遍历

    题目链接&#xff1a;94. 二叉树的中序遍历 - 力扣&#xff08;LeetCode&#xff09; 中序遍历就是先遍历左子树再遍历根最后遍历右子树 class Solution { public:void traverse(TreeNode *root) {if (!root)return;traverse(root->left);ans.push_back(root->val);tra…...

    2024/4/27 18:01:59
  5. 数据结构——二叉树——二叉搜索树(Binary Search Tree, BST)

    目录 一、98. 验证二叉搜索树 二、96. 不同的二叉搜索树 三、538. 把二叉搜索树转换为累加树 二叉搜索树&#xff1a;对于二叉搜索树中的每个结点&#xff0c;其左子结点的值小于该结点的值&#xff0c;而右子结点的值大于该结点的值 一、98. 验证二叉搜索树 给你一个二叉树的…...

    2024/4/26 8:24:13
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/28 13:52:11
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/28 3:28:32
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/26 23:05:52
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/28 13:51:37
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/28 15:57:13
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/25 18:39:16
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/28 1:22:35
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/25 18:39:14
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/4/26 23:04:58
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/26 19:46:12
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/27 11:43:08
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/27 8:32:30
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57