New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

使用 umi 改进 dva 项目开发 #66

Open
sorrycc opened this Issue Mar 30, 2018 · 37 comments

Comments

Projects
None yet
@sorrycc
Copy link
Owner

sorrycc commented Mar 30, 2018

最近给 antd-admin 提了迁移到 umi 的 PR,过程中发现使用 umi + umi-plugin-dva 的方式在代码组织、可维护性等方面相比之前都有不少提升,在这里介绍给大家。

Duck Directory

大家都说命名是编程中最难的事情之一,我觉得目录结构组织也差不了多少。

Duck Directory 可以看下图理解下,是指按功能/页面维护进行目录组织,与之相对的是一种扁平的目录组织形式。

dva 项目之前通常都是这种扁平的组织方式,

+ models
  - global.js
  - a1.js
  - a2.js
  - b.js
+ services
  - a.js
  - b.js
+ routes
  - PageA.js
  - PageB.js

用了 umi 后,可以按页面维度进行组织,

+ models/global.js
+ pages
  + a
    - index.js
    + models
      - a1.js
      - a2.js
    + services
      - a.js
  + b
    - index.js
    - model.js
    - service.js

好处是更加结构更加清晰了,减少耦合,一删全删,方便 copy 和共享。

自动注册 models

详见 umijs/umi#171

+ src
  + models
    - g.js
  + pages
    + a
      + models
        - a.js
        - b.js
        + ss
          - s.js
      - page.js
    + c
      - model.js
      + d
        + models
          - d.js
        - page.js
      - page.js

如上目录:

  • global model 为 src/models/g.js
  • /a 的 page model 为 src/pages/a/models/{a,b,ss/s}.js
  • /c 的 page model 为 src/pages/c/model.js
  • /c/d 的 page model 为 src/pages/c/model.js, src/pages/c/d/models/d.js

再见!router.js

改造前,

改造后,

再见!query-string

umi 内置的 history 是处理了 location.query 的,所以大家可以回到 dva@1 的时代,无需手动同 query-string 进行编码和解码了。

再见!配置文件

首先,我们的 package.json 里会少很多依赖,

  • dva-loading
  • dva-hmr
  • dva
  • react
  • react-dom

如果你用了 antd,那么还可以省掉

  • antd
  • antd-mobile
  • babel-plugin-import

然后,webpack.config.js、原 .roadhogrc.js、原 .roadhogrc.mock.js 也能大幅省略,参考 zuiidea/antd-admin@59e9a10

FAQ

如何配置 onError、initialState 等 hook?

新建 src/dva.js,通过导出的 config 方法来返回额外配置项,比如:

import { message } from 'antd';

export function config() {
  return {
    onError(err) {
      err.preventDefault();
      message.error(err.message);
    },
    initialState: {
      global: {
        text: 'hi umi + dva',
      },
    },
  };
}

url 变化了,但页面组件也刷新,是什么原因?

layouts/index.js 里如果用了 connect 传数据,需要用 umi/withRouter 高阶一下。

import withRouter from 'umi/withRouter';

export default withRouter(connect(mapStateToProps)(LayoutComponent));

如何访问到 store 或 dispatch 方法?

window.g_app._store
window.g_app._store.dispatch

如何禁用包括 component 和 models 的按需加载?

在 .umirc.js 里配置:

export default {
  disableDynamicImport: true,
};

umi 社区

钉钉群

微信群

扫码加 UMI_HELPER,回复 umi 自动加群。

参考

@wangyazhen

This comment has been minimized.

Copy link

wangyazhen commented Mar 30, 2018

@sorrycc

url 变化了,但页面组件也刷新,是什么原因?

应该是页面不刷新

@superlbr

This comment has been minimized.

Copy link

superlbr commented Mar 30, 2018

umi 内置的 history 是处理了 location.query 的,所以大家可以回到 dva@1

dva@1 是个啥用法

@zcSkr

This comment has been minimized.

Copy link

zcSkr commented Mar 30, 2018

@superlbr 就是正常使用,通过location.query获取到查询字符对象,而dva2.0中因为history升级的原因,取而代之的是location.search去获取查询字符串,umi内置了这个 解析字符串的功能

@superlbr

This comment has been minimized.

Copy link

superlbr commented Mar 30, 2018

明白,location.query 不需要queryString.parse(location.search)了,但是queryString.stringify貌似还是要用下。。。

@superlbr

This comment has been minimized.

Copy link

superlbr commented Mar 31, 2018

@sorrycc antd依赖由umi维护,那依赖升级怎么操作呢,是否有类似egg.js的autod?

@superlbr

This comment has been minimized.

Copy link

superlbr commented Mar 31, 2018

@sorrycc

This comment has been minimized.

Copy link
Owner

sorrycc commented Mar 31, 2018

antd依赖由umi维护,那依赖升级怎么操作呢,是否有类似egg.js的autod?

@superlbr 项目里有 antd 依赖会优先用项目里的。

@sl5310

This comment has been minimized.

Copy link

sl5310 commented Jul 4, 2018

新建 src/dva.js,通过导出的 config 方法来返回额外配置项
可以在具体一些吗?加入了没有反应

@xiaohuoni

This comment has been minimized.

Copy link

xiaohuoni commented Jul 4, 2018

@sl5310 能具体描述一下你的问题,或者给一个不可行的例子吗?

@sl5310

This comment has been minimized.

Copy link

sl5310 commented Jul 4, 2018

@xiaohuoni 我在src目录下加了dva.js

import { message } from 'antd';

export function config() {
	return {
		onError(err) {
			err.preventDefault();
			message.error(err.message);
		},
	};
}

也重新跑了npm start 是否还需要加入其他东西呢?
在生成DvaContainer.js 并没有看见onError被加入,
我也尝试在src/plugin加入onError.js 加入以上的code,也没有看见plugin自动载入去DvaContainer.js

@superbervin

This comment has been minimized.

Copy link

superbervin commented Jul 26, 2018

@sorrycc 谦神,能不能给antd-design-pro也提个pr,把umi搞进来

1 similar comment
@kaoding

This comment has been minimized.

Copy link

kaoding commented Jul 28, 2018

@sorrycc 谦神,能不能给antd-design-pro也提个pr,把umi搞进来

@iamweilee

This comment has been minimized.

Copy link

iamweilee commented Aug 14, 2018

umijs的目录组织结构很不错,和大神想到一块了

@littleone2018

This comment has been minimized.

Copy link

littleone2018 commented Aug 17, 2018

我觉得以页面为划分维度是不合适的,因为同一块数据可能会在不同的页面上都展示,那这块数据的业务逻辑要写两套吗?列表和对应详情是两个页面,但显然它们应该属于一块service和model。数据和页面之间不是一一对应的,一个页面可能有好几块数据,一块数据也可能分散到好多个页面,以页面为维度会带来冗余和复杂。

@superlbr

This comment has been minimized.

Copy link

superlbr commented Aug 17, 2018

@onecompany
service和model可以share,直接require。可以public,放services文件夹
services文件夹不一定要跟pages对应呀,可以自己梳理逻辑。
更不必说redux里可以操作不同model的数据

@littleone2018

This comment has been minimized.

Copy link

littleone2018 commented Aug 17, 2018

@superlbr
按你方案碰到“一个页面可能有好几块数据,一块数据也可能分散到好多个页面”这种情况,它们之间的调用会非常复杂的。

模块化的目的是每个模块是独立的,很少要调用其他模块。

模块的划分应该以业务和数据域为依据,页面只是ui而已,它后期的变化可能会很大,以它为维度是难以维护的。

@superlbr

This comment has been minimized.

Copy link

superlbr commented Aug 17, 2018

@onecompany

一个页面可能有好几块数据

可以把一个页面当成一个模块去处理,pages里service可以放好几块调用的声明

一块数据也可能分散到好多个页面

可以public,services下建文件夹或者放global.js

它后期的变化可能会很大,以它为维度是难以维护的。

页面按模块组织,页面增减直接增删文件夹,public的部分视情况增减就ok

总体来说,考虑到了灵活性和颗粒度

@littleone2018

This comment has been minimized.

Copy link

littleone2018 commented Aug 17, 2018

@superlbr

可以把一个页面当成一个模块去处理,pages里service可以放好几块调用的声明

好,比如这个页面含5块数据;如果又有一个页面含这个5个中的3个,又有一个模块含这5个中的4个, 并且它们的数据字段详细程度还不一样(详情级别和快照级别)..., 于是很多页面都要调用这个页面下的service,并且这个service还要不断的维护,这就太混乱了!

可以public,services下建文件夹或者放global.js

如果这样的数据比较多,那这个global无疑会变大很大,很难维护。

@littleone2018

This comment has been minimized.

Copy link

littleone2018 commented Aug 17, 2018

再加上如果后期页面的展示内容要大改呢!

@superlbr

This comment has been minimized.

Copy link

superlbr commented Aug 17, 2018

@onecompany

比如这个页面含5块数据;如果又有一个页面含这个5个中的3个...

那可以认为这几个页面是相关的,放在同一个文件夹下,即pages/[A/B/C/services/models],
ABC按需求require service/model

数据字段详细程度还不一样(详情级别和快照级别)

控制传参

如果这样的数据比较多,那这个global无疑会变大很大,很难维护

做好注释和说明,应该还好把

如果后期页面的展示内容要大改呢

没有银弹吧。。。数据流改改能用吗

@littleone2018

This comment has been minimized.

Copy link

littleone2018 commented Aug 17, 2018

这种不可以简单的认为是相关的,比如这个页面含5块数据;如果又有一个页面含这个5个中的3个,但它同时又含有另外的7块数据呢,它要和谁相关??这个两个页面本来是平行的,你不能写成嵌套的。

不能简单的控制传参,因为后端可能不是通过传参,而是给两个不同的端点。

如果以数据为维度是不会有这些问题的。因为它根本不考虑页面,完全可以在不写页面的情况下,写完整个业务逻辑。

@superlbr

This comment has been minimized.

Copy link

superlbr commented Aug 17, 2018

如果以数据为维度...

请问数据怎么解决这些问题?跟现有model/service有什么差异吗?
不考虑页面?那页面怎么组织?

@littleone2018

This comment has been minimized.

Copy link

littleone2018 commented Aug 17, 2018

+artilces
-sevices
-models
+components
-Item.js
-List.js

service和modles包含了article这块数据的所有增删改查操作,Item和List是纯ui组件,只接受数据。

所谓的页面,就是把它们组合起来就行了,dva基本是这个思路,你可以看看它的hacknews项目。

@superlbr

This comment has been minimized.

Copy link

superlbr commented Aug 17, 2018

@onecompany

看不出任何差别,umi + dva, 就是这样的呀

@littleone2018

This comment has been minimized.

Copy link

littleone2018 commented Aug 17, 2018

我articles不是pages里面的,它是一个独立的模块,和具体的页面无关。

@superlbr

This comment has been minimized.

Copy link

superlbr commented Aug 17, 2018

@onecompany
那跟services下面建articles, models下面建articles 一样呗。umi的pages可以放模块呀,配置式路由。
既然是独立模块,那得指定路由/指定渲染,而且各种require UI组件,可维护性高?遇到删功能、删模块、删组件,一发动全身

@littleone2018

This comment has been minimized.

Copy link

littleone2018 commented Aug 17, 2018

模块化是一个大的主题,你可以看看相关文章。

@superlbr

This comment has been minimized.

Copy link

superlbr commented Aug 17, 2018

@onecompany 脱离业务谈模块有点皮啦。考虑到umi毕竟是一个开源框架,得通用呀

@superbervin

This comment has been minimized.

Copy link

superbervin commented Aug 17, 2018

@onecompany
@superlbr
https://github.com/dvajs/dva-docs/blob/master/v1/zh-cn/tutorial/03-%E8%AE%BE%E8%AE%A1Model.md

看两位大佬讨论受益匪浅,dva的文档有模块组织的建议,但是讨论的场景比较简单。模块化确实是一个大主题,@sorrycc 不知道谦谦有没有什么好的文章或者建议给到我们

@superlbr

This comment has been minimized.

Copy link

superlbr commented Aug 31, 2018

@onecompany why not angular

@OPY-bbt

This comment has been minimized.

Copy link

OPY-bbt commented Sep 4, 2018

想问一下 根据目录结构生成route.js 的话 如何处理当url不同却指向同一个component的问题。我现在遇到了这么一个情况,两个route之间差别不大,按照原来的方法,我可以配置不同路径对应相同component,但是现在的话应该这么做?

@gluefxu

This comment has been minimized.

Copy link

gluefxu commented Sep 11, 2018

请问page js 访问page model要怎样实现呢?如果还是在页面里实现mapStateToProps然后connect,那跟原来一样啊,而且访问都是所有的state数据。
另外,文档中提到的“页面 model 不能被其他页面所引用”到底是一种建议的约定还是实际的技术限制呢?

@xiaohuoni

This comment has been minimized.

Copy link

xiaohuoni commented Sep 11, 2018

@gluefxu 页面model是按需加载的,访问的时候,所有的state并不一定是所有的。至于另一个问题,会有重复引用的问题

@sl5310

This comment has been minimized.

Copy link

sl5310 commented Sep 11, 2018

@xiaohuoni 我现在有2个页面model,一个叫dashboard,一个叫product,如果dashboard的model已经加载了,我在product page的时候connect起dashboard model,一样可以获取dashboard的state,这是正常的吗?所以文档中提到的“页面 model 不能被其他页面所引用”是一种建议的约定吗?

@amenzai amenzai referenced this issue Sep 13, 2018

Closed

testmd #60

lanweifeng pushed a commit to lanweifeng/yygj that referenced this issue Sep 15, 2018

@stoneWeb

This comment has been minimized.

Copy link

stoneWeb commented Sep 19, 2018

请问目录约定的路由形式如何配置browserHistory的base路径?

@xiaohuoni

This comment has been minimized.

Copy link

xiaohuoni commented Sep 20, 2018

@sl5310 我一样的,你这么用,要是页面加载顺序改变的话,这个就会出错了。

@moxunhzb

This comment has been minimized.

Copy link

moxunhzb commented Dec 18, 2018

用umi脚手架创建项目,插件也配置了,connect后,dispatch正常,但是报put,call不是function的错,怎么回事?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment