Skip to content
This repository has been archived by the owner on Nov 12, 2024. It is now read-only.

magix中store的设计 #36

Closed
xinglie opened this issue Jun 16, 2017 · 11 comments
Closed

magix中store的设计 #36

xinglie opened this issue Jun 16, 2017 · 11 comments

Comments

@xinglie
Copy link
Member

xinglie commented Jun 16, 2017

magix view的mixin可以很方便的复用独立的功能片断。同时也可以被merge到view的原型链上被整个项目的view使用,但该情况可能会导致代码不清晰,因此不建议全局使用。

数据存储共享对象Magix.Store被设计为mixin,可以通过view的mixins属性很方便的被某几个view使用。

编写store

// app/stores/count
let Magix = require('magix');
module.exports = Magix.Store.extend({
    init() {
        console.log('store init');
        return {
            count: 1
        };
    },
    load() {
        let me = this;
        return new Promise((resolve) => {
            setTimeout(() => {
                console.log(me.get('count'));
                resolve({
                    count: me.get('count') + 1
                });
                console.log('xxxx', me.get('count'));
            }, 2000);
        });
    },
    increase() {
        return this.load();
    }
});

使用store

let Magix = require('magix');
let Store = require('./store');
module.exports = Magix.View.extend({
    tmpl: '@test.html',
    mixins: [Store],
    render() {
        this.store.dispatch('load');
    }
});

test.html模板

count:<%=count%>

通过mixins属性挂在view上,则view会有一个store属性,所有与数据相关的操作均放在store中
如果当前view你使用了store,则非常不建议再使用updater,所有数据均需要放在store中

在view中,只需要通过store.dispatch调用store中定义好的方法,界面就会自动渲染。

关键方法与返回值

init

init方法会在加载并首次初始化的时候调用一次

load

load方法分2种情况讨论

  1. 如果该方法在store内部被调用,如该示例中的increase方法中调用load,则是普通使用
  2. 如果该方法在view中使用store.dispatch调用,则不管dispatch多少次,也不管是多个view进行dispatch,该方法只会被调用一次。该设计有利于多个view依赖同一份数据,但这一份数据无需反复请求服务器

方法返回值

init方法需要返回一个对象表示初始化的数据,其它方法包括load方法,需要返回一个Promise,即使不是异步的情况。返回值只能是一个对象

向store中存储共享数据

1. 如果在store内部,只需要定义自己需要的方法,返回promise,在resolve时把数据传递进来即可
2. 如果在view中,则使用如this.store.dispatch('yourMethod',{data:data});通过dispatch调用你在store中定义好的方法,把数据传递进去,而你的yourMethod返回一个promise即可

向store中存储当前view的数据

直接调用store的set方法,如this.store.set({data:data});获取数据则使用this.store.get('data');
通过set方法设置的数据只跟当前view有关,并不是共享型数据,其它view无法获取该view set的数据

渲染界面

界面会自动渲染,在渲染界面时,每个view使用的数据是store中共享的数据+view自身set上的数据。

@youwenda
Copy link

看完后有几个问题:

1、通过mixins store 方式是所有使用共享store的view都需要mixin,还是只需要在根view mixin一下,然后其他view通过类似this.context.store去访问。

2、在模板中如何使用例子中count数据(这个count应该是共享数据吧),另外如果当前view设置了自身的count怎么和全局区分开。

3、* 如果当前view你使用了store,则非常不建议再使用updater *, store是默认应用了updater了么,如果是的话,是应用store就必须用updater了么。

暂时先想到这些问题。

@xinglie
Copy link
Member Author

xinglie commented Jun 17, 2017

@youwenda
目前只是一个初步想法,仍需要完善,先针对你的问题回复一下
1. 目前我的想法是store可以有多个,当然也有建议说一个项目中只有一个。就目前magix view的核心设计来说:view之间应尽可能的独立,这样才更方便的被复用。我们在项目中当前view需要什么数据就自己去取什么数据,这种方式在绝大多数情况下都很好,也不需要所谓的store来管理数据,很多时候我觉得数据放在view中就好。只有很少的情况有几个view依赖同一份数据的情况,只有这种情况下才会用到store。
BTW:像view扩展我觉得是不太建议使用了,因为这会让view无形的过多的依赖外部环境,当某个view从一个项目拿到另外一个项目中时,很难直接跑起来,这是和我们最初的目标不符的。所以整个项目一个store的情况也是如此,让view无形中依赖这个store。

所以目前我的想法是项目中store可以有多个,view根据自己的情况mixin store,即你说的前一种形式。至于根view mixin一下这个也是非常方便的能做到,只是根据我们的项目情况(通常都很庞大和复杂),建议先少量试用,没问题再推开使用。

2. 因为一个store可以被多个view使用,就存在2种数据,一种是store中存在的对所有view都共享的数据,一种是各自view自身的私有数据。
共享的数据我是这样设计的:写在store中的才是共享的,即示例中load或increase方法promise resolve的数据,这样的数据才是共享的。
私有的数据在view中通过调用store.set方法设置的数据为当前view私有的。多个view set同一个key的数据并不会冲突。即多个view如果调用同样的this.store.set({viewId:this.id});在store中是分别存放的。

例子中,模板直接输出<%=count%>即可,昨天漏写模板了,已经补上了。在模板中可以直接输出共享数据和私有数据。

如果view自身也设置了count,目前是设置count的view使用自身的count,未设置的view使用共享中的count,这种同名的暂没什么好办法,先由开发者避免吧

3. 因为store中的数据变化会自动更新界面,所以就要求背后一定要用updater。另一个原因是store和updater都有存放数据的能力,如果在同一个view中,一会数据放updater中,一会放store中,看上去也较乱。所以统一用一个就好

@youwenda
Copy link

恩恩 了解啦。感谢行老师耐心讲解。

先占个坑记录一下吧

1、同名的问题可以在store set时存储在一个特定的对象空间里面,比如由viewId开辟的一片空间。。。
2、store dispatch 时可不可以指定更新view的方式,默认updater去更新,如果想用vue 或其他方式渲染,自己处理好了。
3、多个view mixins也挺好的,没有什么问题,同意

@xinglie
Copy link
Member Author

xinglie commented Jun 26, 2017

删除init方法

初始化似乎是一个不必要的,且可以在load方法内完成,先删除

独立更新store内的数据

有时候我们需要直接拿到store这个对象,然后存储一些数据,并希望这些数据影响到所有使用store的view,则可以使用store上的dispatch方法,如

let Store=require('app/stores/count');
Store.dispatch('increase');

这个看上去和通过view上的this.store.dispatch方法类似,但是对于load方法并不会特殊处理

多个store的依赖

实际开发中,如果全项目一个store,对于magix支持过的项目并不是一件好事。应该根据业务场景,拆分归类出多个store,这样就会出现同一个view可能依赖多个store,同时多个store间也可能有依赖。

store之间可以依赖,也就是可以通过一个store去使用其它的多个store,目前store的设计尽可能的类似普通模块,不能有太多的类似生命周期、与view绑死的方法名称等。

目前要求view只能使用一个store,当某个view需要多个store时,要新建一个store管理好多个store后,然后view只与这一个store关联即可。

其它问题

store与view更新:只要某一个store与view有关联,它的dispatch方法被调用且数据有变化后,关联的view就会自动更新,即使这个store被别的store依赖。

@xinglie
Copy link
Member Author

xinglie commented Jun 27, 2017

@purplebamboo
Copy link

多个store的问题

很担心,以后会变的混乱,尤其是store之间还有各种引用。
是否直接搞一个store就行了,而不是像现在这样每个store用init返回自己的独一份数据。是否store提供注册资源的方法。针对一个资源可以再添加各种操作函数。
view里面直接通过key进行绑定。

store的功能问题

store 我觉得不应该跟magix强绑定,目前看下来store会针对load特殊处理,会跟updater有依赖。

我觉得store不应该存储当前view数据,我觉得store应该只管公共状态的管理。view自己的数据自己存储就好了不应该放到store上。

应该是store接受状态变更(dispatch),管理后会调用view的render方法,至于render方法内部怎么渲染不应该去关心,不应该跟模板绑定。

  1. 考虑下面这个场景:
    render(){
    // 每次返回的数据针对当前这个view需要特殊处理
    // 这个处理可能是很特殊的,所以不适合在store获取数据时就处理

    var data = this.store.load()
    // 针对data的各种注入处理
    this.updater.set(data).digest()
    },
    'test':(){
    this.store.dispatch('load')
    }

按照现在的意思,在dispatch后会自动使用新的数据去 渲染,对开发者不透明。

  1. 还有两个dispach的问题。
    如果一个页面调用了 两个dispatch(不同store),那么会渲染两次?
    那先后顺序咋办。

综上模板渲染应该自己控制

@xinglie
Copy link
Member Author

xinglie commented Jun 27, 2017

1. 一个store看应用,像直通车、钻展一个铁定不行的
2. store的设计就是要store中的数据变化就会自动更新界面,这个前提是store中的数据会自动设置到各个view的updater数据管理对象里,至于是调用render方法更新还是直接调用view的updater更新,感觉都一样吧

@xinglie
Copy link
Member Author

xinglie commented Jun 30, 2017

重新设计

  1. magix提供一个基础的store供继承使用
  2. 继承时,开发者提供更改数据(获取数据、改写数据)的方法只能是返回promise的异步方法
  3. store只存储数据,不与view及view的updater强关联
  4. 更改数据时只能通过store.dispatch进行
  5. store提供与view关联的方法并可指定监听store中哪些数据的变化,且可指定数据变化后的更新方式。如store.link(view,['key1','key2'],(state)=>{ view.render() });
  6. 允许项目中多store,允许view关联多个store

@purplebamboo
Copy link

我觉得没什么大问题,多个store应对复杂情况的确更合适点,哈我们移动端就用精简版。

其他我觉得都没问题。主要是store怎么跟view绑定我们可以再想想,怎么更直观 方便

render方法直接默认缺省调用就行,当然也可以特殊定制。

所以这样用?
store = require(listStore)
magix.view.extend({
mixin:[store.export("key1","key2")]
这样这个view就会监听到变更,我们直接通过dispatch来修改数据

这么一弄 我觉得有点类似我们以前的路由的localchange了。。。

@xinglie
Copy link
Member Author

xinglie commented Jul 1, 2017

是有点像的,引起view渲染的情况可以是url改变,可以是数据改变,也可以是用户的交互等。xufei/blog#42
其实应该类似redux之类的,独立一个单纯的数据处理模块,然后再与view绑定

@xinglie
Copy link
Member Author

xinglie commented Jul 4, 2017

@keyapril 深入交流后,从整体流程到如异步发送、错误处理、请求控制等细节,现有的https://github.com/reactjs/redux 已非常好的满足需求,因此不再重复造轮子。后期如果真有需要,最多做一个magix-redux中间粘合层

如果项目中是轻量使用共享数据,可参考这里:#39

@xinglie xinglie closed this as completed Jul 4, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants