Skip to content
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

在基于route 做了 code splitting 的项目使用会出问题 #2

Closed
uglyspoon opened this issue Aug 1, 2019 · 4 comments
Closed

Comments

@uglyspoon
Copy link

其实更准确的描述是,

useModel这个方法依赖于在这之前已经setModel过,

比如我在 路由 /couter模块中使用了useModel('home'),但是setModel('Home')声明在/home

直接访问/couter就会出错,因为这时候/home还没有挂载,感觉这样的话还是要在顶层容器中集中遍历setModel下?这样就不是去中心化了,作者有没有考虑这方面怎么解决呢?因为在实际项目中, 基于路由的代码分割还是挺常见的

@nanxiaobei
Copy link
Owner

nanxiaobei commented Aug 1, 2019

考虑过这个问题。

如果做 code splitting 的话,就只能把需要的 model 在组件中都引入,然后 setModel() 注册,比如 home、counter 等,没什么特别好的解决方案。

所以这里只能推荐在项目设计上,把公共的部分提取成一个 "common" model,在上层引入,减少不同模块的相互依赖。

现在如果一个 model 在不同组件中注册,会进行提示,后续会考虑去除这个提示~

@uglyspoon
Copy link
Author

uglyspoon commented Aug 2, 2019

谢谢答复。
刚才特意去试了下dva里面的model设计,通过封装的redux-saga库的effects里面put关键字获取其他namespace的model,发现也是要先加载后,才会出现对应模块的model,
所以在有useModel('other')先保证已经存在就好了,先尝试在项目用一下吧,哈哈~

@uglyspoon
Copy link
Author

初步尝试了下我还是感觉setModel 这个用起来有点不舒服,更期望能和dva一样有个namespace类似的字段,来区分不同的模块,在加载这个model的时候就自动setModel(namespace, this);

我觉得dva那样做有个好处,这个model.js无论在哪里加载,他的namespace都固定,模块化更清晰,而现在手动引入再setModel给予模块名的话,是更灵活了,(逻辑相同的两个模块甚至可以引入同一个model.js 然后set不同的名字去使用),
但是同时也就是您上面说的

现在如果一个 model 在不同组件中注册,会进行提示,后续会考虑去除这个提示~

我觉得如果setmodel用同样的名字应该是不被允许的,因为现在存在相同名字的model,state里面有相同的字段的话,修改的同时会影响另一个,这在使用的时候无感知的;

同时您在掘金上的文章提到,dva的dispatch写在业务代码里面会很丑,诚然它并不漂亮,但是还是很清晰的,type指向了 那个模块的model及对应的action,payload紧随其后,而且最重要是一看到这个dispatch马上能反映到这部分的逻辑被放到了model里,逻辑分离的好的话,相当于分离了个service层出去,

flooks中 这部分道理和dva一致,甚至写起来不需要generate的写法,简洁,但是action是函数,名字定义随意一点的话,和普通的业务逻辑的函数是差不多的,

//page.jsx
//使用的时候
 const { count, increment, decrement } = useModel('home');

 useEffect(() => {
    increment(payload)
  }, [])

//假如我页面上也有个 const increment = xxxx
项目再越来越大的时候就会混在一起,分不清哪些是页面本身的函数,哪些是放在model处理了,
但是在dva

  useEffect(() => {
    dispatch({
      type: 'profile/increment',
      payload: { }
    })
  }, [])

不知道我表达清楚意思没有,就是dispatch虽然很丑,写起来很长

好吧写到这里我觉得这点有点那个了,名字定义好还是可以规避的;就当我没说吧 0.0

@nanxiaobei
Copy link
Owner

nanxiaobei commented Aug 3, 2019

感谢建议~

1.

setModel() 一开始叫 createModel(),确实考虑过 setModel(model) 的方案,然后 model 中添加 name 字段,这样设计与现有设计各有利弊。

现在这样显示声明 setModel(name, model) 中的 name,是为了让 useModel(name) 中 name 的来源更加「直观」,同时 model 中就只有 stateactions 两个字段,更「清晰」。

当然,加 namespace 的方案也有好处,只是觉得从 API 互相的「连贯性」上来说可能不太直观。想用 namespace 的方案,其实可以直接在 model.js 文件中:

export default setModel('someModel', {
  state: {}
  actions: ({ model, setState }) =>({})
})

然后组件中 import './model.js'; 即可。

2.

「someModel 已存在」的提示已经去掉,主要是如果启用了 HMR,每次热更新都触发函数调用,提示就很多余。

setModel('someModel', model) 是可以在不同组件中都调用的,是为了支持本 issue 一开始提到的 code splitting 的情况。只要 name 一样,注册的都是"第一次"调用时注册的那个 model,后面的调用会忽略。

其实,同一个 model 文件比如 x,setModel('a', x)setModel('b', x) 之后,只要更新数据不是 obj.foo.bar = 123(深层数据),数据就不会互相影响,因为每次 setState() 都是给 state 一个新的对象,它会变成 "a" 和 "b" 两个 model,相当于一个 model 用于两个模块,算是一个 hack feature。

3.

dva 的显式调用 dispatch 的思路,各有利弊。Redux 也提供了 bindActionCreators() 函数,以供直接调用函数,而不是用 dispatch 调用,所以没有一定"对"的设计,只是思路的倾向。

加上 ESLint,函数组件中,命名冲突会报错。另外就是,其实可以这样使用 useModel()

const modelA = useModel('modelA');
const modelB = useModel('modelB');

// modeA.count
// modelA.increment()

不把 state 和 action 解构出来,这也算一种对不同变量加以区分的思路~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants