步骤:css
建立应用,返回 dva 实例。(注:dva 支持多实例)。node
const app = dva({
history, // 指定给路由用的 history,默认是 hashHistory
initialState, // 指定初始数据,优先级高于 model 中的 state
onError, // effect 执行错误或 subscription 经过 done 主动抛错时触发,可用于管理全局出错状态。
onAction, // 在 action 被 dispatch 时触发
onStateChange, // state 改变时触发,可用于同步 state 到 localStorage,服务器端等
onReducer, // 封装 reducer 执行。好比借助 redux-undo 实现 redo/undo
onEffect, // 封装 effect
onHmr, // 热替换相关
extraReducers, // 指定额外的 reducer,好比 redux-form 须要指定额外的 form reducer
extraEnhancers, // 指定额外的 StoreEnhancer ,好比结合 redux-persist 的使用
});
复制代码
这里能够对如下的hook进行option配置 这里能够将hashhistory转化为browserHistoryreact
import createHistory from 'history/createBrowserHistory';
const app = dva({
history: createHistory(),
});
复制代码
一样能够配置hooks以及注册其余插件webpack
import createLoading from 'dva-loading';
...
app.use(createLoading(opts));
复制代码
在普通的react-redux+redux-saga的项目中,咱们首先会建4个文件夹,分别是actions,reducer,saga,组件,还有获取请求数据的services文件夹,一样在入口文件那要引入不少中间件、provider、connect等去将这几个文件夹联系起来,在这里的model如下就将这些集成在了一块儿,大大减少了开发工做量。 web
namespace model 的命名空间,同时也是他在全局 state 上的属性,只能用字符串,不支持经过 . 的方式建立多层命名空间。至关于这个model的key 在组件里面,经过connect+这个key将想要引入的model加入数据库
import { connect } from 'dva'
复制代码
const app = dva({
initialState: { count: 1 },
});
app.model({
namespace: 'count',
state: 0,
});
复制代码
此时为1express
['setQuery']: [function*() {}, { type: 'takeEvery'}],
复制代码
- takeEvery监听action的每次变化执行(默认)
- takeLatest监听action最近一次的变化
- take监听一次action留着,后面执行动做
复制代码
Effect 被称为反作用,在咱们的应用中,最多见的就是异步操做。它来自于函数编程的概念,之因此叫反作用是由于它使得咱们的函数变得不纯,一样的输入不必定得到一样的输出。npm
dva 为了控制反作用的操做,底层引入了redux-sagas作异步流程控制,因为采用了generator的相关概念,因此将异步转成同步写法,从而将effects转为纯函数。至于为何咱们这么纠结于 纯函数,若是你想了解更多能够阅读Mostly adequate guide to FP,或者它的中文译本JS函数式编程指南。编程
纯函数的好处:将函数抽离出来,与业务不耦合json
更有利于单元测试
无反作用(side-effect),不会修改做用域外的值,使代码好调试
执行顺序不会对系统形成影响
剥离出业务逻辑,好复用
复制代码
const { dispatch } = this.props;
dispatch({
type: 'app/updateState' ,
payload: {
opacityTop: 'none',//控制top的透明度
hiddenDivDisplay: 'none',//控制隐藏头部的display
footerDisplay: 'none'//控制footer的display
}
});
复制代码
subscriptions 以 key/value 格式定义 subscription。subscription 是订阅,用于订阅一个数据源,而后根据须要 dispatch 相应的 action。在 app.start() 时被执行,数据源能够是当前的时间、服务器的 websocket 链接、keyboard 输入、geolocation 变化、history 路由变化等等。
格式为 ({ dispatch, history }, done) => unlistenFunction。
注意:若是要使用 app.unmodel(),subscription 必须返回 unlisten 方法,用于取消数据订阅。
复制代码
app.router(require('./router'));
app.router(() => <App />);
复制代码
启动应用。selector 可选,若是没有 selector 参数,会返回一个返回 JSX 元素的函数。 selector为根元素
app.start('#root');
复制代码
roadhog server 支持 mock 功能,相似 dora-plugin-proxy,在 .roadhogrc.mock.js 中进行配置,支持基于 require 动态分析的实时刷新,支持 ES6 语法,以及友好的出错提示。在配置文件进行一下(node语法)配置,就能够经过简单的fetch请求获取到数据。
.roadhogrc.mock.js
export default {
// 支持值为 Object 和 Array
'GET /api/users': { users: [1,2] },
// GET POST 可省略
'/api/users/1': { id: 1 },
// 支持自定义函数,API 参考 express@4
'POST /api/users/create': (req, res) => { res.end('OK'); },
// Forward 到另外一个服务器
'GET /assets/*': 'https://assets.online/',
// Forward 到另外一个服务器,并指定子路径
// 请求 /someDir/0.0.50/index.css 会被代理到 https://g.alicdn.com/tb-page/taobao-home, 实际返回 https://g.alicdn.com/tb-page/taobao-home/0.0.50/index.css
'GET /someDir/(.*)': 'https://g.alicdn.com/tb-page/taobao-home',
};
复制代码
若为多接口应用,则在mock文件夹下利用mockjs进行数据模拟,再在配置文件里,进行文件遍历引入
mock->user.js
const qs = require('qs');
const mockjs = require('mockjs'); //导入mock.js的模块
const Random = mockjs.Random; //导入mock.js的随机数
// 数据持久化 保存在global的全局变量中
let tableListData = {};
if (!global.tableListData) {
const data = mockjs.mock({
'data|100': [{
'id|+1': 1,
'name': () => {
return Random.cname();
},
'mobile': /1(3[0-9]|4[57]|5[0-35-9]|7[01678]|8[0-9])\d{8}/,
}],
page: {
total: 100,
current: 1,
},
});
tableListData = data;
global.tableListData = tableListData;
} else {
tableListData = global.tableListData;
}
module.exports = {
//post请求 /api/users/ 是拦截的地址 方法内部接受 request response对象
'GET /users' (req, res) {
setTimeout(() => {
res.json({ //将请求json格式返回
success: true,
data,
page: '123',
});
}, 200);
},
.roadhogrc.mock.js
const mock = {}
require('fs').readdirSync(require('path').join(__dirname + '/mock')).forEach(function(file) {
Object.assign(mock, require('./mock/' + file))
})
module.exports = mock
复制代码
格式为 JSON,容许注释,布尔类型的配置项默认值均为 false,支持经过 webpack.config.js 以编码的方式进行配置,但不推荐,由于 roadhog 自己的 major 或 minor 升级可能会引发兼容问题。
"extraBabelPlugins": [
"transform-runtime",
"dva-hmr",
["import", { "libraryName": "antd", "libraryDirectory": "lib", "style": "css" }]
]
复制代码
"proxy": {
"/api": {
"target": "http://jsonplaceholder.typicode.com/",
"changeOrigin": true,
"pathRewrite": { "^/api" : "" }
}
}
复制代码
multipage 配置是否多页应用。多页应用会自动提取公共部分为 common.js 和 common.css 。
define 配置 webpack 的 DefinePlugin 插件,define 的值会自动作 JSON.stringify 处理。
env 针对特定的环境进行配置。server 的环境变量是 development,build 的环境变量是 production。防止生产环境冗余。
"extraBabelPlugins": ["transform-runtime"],
"env": {
"development": {
"extraBabelPlugins": ["dva-hmr"]
}
}
复制代码
"theme": {
"@primary-color": "#1DA57A"
}
/
"theme": "./node_modules/abc/theme-config.js"
复制代码
在router.js中使用,动态加载model和component app: dva 实例,加载 models 时须要 models: 返回 Promise 数组的函数,Promise 返回 dva model component:返回 Promise 的函数,Promise 返回 React Component
在roadhog中引入他们本身封装的af-webpack,这里面用css-loader以及加上.webpackrc的配置对css进行模块化,将css结果js的一层封装,给classname后面加上随机的hash,使得classname不会冲突,若要全局的就加上:global便可
若是当前应用中加载了不止一个model,在其中一个的effect里面作select操做,是能够获取另一个中的state的:
*foo(action, { select }) {
const { a, b } = yield select();
}
复制代码
function createModel(options) {
const { namespace, param } = options;
return {
namespace: `demo${namespace}`,
states: {},
reducers: {},
effects: {
*foo() {
// 这里能够根据param来肯定下面这个call的参数
yield call()
}
}
};
}
const modelA = createModel({ namespace: 'A', param: { type: 'A' } });
const modelB = createModel({ namespace: 'A', param: { type: 'B' } });
复制代码
const [result1, result2] = yield all([
call(service1, param1),
call(service2, param2)
])
复制代码
const { data, timeout } = yield race({
data: call(service, 'some data'),
timeout: call(delay, 1000)
});
if (data)
put({type: 'DATA_RECEIVED', data});
else
put({type: 'TIMEOUT_ERROR'});
复制代码
若是这里是要在组件里面作某些事情,怎么办? 将resolve传给model
new Promise((resolve, reject) => {
dispatch({ type: 'reusable/addLog', payload: { data: 9527, resolve, reject } });
})
.then((data) => {
console.log(`after a long time, ${data} returns`);
});
复制代码
在model进行跨model通讯
try {
const result = yield call(service1);
yield put({ type: 'service1Success', payload: result });
resolve(result);
}
catch (error) {
yield put({ type: 'service1Fail', error });
reject(ex);
}
复制代码
roadhog主要是依赖于他们本身封装的af-webpack
在getUserConfig的文件夹下,直接经过json内容去获取配置