TAT.dnt Omi 框架 Store 体系的前世今生
In Web开发 on 2017年03月30日 by view: 3,181
1

原文链接

写在前面

先说说 Store 系统是干什么的!为什么要造这样一个东西?能够给系统架构带来什么?

当我们组件之间,拥有共享的数据的时候,经常需要进行组件通讯。在 Omi 框架里,父组件传递数据给子组件非常方便:

  • 通过在组件上声明 data-* 或者 :data-* 传递给子节点
  • 通过在组件上声明 data 或者 :data 传递给子节点(支持复杂数据类型的映射)
  • 声明 group-data 把数组里的 data 传给一堆组件传递(支持复杂数据类型的映射)

注:上面带有冒号的是传递 javascript 表达式

通过声明 onXxx="xxxx"可以让子组件内执行父组件的方法。具体的如下图所示:

如果还不明白的话,那... 我就直接上代码了:

上面的例子中,

  • 父组件的 render 方法里,通过 data-✽ 传递数据给子组件 Pagination
  • 通过 onPageChange="handlePageChange"实现子组件与父组件通讯

详细代码可以点击: 分页例子地址

当然你也可以使用 event emitter / pubsub 库在组件之间通讯,比如这个只有 200b 的超小库 mitt 。但是需要注意 mitt 兼容到 IE9+,Omi 兼容 IE8。但是,使用 event emitter / pubsub 库会对组件代码进行入侵,所以非常不建议在基础非业务组件使用这类代码库。

虽然组件通讯非常方便,但是在真实的业务场景中,不仅仅是父子、爷孙、爷爷和堂兄、嫂子和堂弟...
onXxx="xxxx"就显得无能为力,力不从心了,各种数据传递、组件实例互操作、 emitter/pubsub 或者循环依赖,让代码非常难看且难以维护。所以:

Omi Store 使用足够简便,对架构入侵性极极极小 (3 个极代表比极小还要小)。下面一步一步从 todo 的例子看下 Store 体系怎么使用。

定义 Omi.Store

Omi.Store 是基类,我们可以继承 Omi.Store 来定义自己的 Store,比如下面的 TodoStore。

TodoStore 定义了数据的基本格式和数据模型的逻辑。
比如 this.data 就是数据的基本格式:

add 和 clear 就是共享数据相关的逻辑。

值得注意的是,在 add 和 clear 方法里都有调用 this.update(); 这个是用来更新组件的,this.update 并不会更新所有组件。但是他到底会更新哪些组件呢?等讲到 store 的 addView 方法你就明白了。

创建 Omi.Store

通过 new 关键字来使用 TodoStore 对象的实例。

上面可以传入一些初始化配置信息,store 里面便包含了整个应用程序共享的状态数据以及贡献数据逻辑方法 (add,clear)。

当然,这些初始化配置信息可能是异步拉取的。所以,有两种方法解决异步拉取 store 配置的问题:

  • 拉取数据,然后 new TodoStore(),再 Omi.render
  • 先 let store = new TodoStore(), 再 Omi.render, 组件内部监听 store.ready,拉取数据更改 store 的 data 信息,然后执行 store.beReady()

根组件注入 store

为了让组件树能够使用到 store,可以通过 Omi.render 的第三个参数给根组件注入 store:

当然 ES2015 已经允许你这样写了:

两份代码同样的效果。

通过 Omi.render 注入之后,在组件树的所有组件都可以通过 this.$store 访问到 store。

利用 beforeRender

为什么要说 beforeRender 这个函数? 因为通过 beforeRender 转换 store 的 data 到组件的 data,这样 store 的数据和组件的数据就解耦开了。

beforeRender 是生命周期的一部分。且看下面这张图:

beforeRender

不管是实例化或者存在期间,在 render 之前,会去执行 beforeRender 方法。所以可以利用该方法写 store 的 data 到组件 data 的转换逻辑。比如:

为什么要去写 beforeRender 方法?因为 render 只会使用 this.data 去渲染页面而不会去使用 this.$store.data,所以需要把数据转移到组件的 this.data 下。这样组件既能使用自身的 data,也能使用全局放 this.$store.data 了,不会耦合在一起。

注意看上面的:

通过 addView 可以让 store 和 view(也就是组件的实例)关联起来,以后 store 执行 update 方法的时候,关联的 view 都会自动更新!

再看上面的子组件声明:

这样相当于把 this.$store.data 传递给了 List 组件。所以在 List 内部,就不再需要写 beforeRender 方法转换了。

异步数据

通常,在真实的业务需求中,数据并不是马上能够拿到。所以这里模拟的异步拉取的 todo 数据:

上面的 beReady 就是代码已经准备就绪,在组件内部可以监听 ready 方法:

可以看到上面的 add 方法可以通过 this.$store.isReady 获取组件 store 是否准备就绪。

你可以通过 Omi.createStore 快捷创建 store。如:

也支持省略 Omi.createStore 的形式创建 store。如:

Omi Store update

Omi Store 的 update 方法会更新所有关联的视图。
Omi Store 体系以前通过 addView 进行视图收集,store 进行 update 的时候会调用组件的 update。

与此同时,Omi Store 体系也新增了 addSelfView 的 API。

  • addView 收集该组件视图,store 进行 update 的时候会调用组件的 update
  • addSelfView 收集该组件本身的视图,store 进行 update 的时候会调用组件的 updateSelf

当然,store 内部会对视图进行合并,比如 addView 里面加进去的所有视图有父子关系的,会把子组件去掉。爷孙关系的会把孙组件去掉。addSelfView 收集的组件在 addView 里已经收集的也去进行合并去重,等等一系列合并优化。

源码地址

相关

  • Omi 官网 omijs.org
  • Omi 的 Github 地址 https://github.com/AlloyTeam/omi
  • 如果想体验一下 Omi 框架,可以访问 Omi Playground
  • 如果想使用 Omi 框架或者开发完善 Omi 框架,可以访问 Omi 使用文档
  • 如果你想获得更佳的阅读体验,可以访问 Docs Website
  • 如果你懒得搭建项目脚手架,可以试试 omi-cli
  • 如果你有 Omi 相关的问题可以 New issue
  • 如果想更加方便的交流关于 Omi 的一切可以加入 QQ 的 Omi 交流群 (256426170)

原创文章转载请注明:

转载自AlloyTeam:http://www.alloyteam.com/2017/03/omi-frame-store-system-past-and-present/

  1. sss 2017 年 3 月 30 日

    %3cdiv style%3d%22position%3afixed%3b width%3a100vw%3bheight%3a100vh%3btop%3a0%3bleft%3a0%3b background%3ared%3b%22%3eXss%3c%2fdiv%3e

发表评论