原文链接-https://github.com/AlloyTeam/omi

Store 体系

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

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

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

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

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

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

class Main extends Omi.Component {

    handlePageChange(index){
this.content.goto(index+1)
this.update()
} render () {
return `<div>
<h1>Pagination Example</h1>
<Content name="content" />
<Pagination
name="pagination"
:data-total="100"
:data-page-size="10"
:data-num-edge="1"
:data-num-display="4"     
onPageChange="handlePageChange" />
</div>`;
}
}

上面的例子中,

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

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

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

虽然组件通讯非常方便,但是在真实的业务场景中,不仅仅是父子、爷孙、爷爷和堂兄、嫂子和堂弟...

onXxx="xxxx"就显得无能为力,力不从心了,各种数据传递、组件实例互操作、 emitter/pubsub或者循环依赖,让代码非常难看且难以维护。所以:

Omi.Store是用来管理共享数据以及共享数据的逻辑 。

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

定义 Omi.Store

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

import Omi from 'omi'

class TodoStore extends Omi.Store {
constructor(data , isReady) {
super(isReady) this.data = Object.assign({
items:[],
length:0
},data) this.data.length = this.data.items.length
} add(value){
this.data.items.push(value)
this.data.length = this.data.items.length
this.update()
} clear(){
this.data.items.length = 0
this.data.length = 0
this.update()
}
} export default TodoStore

TodoStore定义了数据的基本格式和数据模型的逻辑。

比如 this.data 就是数据的基本格式:

{
items:[],
length:0
}

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

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

创建 Omi.Store

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

let 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:

Omi.render(new Todo(),'body',{
store: store
});

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

Omi.render(new Todo(),'body',{
store
});

两份代码同样的效果。

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

利用 beforeRender

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

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

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

import Omi from '../../src/index.js';
import List from './list.js'; Omi.makeHTML('List', List); class Todo extends Omi.Component {
constructor(data) {
super(data)
} install(){
this.$store.addView(this)
} beforeRender(){
this.data.length = this.$store.data.items.length
} add (evt) {
evt.preventDefault()
let value = this.data.text
this.data.text = ''
this.$store.add(value)
} style () {
return `
h3 { color:red; }
button{ color:green;}
`;
} clear(){
this.data.text = ''
this.$store.clear()
} handleChange(evt){
this.data.text = evt.target.value
} render () {
return `<div>
<h3>TODO</h3>
<button onclick="clear">Clear</button>
<List name="list" data="$store.data" />
<form onsubmit="add" >
<input type="text" onchange="handleChange" value="{{text}}" />
<button>Add #{{length}}</button>
</form> </div>`;
}
} export default Todo;

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

注意看上面的:

    install(){
this.$store.addView(this)
}

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

再看上面的子组件声明:

<List name="list" data="$store.data" />

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

class List extends Omi.Component {
constructor(data) {
super(data)
} render () {
return ` <ul> {{#items}} <li>{{.}}</li> {{/items}}</ul>`
}
}
这里需要特别强调,不需要把所有的数据提取到store里,只提取共享数据就好了,组件自身的数据还是放在组件自己进行管理。

异步数据

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

let todoStore = new TodoStore()
setTimeout(()=>{
todoStore.data.items = ["omi","store"];
todoStore.data.length = todoStore.data.items.length
todoStore.beReady();
},2000)

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

class Todo extends Omi.Component {
constructor(data) {
super(data)
} install(){
this.$store.addView(this)
} installed(){
this.$store.ready(()=>this.$store.update())
} add (evt) {
evt.preventDefault()
if(!this.$store.isReady){
return
}
let value = this.data.text
this.data.text = ''
this.$store.add(value)
}

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

源码地址

相关

Omi框架Store体系的前世今生的更多相关文章

  1. Omi教程-插件体系

    插件体系 Omi是Web组件化框架,怎么又来了个插件的概念? 可以这么理解: Omi插件体系可以赋予dom元素一些能力,并且可以和组件的实例产生关联. omi-drag 且看这个例子: 点击这里→在线 ...

  2. Omi框架学习之旅 - 之开篇扯蛋

    说实话, 我也不知道Omi是干啥的, 只因此框架是alloyTeam出的, dntzhang写的, 也有其他腾讯大神参与了, 还有一些其他贡献者, 以上我也不太清楚, 当我胡说八嘎. 因其写法有人说好 ...

  3. Omi框架学习之旅 - Hello World 及原理说明

    学什么东西都从hello world开始, 我也不知道为啥. 恩,先上demo代码, 然后提出问题, 之后解答问题, 最后源码说明. hello world - demo: class Hello e ...

  4. Omi框架学习之旅 - 插件机制之omi-router及原理说明

    先来看看官网的介绍吧:https://github.com/AlloyTeam/omi/tree/master/plugins/omi-router 其实我推荐直接看官网的介绍.我所写的,主要给个人做 ...

  5. Omi框架学习之旅 - 插件机制之omi-finger 及原理说明

    以前那篇我写的alloyfinger源码解读那篇帖子,就说过这是一个很好用的手势库,hammer能做的,他都能做到, 而且源码只有350来行代码,很容易看懂. 那么怎么把这么好的库作为omi库的一个插 ...

  6. Omi框架学习之旅 - 组件 及原理说明

    hello world demo看完后其实基本的写法就会了. 但是omi中的组件是神马鬼?其实我也不知道组件是啥. 百度百科是这么说的: 是对数据和方法的简单封装.es6中,一个类其实也可以做到对方法 ...

  7. [javaSE] 集合框架(体系概述)

    为什么出现集合类 为了方便对多个对象的操作,对对象进行存储,集合就是存储对象最常用的一种方式 数组和集合的不同 数组是固定长度的,集合是可变长度的 数组可以存储基本数据类型,集合只能存储对象 数组只能 ...

  8. Omi框架学习之旅 - 插件机制之omi-touch 及原理说明

    这个插件也能做好多好多的事,比如上拉下拉加载数据,轮播,等一切和运动有关的特效. 具体看我的allowTouch这篇博客,掌握了其用法,在来看它是怎么和omi结合的.就会很简单. 当然使用起来也比较方 ...

  9. Omi框架学习之旅 - 插件机制之omi-transform及原理说明

    给omi-transform插件做个笔记,使用起来也很爽. transform.js这个库,一直想写一篇帖子的,可是,数学不好,三维矩阵和二位矩阵理解的不好,所以迟迟没写了, 这也是一个神库,反正我很 ...

随机推荐

  1. [html] 学习笔记-Canvas图形绘制处理

    使用Canvas API 可以将一个图形重叠绘制在另外一个图形上,也可以给图形添加阴影效果. 1.Canvas 图形组合 通过 globalCompositeOperation = 属性 来指定重叠效 ...

  2. redis&rabbitMQ安装

    前言: 学习python已经有一段时间了,最近在学twisted(博客:twisted安装),redis,rabbitMQ感觉有点难度,所以还是写下博客整理下. 一.Redis的安装 redis是一种 ...

  3. 法国总统放大招,用“分身术”竞选总统 全息3d 网

    编辑:大熊 [摘要]法国总统采用全息技术实现"分身"演讲,可谓是一次演讲,全面覆盖! 全息3d网讯:众所周知,欧美国家的总统是通过公开竞选得到的,所以能更直接.更广泛的近距离接触民 ...

  4. SQL Server 安装报错找不到vc_red.msi

    问题描述: 今天给 WIN 7 SP1 操作系统安装 SQL Server 2014 ,报错找不到vc_red.msi   (图片来源网络,请忽略2012字样..)   问题解决: 1.由于安装程序提 ...

  5. spring事件驱动模型--观察者模式在spring中的应用

    spring中的事件驱动模型也叫作发布订阅模式,是观察者模式的一个典型的应用,关于观察者模式在之前的博文中总结过,http://www.cnblogs.com/fingerboy/p/5468994. ...

  6. MySql学习(七) —— 查询性能优化 深入理解MySql如何执行查询

    本篇深入了解查询优化和服务器的内部机制,了解MySql如何执行特定查询,从中也可以知道如何更改查询执行计划,当我们深入理解MySql如何真正地执行查询,明白高效和低效的真正含义,在实际应用中就能扬长避 ...

  7. 纯css 构造的tip

    css部分: <style>   .abc{ margin-top:20px; } span{ position:relative; display: inline-block; back ...

  8. 用qemu搭建aarch64学习环境

    作者信息 作者: 彭东林 邮箱: pengdonglin137@163.com QQ: 405728433 软件平台 主机: Ubuntu14.04 64位版本 模拟器:Qemu-2.8.0 Linu ...

  9. js五种设计模式说明与示例

    第一种模式:js工厂模式    var lev=function(){        return "啊打";      };      function Parent(){    ...

  10. MySQL主从复制与主主复制

    1.简介 MySQL作为世界上使用最为广泛的数据库之一,免费是其原因之一.但不可忽略的是它本身的功能的确很强大.随着技术的发展,在实际的生产环境中,由单台MySQL数据库服务器不能满足实际的需求.此时 ...