做网站需要哪些程序,免费seo网站推广,个人网站设计与实现结论,广州建网站的网络公司2019独角兽企业重金招聘Python工程师标准 image.png Redux 的第一次代码提交是在 2015 年 5 月底#xff08;也就是一年多前的样子#xff09;#xff0c;那个时候 React 的最佳实践还不是明晰#xff0c;作为一个 View 层#xff0c;有人会用 backbone 甚至… 2019独角兽企业重金招聘Python工程师标准 image.png Redux 的第一次代码提交是在 2015 年 5 月底也就是一年多前的样子那个时候 React 的最佳实践还不是明晰作为一个 View 层有人会用 backbone 甚至是 angular 和它搭配也有人觉得这层 View 功能已经足够强大简单地搭配一些 utils 就直接上。后来便有了 FLUX 的演讲React 社区开始注意到这种新的类似函数式编程的理念Redux 也作为 FLUX 的一种变体开始受到关注再后来顺理成章地得到 React 的『钦点』作者也加入了 Facebook 从事 React 的开发。生态圈经过了这一年的成熟现在很多第三方库已经非常完善所以这里想介绍一下目前 Redux 的一些最佳实践。 1. 复习一下 Redux 的基本概念 首先我们复习一下 Redux 的基本概念 如果你已经很熟悉了就直接跳过这一章吧。 Redux 把界面视为一种状态机界面里的所有状态、数据都可以由一个状态树来描述。所以对于界面的任何变更都简化成了状态机的变化 (State, Input) NewState 这其中切分成了三个阶段 actionreducerstore所谓的 action就是用一个对象描述发生了什么Redux 中一般使用一个纯函数即 actionCreator 来生成 action 对象。 // actionCreator action
// 这是一个纯函数只是简单地返回 action
function somethingHappened(data){return {type: foo,data: data}
} 随后这个 action 对象和当前的状态树 state 会被传入到 reducer 中产生一个新的 state //reducer(action, state) newState
function reducer(action, state){switch(action.type){case foo:return { data: data };default:return state;}
} store 的作用就是储存 state并且监听其变化。简单地说就是你可以这样产生一个 store : import { createStore } from redux
//这里的 reducer 就是刚才的 Reducer 函数
let store createStore(reducer); 然后你可以通过 dispatch 一个 action 来让它改变状态 store.getState();//{}
store.dispatch(somethingHappened(aaa));
store.getState(); // { data: aaa} 好了这就是 Redux 的全部功能。对的它就是如此简单以至于它本体只有 3KB 左右的代码因为它只是实现了一个简单的状态机而已任何稍微有点编程能力的人都能很快写出这个东西。至于和 React 的结合则需要 react-redux 这个库这里我们就不讲怎么用了。 2. Redux的一些痛点 大体上Redux 的数据流是这样的 界面 action reducer store react virtual dom 界面 每一步都很纯净看起来很美好对吧对于一些小小的尝试性质的 DEMO 来说确实很美好。但其实当应用变得越来越大的时候这其中存在诸多问题 如何优雅地写异步代码从简单的数据请求到复杂的异步逻辑状态树的结构应该怎么设计如何避免重复冗余的 actionCreator状态树中的状态越来越多结构越来越复杂的时候和 react 的组件映射如何避免混乱每次状态的细微变化都会生成全新的 state 对象其中大部分无变化的数据是不用重新克隆的这里如何提高性能你以为我会在下面一一介绍这些问题是怎么解决的还真不是这里大部分问题的回答都可以在官方文档中看到 技巧 | Redux 中文文档 文档里讲得已经足够详细有些甚至详细得有些啰嗦了。所以下面只挑 Redux 生态圈里几个比较成熟且流行的组件来讲讲。 3. Redux 异步控制 官方文档里介绍了一种很朴素的异步控制中间件 redux-thunk 如果你还不了解中间件的话请看 Middleware | Redux 中文文档 事实上 redux-thunk 的代码很简单简单到只有几行代码 function createThunkMiddleware(extraArgument) {return ({ dispatch, getState }) next action {if (typeof action function) {return action(dispatch, getState, extraArgument);}return next(action);};
} 它其实只干了一件事情判断 actionCreator 返回的是不是一个函数如果不是的话就很普通地传给下一个中间件或者 reducer如果是的话那么把 dispatch 、 getState 、 extraArgument 作为参数传入这个函数里实现异步控制。 比如我们可以这样写 //普通action
function foo(){return {type: foo,data: 123}
}//异步action
function fooAsync(){return dispatch {setTimeout(_ dispatch(123), 3000);}
} 但这种简单的异步解决方法在应用变得复杂的时候并不能满足需求反而会使 action 变得十分混乱。 举个比较简单的例子我们现在要实现『图片上传』功能用户点击开始上传之后显示出加载效果上传完毕之后隐藏加载效果并显示出预览图如果发生错误那么显示出错误信息并且在2秒后消失。 用普通的 redux-thunk 是这样写的 function upload(data){return dispatch {// 显示出加载效果dispatch({ type: SHOW_WAITING_MODAL });// 开始上传api.upload(data).then(res {// 成功隐藏加载效果并显示出预览图dispatch({ type: PRELOAD_IMAGES, data: res.images });dispatch({ type: HIDE_WAITING_MODAL });}).catch(err {// 错误隐藏加载效果显示出错误信息2秒后消失dispatch({ type: SHOW_ERROR, data: err });dispatch({ type: HIDE_WAITING_MODAL });setTimeout(_ dispatch({ type: HIDE_ERROR }), 2000);})}
} 这里的问题在于一个异步的 upload action 执行过程中会产生好几个新的 action更可怕的是这些新的 action 也是包含逻辑的比如要判断是否错误这直接导致异步代码中到处都是 dispatch(action) 是很不可控的情况。如果还要进一步考虑取消、超时、队列的情况就更加混乱了。 所以我们需要更强大的异步流控制这就是 GitHub - yelouafi/redux-saga: An alternative side effect model for Redux apps 。下面我们来看看如果换成 redux-saga 的话会怎么样 import { take, put, call, delay } from redux-saga/effects
// 上传的异步流
function *uploadFlow(action) {// 显示出加载效果yield put({ type: SHOW_WAITING_MODAL });// 简单的 try-catchtry{const response yield call(api.upload, action.data);yield put({ type: PRELOAD_IMAGES, data: response.images });yield put({ type: HIDE_WAITING_MODAL });}catch(err){yield put({ type: SHOW_ERROR, data: err });yield put({ type: HIDE_WAITING_MODAL });yield delay(2000);yield put({ type: HIDE_ERROR });}
}function* watchUpload() {yield* takeEvery(BEGIN_REQUEST, uploadFlow)
} 是不是规整很多呢redux-saga 允许我们使用简单的 try-catch 来进行错误处理更神奇的是竟然可以直接使用 delay 来替代 setTimeout 这种会造成回调和嵌套的不优雅的方法。 本质上讲redux-sage 提供了一系列的『副作用side-effects方法』比如以下几个 put 产生一个 actioncall 阻塞地调用一个函数fork 非阻塞地调用一个函数take 监听且只监听一次 actiondelay 延迟race 只处理最先完成的任务并且通过 Generator 实现对于这些副作用的管理让我们可以用同步的逻辑写一个逻辑复杂的异步流。 下面这个例子出自于 官方文档 实现了一个对于请求的队列即让程序同一时刻只会进行一个请求其它请求则排队等待直到前一个请求结束 import { buffers } from redux-saga;
import { take, actionChannel, call, ... } from redux-saga/effects;function* watchRequests() {// 1- 创建一个针对请求事件的 channelconst requestChan yield actionChannel(REQUEST);while (true) {// 2- 从 channel 中拿出一个事件const {payload} yield take(requestChan);// 3- 注意这里我们使用的是阻塞的函数调用yield call(handleRequest, payload);}
}function* handleRequest(payload) { ... } 更多关于 redux-saga 的内容请参考 Read Me | redux-saga 中文文档 自述 | Redux-saga 中文文档 。 4. 提高 selector 的性能 把 react 与 redux 结合的时候react-redux 提供了一个极其重要的方法 connect 它的作用就是选取 redux store 中的需要的 state 与 dispatch , 交由 connect 去绑定到 react 组件的 props 中 import { connect } from react-redux;
import { toggleTodo } from ../actions
import TodoList from ../components/TodoList// 我们需要向 TodoList 中注入一个名为 todos 的 prop
// 它通过以下这个函数从 state 中提取出来
const mapStateToProps (state) {// 下面这个函数就是所谓的selectortodos: state.todos.filter(i i.completed)// 其它props...
}const mapDispatchToProps (dispatch) {onTodoClick: (id) {dispatch(toggleTodo(id))}
}// 绑定到组件上
const VisibleTodoList connect(mapStateToProps,mapDispatchToProps
)(TodoList)export default VisibleTodoList 在这里需要指定哪些 state 属性被注入到 component 的 props 中这是通过一个叫 selector 的函数完成的。 上面这个例子存在一个明显的性能问题每当组件有任何更新时都会调用一次 state.todos.filter 来计算 todos 但我们实际上只需要在 state.todos 变化时重新计算即可每次更新都重算一遍是非常不合适的做法。下面介绍的这个 reselect 就能帮你省去这些没必要的重新计算。 你可能会注意到 selector 实际上就是一个『 纯函数』 selector(state) some props 而纯函数是具有可缓存性的即对于同样的输入参数永远会得到相同的输出值 如果对这个不太熟悉的同学可以参考 JavaScript函数式编程 reselect 的原理就是如此每次调用 selector 函数之前它会判断参数与之前缓存的是否有差异若无差异则直接返回缓存的结果反之则重新计算 import { createSelector } from reselect;var state {a: 100
}var naiveSelector state state.a;// mySelector 会缓存输入 a 对应的输出值
var mySelector createSelector(naiveSelector, a {console.log(做一次乘法!!!);return a * a;}
)console.log(mySelector(state)); // 第一次计算需要做一次乘法
console.log(mySelector(state)); // 输入值未变化直接返回缓存的结果
console.log(mySelector(state)); // 同上
state.a 5; // 改变 a 的值
console.log(mySelector(state)); // 输入值改变做一次乘法
console.log(mySelector(state)); // 输入值未变化直接返回缓存的结果
console.log(mySelector(state)); // 同上 上面的输出值是 做一次乘法!!!
10000
10000
10000
做一次乘法!!!
25
25
25 之前那个关于 todos 的范例可以这样改就可以避免 todos 数组被重复计算的性能问题 import { createSelector } from reselect;
import { connect } from react-redux;
import { toggleTodo } from ../actions
import TodoList from ../components/TodoListconst todoSelector createSelector(state state.todos,todos todos.filter(i i.completed)
)const mapStateToProps (state) {todos: todoSelector// 其它props...
}const mapDispatchToProps (dispatch) {onTodoClick: (id) {dispatch(toggleTodo(id))}
}// 绑定到组件上
const VisibleTodoList connect(mapStateToProps,mapDispatchToProps
)(TodoList)export default VisibleTodoList 更多可以参考 GitHub - reactjs/reselect: Selector library for Redux 5. 减少冗余代码 redux 中的 action 一般都类似这样写 function foo(data){return {type: FOO,data: data}
}//或者es6写法
var foo data ({ type: FOO, data}) 当应用越来越大之后action 的数量也会大大增加为每个 action 对象显式地写上 type 和 data 或者其它属性会造成大量的代码冗余这一块是完全可以优化的。 比如我们可以写一个最简单的 actionCreator function actionCreator(type){return function(data){return {type: type,data: data}}
}var foo actionCreator(FOO);
foo(123); // {type: FOO, data: 123} redux-actions 就可以为我们做这样的事情除了上面这种朴素的做法它还有其它比较好用的功能比如它提供的 createActions 方法可以接受不同类型的参数以产生不同效果的 actionCreator下面这个范例来自官方文档 import { createActions } from redux-actions;const { actionOne, actionTwo, actionThree } createActions({// 函数类型ACTION_ONE: (key, value) ({ [key]: value }),// 数组类型ACTION_TWO: [(first) first, // payload(first, second) ({ second }) // meta],// 最简单的字符串类型
}, ACTION_THREE);actionOne(key, 1));
//
//{
// type: ACTION_ONE,
// payload: { key: 1 }
//}actionTwo(Die! Die! Die!, It\s highnoon~);
//
//{
// type: ACTION_TWO,
// payload: [Die! Die! Die!],
// meta: { second: It\s highnoon~ }
//}actionThree(76);
//
//{
// type: ACTION_THREE,
// payload: 76,
//} 更多可以参考 GitHub - acdlite/redux-actions: Flux Standard Action utilities for Redux. 转载于:https://my.oschina.net/cllgeek/blog/1584693