Dva上手示例及用法解析

快速上手

dva 首先是一个基于 redux  redux-saga 的数据流方案,为了简化开发体验,dva还额外内置了 react-router 和 fetch,所以也可

以理解为一个轻量级的应用框架。

 

#全局安装 dva-cli

cnpm install dva-cli -g

#创建新项目

dva new <project-name>

#使用 antd

安装 antd 和 babel-plugin-import。babel-plugin-import 实现 antd 的按需加载。

cnpm install antd babel-plugin-import --save

编辑 .webpackrc 使 babel-plugin-import 插件生效。

'extraBabelPlugins': [
    ['import', {'libraryName':'antd','libraryDirectory':'es','style':'css'}]
]

 

 

DVA核心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'),
});
  • app: dva 实例,加载 models 时需要
  • models: 返回 Promise 数组的函数,Promise 返回 dva model
  • component:返回 Promise 的函数,Promise 返回 React Component

 

dva API

index.js示例

import dva from 'dva';
const app = dva();
app.use({});
app.model(require('./models/products/products').default);
app.router(require('./router').default);
app.start('#root');

#const app = dva(opts)

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

opts 包含如下属性:

  • initialState        // 指定初始数据,优先级高于 model 中的 state,默认为空。

  • history              // 指定给路由用的 history,默认 hashHistory。

如需使用 browserHistory,需安装history第三方库。

    (1) 安装 history

cnpm install history

    (2) 修改 history 配置 

import { createBrowserHistory } from 'history'
const app = dva({
  history: createBrowserHistory()
})

出于易用性的考虑,opts 里也可以配置所有 hooks。

 

2. app.use(hooks)

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

hooks 包含如下配置项:

    onError, 

    onAction, 

    onReducer, 

    onEffect, 

    onStateChange, 

    onHmr,

    extraReducers, 

    extraEnhancers

 

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

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

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

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

(2) onAction(fn | fn[ ])

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

    例如我们要通过 redux-logger 打印日志:

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

(3) 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
      }
    }
  }
})

(4) onEffect(fn)

    封装 effects 执行。比如 dva-loading 基于此实现了自动处理 loading 状态。

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

(5) onStateChange(fn)

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

(6) onHmr(fn)

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

(7) extraReducers

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

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

 

3. app.model(model)

注册 model

model对象中包含5个重要的属性:

    namespace,

    state,

    reducers,

    effects,

    subscriptions

 

(1) namespace

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

(2) state

    reducer的初始值,优先级低于 dva() 实例对象的 initialState。

    model 内的 state 指当前 model 的 state,外部的 state 指全局的 state。

    外部使用 model 内部 state 的方式为:state. + namespace

(3) reducers

    reducer 用于处理同步操作,是唯一可以修改 state 的地方,由 action 触发。

    reducer 接受两个参数,state 和 action,需要返回新的 state。

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

(4)effects

    effect 处理异步操作和业务逻辑,能与服务器交互,获取全局 state等。不直接修改 state。由 action 触发,可以触发action。

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

(5) subscriptions

    subscription 是订阅,用于订阅一个数据源,然后根据需要 dispatch 相应的 action。

    在 app.start() 时被执行,数据源可以是当前时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history

    路由变化等等。

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

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

    subscriptions 写法:

subscriptions: {
  setup({ dispatch, history }) {
    // 监听路由的变化,请求页面数据
    return history.listen(({ pathname, search }) => {
      const query = queryString.parse(search)
      let list = []
      if (pathname === 'todoList') {
        dispatch({ type: 'save', payload: {list} })
      }
    })
  }
}

 

4. app.unmodel(namespace)

取消 model 注册,清理 reducers, effects 和 subscriptions。

subscription 如果没有返回 unlisten 函数,使用 app.unmodel 会给予警告

 

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

注册路由表,注册路由

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

如果我们想解决组件动态加载问题,我们的路由文件可以按照下面的写法来写:

import { Router, Switch, Route } from 'dva/router'
import dynamic from 'dva/dynamic'

function RouterConfig({ history, app }) {
  const IndexPage = dynamic({
    app,
    component: () => import('./routes/IndexPage'),
  })

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

  return (
    <Router history={history}>
      <Switch>
        <Route exact path="/" component={IndexPage} />
        <Route exact path="/products" component={Products} />
      </Switch>
    </Router>
  )
}

export default RouterConfig

其中 dynamic(opts) 中 opts 包含三个配置项:

    app: dva 实例,加载 models 时需要。

    models: 返回 Promise 数组的函数,Promise 返回 dva model。

    component:返回 Promise 的函数,Promise 返回 React Component。

 

6. app.start('selector')

app.start('#root')

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

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值