react学习笔记 —— dva

什么是dva?

dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch ,所以也可以理解为一个轻量级的应用框架。

dva 应用的最简结构(带 model)

// 创建应用
const app = dva();

// 注册 Model
app.model({
  namespace: 'count',
  state: 0,
  reducers: {
    add(state) { return state + 1 },
  },
  effects: {
    *addAfter1Second(action, { call, put }) {
      yield call(delay, 1000);
      yield put({ type: 'add' });
    },
  },
});

// 注册视图
app.router(() => <ConnectedApp />);

// 启动应用
app.start('#root');

dva
默认输出文件。
react-router 接口, react-router-redux 的接口通过属性 routerRedux 输出。

在 dva 中,通常需要 connect Model的组件都是 Route Components,组织在/routes/目录下,而/components/目录下则是纯组件(Presentational Components)

API

输出文件

dva

默认输出文件。

dva/router

默认输出 react-router
接口, react-router-redux

的接口通过属性 routerRedux 输出。

比如:

import { Router, Route, routerRedux } from 'dva/router';

dva/fetch

异步请求库,输出 isomorphic-fetch

的接口。不和 dva 强绑定,可以选择任意的请求库。

dva/saga

输出 redux-saga

的接口,主要用于用例的编写。(用例中需要用到 effects)

dva/dynamic

解决组件动态加载问题的 util 方法。

比如:

import dynamic from 'dva/dynamic';

const UserPageComponent = dynamic({
app,
models: () => [
import('./models/users'),
],
component: () => import('./routes/UserPage'),
});

opts 包含:

app: dva 实例,加载 models 时需要
models: 返回 Promise 数组的函数,Promise 返回 dva model
component:返回 Promise 的函数,Promise 返回 React Component

dva API

app = dva(opts)

创建应用,返回 dva 实例。(注:dva 支持多实例)

opts 包含:

history:指定给路由用的 history,默认是 hashHistory
initialState:指定初始数据,优先级高于 model 中的 state,默认是 {}

如果要配置 history 为 browserHistory,可以这样:

import createHistory from 'history/createBrowserHistory';
const app = dva({
history: createHistory(),
});

另外,出于易用性的考虑,opts 里也可以配所有的 hooks ,下面包含全部的可配属性:

const app = dva({
history,
initialState,
onError,
onAction,
onStateChange,
onReducer,
onEffect,
onHmr,
extraReducers,
extraEnhancers,
});

app.use(hooks)

配置 hooks 或者注册插件。(插件最终返回的是 hooks )

比如注册 dva-loading

插件的例子:

import createLoading from 'dva-loading';
...
app.use(createLoading(opts));

hooks 包含:

onError((err, dispatch) => {})

effect 执行错误或 subscription 通过 done 主动抛错时触发,可用于管理全局出错状态。

注意:subscription 并没有加 try...catch,所以有错误时需通过第二个参数 done 主动抛错。例子:

app.model({
subscriptions: {
setup({ dispatch }, done) {
done(e);
},
},
});

如果我们用 antd,那么最简单的全局错误处理通常会这么做:

import { message } from 'antd';
const app = dva({
onError(e) {
message.error(e.message, /* duration */3);
},
});

onAction(fn | fn[])

在 action 被 dispatch 时触发,用于注册 redux 中间件。支持函数或函数数组格式。

例如我们要通过 redux-logger

打印日志:

import createLogger from 'redux-logger';
const app = dva({
onAction: createLogger(opts),
});

onStateChange(fn)

state 改变时触发,可用于同步 state 到 localStorage,服务器端等。

onReducer(fn)

封装 reducer 执行。比如借助 redux-undo

实现 redo/undo :

import undoable from 'redux-undo';
const app = dva({
onReducer: reducer => {
return (state, action) => {
const undoOpts = {};
const newState = undoable(reducer, undoOpts)(state, action);
// 由于 dva 同步了 routing 数据,所以需要把这部分还原
return { ...newState, routing: newState.present.routing };
},
},
});

onEffect(fn)

封装 effect 执行。比如 dva-loading

基于此实现了自动处理 loading 状态。

onHmr(fn)

热替换相关,目前用于 babel-plugin-dva-hmr

extraReducers

指定额外的 reducer,比如 redux-form

需要指定额外的 form reducer:

import { reducer as formReducer } from 'redux-form'
const app = dva({
extraReducers: {
form: formReducer,
},
});

extraEnhancers

指定额外的 StoreEnhancer
,比如结合 redux-persist

的使用:

import { persistStore, autoRehydrate } from 'redux-persist';
const app = dva({
extraEnhancers: [autoRehydrate()],
});
persistStore(app._store);

app.model(model)

注册 model,详见 #Model 部分。

app.unmodel(namespace)

取消 model 注册,清理 reducers, effects 和 subscriptions。subscription 如果没有返回 unlisten 函数,使用 app.unmodel 会给予警告⚠️。

app.replaceModel(model)

只在app.start()之后可用

替换model为新model,清理旧model的reducers, effects 和 subscriptions,但会保留旧的state状态,对于HMR非常有用。subscription 如果没有返回 unlisten 函数,使用 app.unmodel 会给予警告⚠️。

如果原来不存在相同namespace的model,那么执行app.model操作

app.router(({ history, app }) => RouterConfig)

注册路由表。

通常是这样的:

import { Router, Route } from 'dva/router';
app.router(({ history }) => {
return (
<Router history={history}>
<Route path="/" component={App} />
</Router>
);
});

推荐把路由信息抽成一个单独的文件,这样结合 babel-plugin-dva-hmr

可实现路由和组件的热加载,比如:

app.router(require('./router'));

而有些场景可能不使用路由,比如多页应用,所以也可以传入返回 JSX 元素的函数。比如:

app.router(() => <App />);

app.start(selector?)

启动应用。selector 可选,如果没有 selector 参数,会返回一个返回 JSX 元素的函数。

app.start('#root');

那么什么时候不加 selector?常见场景有测试、node 端、react-native 和 i18n 国际化支持。

比如通过 react-intl 支持国际化的例子:

import { IntlProvider } from 'react-intl';
...
const App = app.start();
ReactDOM.render(<IntlProvider><App /></IntlProvider>, htmlElement);

Model

model 是 dva 中最重要的概念。以下是典型的例子:

app.model({
namespace: 'todo',
state: [],
reducers: {
add(state, { payload: todo }) {
// 保存数据到 state
return [...state, todo];
},
},
effects: {
*save({ payload: todo }, { put, call }) {
// 调用 saveTodoToServer,成功后触发 add action 保存到 state
yield call(saveTodoToServer, todo);
yield put({ type: 'add', payload: todo });
},
},
subscriptions: {
setup({ history, dispatch }) {
// 监听 history 变化,当进入 / 时触发 load action
return history.listen(({ pathname }) => {
if (pathname === '/') {
dispatch({ type: 'load' });
}
});
},
},
});

model 包含 5 个属性:

namespace

model 的命名空间,同时也是他在全局 state 上的属性,只能用字符串,不支持通过 . 的方式创建多层命名空间。

state

初始值,优先级低于传给 dva() 的 opts.initialState。

比如:

const app = dva({
initialState: { count: 1 },
});
app.model({
namespace: 'count',
state: 0,
});

此时,在 app.start() 后 state.count 为 1 。

reducers

以 key/value 格式定义 reducer。用于处理同步操作,唯一可以修改 state 的地方。由 action 触发。

格式为 (state, action) => newState 或 [(state, action) => newState, enhancer]。

详见: https://github.com/dvajs/dva/blob/master/packages/dva-core/test/reducers.test.js

effects

以 key/value 格式定义 effect。用于处理异步操作和业务逻辑,不直接修改 state。由 action 触发,可以触发 action,可以和服务器交互,可以获取全局 state 的数据等等。

格式为 (action, effects) => void 或 [(action, effects) => void, { type }]。

type 类型有:

takeEvery
takeLatest
throttle
watcher

详见:https://github.com/dvajs/dva/blob/master/packages/dva-core/test/effects.test.js

subscriptions

以 key/value 格式定义 subscription。subscription 是订阅,用于订阅一个数据源,然后根据需要 dispatch 相应的 action。在 app.start() 时被执行,数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。

格式为 ({ dispatch, history }, done) => unlistenFunction。

注意:如果要使用 app.unmodel(),subscription 必须返回 unlisten 方法,用于取消数据订阅。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容

  • dva.js 简介 dva 是阿里前端架构师 sorrycc 带 team 研发的一套轻量级前端框架,其目的是尽量...
    那颗星_fcaf阅读 3,654评论 0 24
  • dva框架的使用详解及Demo教程 在前段时间,我们也学习讲解过Redux框架的基本使用,但是有很多同学在交流群里...
    光强_上海阅读 61,132评论 5 55
  • 笔者升级了 dva 的版本,同时新增了 umi 的使用,具体可以参考这篇文章 dva理论到实践——帮你扫清dva的...
    darrell阅读 39,725评论 8 85
  • title: dva的初识date: 2018-05-28 15:02:34tags: dva 内心独白 这段时间...
    Kris_lee阅读 1,394评论 0 2
  • L先生今年27岁,研究生毕业,刚出来找了份体面的工作,本想着好好奋斗一下,却被家里人接二连三地催着找对象,每次跟爸...
    素颜娇阳阅读 541评论 1 1