前端技术 | dva,美貌与智慧并存

栏目: 前端 · 发布时间: 6年前

内容简介:经过前面四篇的铺垫,终于轮到我们的主角dva了,就是下面这个美女:先擦一擦哈喇子,我们来介绍一下,dva出自于暴雪出品的一款游戏《守望先锋》,援引官方的角色介绍:D.Va拥有一部强大的机甲,它具有两台全自动的近距离聚变机炮、可以使机甲飞跃敌人或障碍物的推进器、 还有可以抵御来自正面的远程攻击的防御矩阵。

经过前面四篇的铺垫,终于轮到我们的主角dva了,就是下面这个美女:

前端技术 | dva,美貌与智慧并存

先擦一擦哈喇子,我们来介绍一下,dva出自于暴雪出品的一款游戏《守望先锋》,援引官方的角色介绍:

D.Va拥有一部强大的机甲,它具有两台全自动的近距离聚变机炮、可以使机甲飞跃敌人或障碍物的推进器、 还有可以抵御来自正面的远程攻击的防御矩阵。

然后呢,蚂蚁金服的一位架构师sorrycc很迷这位美女,正巧刚开发了一款前端框架没有名字,作为一个向女神献礼的项目,dva框架就此诞生。

实际上,dva只是基于现有开源框架的一层轻量封装,并没有引入任何新概念:

  • React:管理View
  • react-router:管理路由
  • Redux:管理Model
  • redux-saga:管理异步调用(副作用)

再来看一下框架图,是不是都是熟悉的配方,熟悉的味道?

前端技术 | dva,美貌与智慧并存

当然,也不是完全没有新东西,其中有一个Subscription好像之前没有见过,这是一种数据源订阅机制,数据源可以是键盘输入事件、路由变化、服务器的 websocket 连接等等。你可以在数据发生变化时收到通知,并派发必要的action。

实际上,dva是一个整合者,它的目标是解决“ Code is everywhere ”问题。当我们同时使用上面这些框架时,一般会呈现下面这种类型的文件结构:

+ src
  + sagas
    - user.js
  + reducers
    - user.js
  + actions
    - user.js
复制代码

然后,当我们需要实现一个功能时,就需要在这几个文件之间来回切换。。。

另一方面,dva还试图 隐藏一些经常重复书写的routine代码 ,让开发者能够更加专注于业务逻辑。比如我们写一个应用的入口文件,需要做下面这么多事情:

  • 配置middleware
  • 创建store
  • 添加<Provider>绑定
  • 创建watcher saga和root saga
  • 启动saga
  • 。。。

实际上,可能95%以上的项目中这些代码都是一模一样的,我们不需要每次都花费时间来重新写一遍这些代码。

下面开始正式介绍dva 1.0相关的内容,dva 2.0做了一些优化升级,后面专门有一节介绍具体变化。

8个概念

其实基本都是前面几篇文章里介绍过的概念:

  • state:也就是全局唯一的Store
  • action:即Redux中的action
  • model:这是dva抽象出来的一个概念,为了把下面这些东西放到一个统一的文件里
    • reducer:即Redux中的reducer
    • effect:即redux-saga中的worker saga
    • subscription:前面介绍过,用于订阅数据源
  • router:即react-router中的<Router>
    • route:即react-router中的<Route/>

最终写出来的model.js会类似下面这个样子,可以发现所有相关代码都放到一起了,不需要在多个文件之间来回切换了(这里的namespace就是以前Redux中的reducer的名字):

export default {
  namespace: 'transactions',
  state: {
    txs: []
  },
  subscriptions: {
    setup({ dispatch, history }) {
      history.listen(location => {
        if (location.pathname === '/transactions/list') {
          dispatch({type: 'fetch'});
        }
      });
    },
  },
  effects: {
    *fetch({ payload }, {call, select, put}) {
      const { result } = yield call(apis.fetchTxs)
      yield put({type:'addTx', payload: result})
    },
  },
  reducers: {
    addTx(state, { payload }) {
      return { ...state, txs: payload };
    },
  },
}
复制代码

7个API

dva只有7个API,所以上手基本上没有什么难度:

  • app = dva(opts):创建dva对象
  • app.use(hooks):使用插件(后面介绍)
  • app.model(model):注册model
  • app.unmodel(namespace):取消model注册
  • app.replaceModel(model):替换model
  • app.router(({ history, app }) => RouterConfig):配置路由
  • app.start(selector?):启动应用(参数是根组件id)

下面这个链接展示了5步创建单页应用的例子: github.com/sorrycc/blo…

4大模块

这个主要是从代码结构上来划分的,一般分为下面4类模块:

  • service:执行异步任务的API
  • model:包含effect, reducer, subscription
  • component:无状态组件
    • 发送action触发状态更新
    • action = 'namespace/effect'或者'namespace/reducer'
  • route:各种container(这个名字取得不太好)
    • 通过connect()连接到model

另外,由于dva 1.0使用的是react-router v3,所以最外层还有一个router.js用于配置静态路由。所以一般的目录结构如下所示:

+ src/
  + services/
    - users.js
  + models/
    - users.js
  + components/
    + users/
      - users.js
      - users.css
  + routes/
    - users.js
  - router.js
复制代码

dva 2.0中采用了react-router v4,就不需要router.js了。另外,现在官方推荐搭配使用umi(乌米,sorrycc最新的开源项目),可以自动帮你注册model、根据目录结构生成路由配置,目录结构会变成下面这个样子:

+ src/
  + models/
    - global.js
  + pages/
    + users/
      + index.js
      + services
        - users.js
      + models/
        - users.js
  + components/
    + users/
      - users.js
      - users.css
复制代码

可以发现,把route以及相关联的model都放到pages目录中了,可以达到减少耦合,一删全删的功能。

插件(Plugin)

前面提到过一个API可以注册“插件”,所谓插件就是一些生命周期“钩子(hooks)”,可以在事件发生时做一些额外处理。包括下面这些类型的钩子:

  • onError
  • onStateChange
  • onAction
  • onHmr:热替换(Hot Module Replacement)
  • onReducer:reducer被调用
  • onEffect:effect被调用
  • extraReducers:额外的reducer被调用(比如搭配合redux-form)
  • extraEnhancers:额外的store enhancer被调用(比如搭配redux-persist)

一个比较典型的例子是页面加载数据时转圈圈,加载完毕后隐藏,这是一个很多地方都需要用到的场景。官方提供了一个dva-loading插件,可以自动在异步请求数据时把loading设置为true,获得数据后把loading设置为false。这样你就可以在组件中绑定loading实现转圈圈的自动控制了:

// index.js
import createLoading from 'dva-loading';
app.use(createLoading());

// components
function mapStateToProps(state) {
  const { list, total, page } = state.users;
  return {
    loading: state.loading.models.users,
    list,
    total,
    page,
  };
}
复制代码

和redux+saga的对应关系

虽然dva只是一层轻量级封装,但是做了一些特殊的命名约定,刚开始写代码的时候会有点迷糊,搞不清楚跟之前直接使用redux+saga的时候的对应关系,这里也帮大家梳理一下。

直接使用redux+saga的流程如下所示:

  • component发送请求action
  • saga调用service,然后put一个结果action
  • reducer从action中获取type和payload,修改state(可以使用combinedReducer,会依次遍历)
  • component在mapStateToProps()中通过state.<reducer名字>获取更新

使用dva时的流程如下所示:(触发effect为例)

  • component发送请求action,type约定为namespace/effect
  • effect调用service,然后put一个结果action,type约定为namespace/reducer(同一个model中可以省略namespace)
  • reducer从action中获取type和payload,修改state(每个reducer只处理一种类型的action,type约定为reducer的名字)
  • component在mapStateToProps()中通过state.<namespace>获取更新

dva2.0的变动

目前dva已经进化到2.0版本,除了采用了react-router v4以外,还有一些细节上的变动:

  • dispatch一个effect 类型的action时返回一个Promise,方便视图层回调

  • 新增 dynamic 接口,配合 react-router@4 处理组件的按需加载

  • take 自动补全 namespace 前缀

  • effect 前后会额外触发 /@@start/@@end 的 action,可利用此约定实现 put 的同步执行

  • 同名 reducer 和 effect 不会 fallthrough(即两者都执行),而是仅执行 effect

具体细节可以参见: github.com/sorrycc/blo…

今天就说到这里,老规矩,上一张思维导图结束本篇文章:

前端技术 | dva,美貌与智慧并存

参考:

dvajs.com/guide

github.com/dvajs/dva/i…

www.github.com/sorrycc/blo…

github.com/sorrycc/blo…

github.com/sorrycc/blo…

juejin.im/post/5b93c0…

github.com/sorrycc/blo…

segmentfault.com/a/119000001…


以上所述就是小编给大家介绍的《前端技术 | dva,美貌与智慧并存》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Two Scoops of Django

Two Scoops of Django

Daniel Greenfeld、Audrey M. Roy / CreateSpace Independent Publishing Platform / 2013-4-16 / USD 29.95

Two Scoops of Django: Best Practices For Django 1.5 is chock-full of material that will help you with your Django projects. We'll introduce you to various tips, tricks, patterns, code snippets, and......一起来看看 《Two Scoops of Django》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换