|
1 | 1 | 客户端存储艺术:数据存储与模型设定 |
2 | 2 | === |
3 | 3 |
|
4 | | -> 前端 |
| 4 | +> Web或者移动应用的重心,由后台往前台挪动的两个标志是:客户端存储,客户端模型维护。在可见的未来,我们将会见证后端将不存储数据、由前端负责存储数据的应用。 |
5 | 5 |
|
6 | | -最近,在用强类型语言的 TypeScript 编写一个前端的库,重新让我发现了强类型语言在建模方面的优势。 |
| 6 | +写过一个又一个的应用,我仍然没有遇到一个业务逻辑复杂的应用。即,我需要在前台处理一系列复杂的业务逻辑,我需要不断的转换前端的数据模型,才能追得上业务的变化。 |
7 | 7 |
|
8 | | -前端从后台拿到数据后,这些数据对于后台来说,就是一个模型。对于后台来说,这就是将单个的 Model 或者 Model 相关的集合放到一起,再用某个方法将他们转向 JSON,如 Spring 里的 ,又或者是 Django 里的。。 |
| 8 | +普通的 Web 应用里, 前台只需要负责显示即可,而后台相对应的提供数据。后台每次都为前端提供相应的数据,处理后显示即可。多数时候,提交的数据也是一次提交,不需要经过复杂的转换。 |
9 | 9 |
|
10 | | -多模型 |
| 10 | +而复杂的 Web 应用来说,他们需要大量的用户交互,由此带来的复杂度则是模型本身的转换。JavaScript 本身是一个弱类型的语言,这就意味着在处理模型这方面,它相当的无力。我们需要写下一个又一个的 ``语句`` 来判断值是否存在?是否是我们想要的结果 ?随后,我们才真正的去转换数据。一旦我们需要多次处理这些数据,这就会变成一个灾难。 |
| 11 | + |
| 12 | +模型与存储 |
11 | 13 | --- |
12 | 14 |
|
13 | | -正常的情况下,前端拿到这些数据,稍微做一些处理就可以显示到页面上。在一些复杂的例子里,我们需要做一些特殊的处理,如当我们从后台拿到了两种不同类型的产品,但是他们继承了同一个类,而返回了两种不同的结果。而在前台出于业务的需要,我们又需要将这些模型转为统一的形式,如不同的用户形式。 |
| 15 | +最近,我在写一个名为 EventStorming.Graph 的图形工具。因为采用的是强类型的 TypeScript,于是自然而然的就创建了很多的 Model。在这个设计的过程中,尽量采用了 DDD 中的一些思想,如基本的观察者模式,作为消息的中心来发布事件。 |
14 | 16 |
|
15 | | -对于 A(普通的用户) 来说,用户名就是它的手机号,而 Full Name 才是它的真实名字。 |
| 17 | + |
16 | 18 |
|
17 | | -对于 B (管理员)来说,公司相关的邮箱才是它的用户名,mobile 才是它的手机号,Full Name 也是真实名字。 |
| 19 | +在这领域里,有一个基本的内容就是事件。当用户创建了一个事件的时候,会发现这么一些事情。在 EventBusiness 中创建了 Observable,并让监听相应的 Observer 监听。有两个基本的观察者: |
18 | 20 |
|
19 | | -虽然对于 A 来说,还可能存在。。尽管对于后台来说,他们可以转换这些模型,但是当我们创建用户的时候,就会变成一个问题。 |
| 21 | + - 存储。当用户创建了一个事件的时候,就会从 EB 中获取到相应的对应,直接存储到数据库中。 |
| 22 | + - 渲染。当用户创建了一个事件的时候,我需要把事件以 Sticky(便利贴)的形式渲染到页面上。这个时候,我需要为事件对象添加一些额外的属性,如色彩、位置等等,这个时候,它已经不是一个事件模型,而是一个事件便利贴。 |
20 | 23 |
|
21 | | -我们不能用同一个模型来创建这些数据 。 |
| 24 | +也因此,我为它创建了一个新的 ID,用来区分旧的便利贴,并且还保留着旧的事件 ID,以便于未来更新对象。随后,这些数据会被存储到存储介质中,并被渲染到页面上。 |
22 | 25 |
|
| 26 | +作为一个『服务端穷』的我(无力支付起国内的服务器),就在想存储的 N 个问题。在客户端上存储了尽可能多的数据,只在最后用户将要离开页面的时候,向服务端发送数据——即用户的 ID、模型的 ID 和模型的内容。 |
23 | 27 |
|
24 | | -继承自同一模型的两个不同的模型,可能会两个不同的名字。普通的用户和管理员就是一个很好的例子,这个时候,你会怎么去解决这个问题? |
| 28 | +而在客户端存储数据,基本上就是两个问题:数据存储、模型变化。 |
25 | 29 |
|
| 30 | +客户端数据存储是一个简单的话题,唯一复杂的地方是选用一个比较好的存储介质。而相应的模型处理,则是一种比较麻烦的事。 |
26 | 31 |
|
27 | | -最近,我在写一个名为 EventStorming.Graph 的图形工具。设计的过程中,尽量采用了 DDD 中的一些思想,于是用观察者模式,作为消息的中心来懊 事件。 |
| 32 | +存储 |
| 33 | +--- |
28 | 34 |
|
29 | | -在这领域里,有一个基本的内容就是事件。当用户创建了一个事件的时候,会发现这么一些事情。在 EventBusiness 中创建了 Observable,并让监听相应的 Observer 监听。有两个基本的观察者: |
| 35 | +客户端出于不同的原因,我们会存储一些相应的用户数据,如: |
30 | 36 |
|
31 | | - - 存储。当用户创建了一个事件的时候,就会从 EB 中获取到相应的对应,直接存储到数据库中。 |
32 | | - - 渲染。当用户创建了一个事件的时候,我需要把事件以 Sticky(便利贴)的形式渲染到页面上。这个时候,我需要为事件对象添加一些额外的属性,如色彩、位置等等,这个时候,它已经不是一个事件模型,而是一个事件便利贴。 |
| 37 | + - 在页面间共享数据——适用于同一个网站,页面间使用不同的框架 |
| 38 | + - 存储用户的 token——缓存在内存或者 localstorage 用于登录,在重要的操作时再验证权限 |
| 39 | + - 缓存数据,加快下次打开速度 |
| 40 | + - 临时保存用户未完成的表单 |
| 41 | + - 存储 JavaScript 代码,以加快打开速度 |
33 | 42 |
|
34 | | -也因此,我为它创建了一个新的 ID,用来区分旧的便利贴,并且还保留着旧的事件 ID,以便于未来更新对象。 |
| 43 | +数据存储并不是一件很难的事。只需要: |
35 | 44 |
|
36 | | -随后,这些数据会被存储到存储介质中。 |
| 45 | + 1. 选择一个合适的存储介质 |
| 46 | + 2. 决定要存储的数据内容及形式 |
| 47 | + 3. 创建存储和读取接口 |
37 | 48 |
|
38 | | -存储 |
39 | | ---- |
| 49 | +我们只需要想一个 key,再想一个 value 就可以保存这个值了,如 localStorge 的setItem 和 getItem 就可以轻松达到这个要求了。而对于常用的数据格式来说,加上个 ``JSON.stringify`` 来转换对象为字符 串,从 localStorage 中读取数据时,再用 ``JSON.parse`` 去解析即可。 |
40 | 50 |
|
41 | | -由于不同的原因,我们会存储不同的内容: |
| 51 | + |
42 | 52 |
|
43 | | - - 在页面间共享数据——适用于同一个网站,页面间使用不同的框架 |
44 | | - - 存储用户的 token |
45 | | - - 缓存在数据库中,加快下次打开速度 |
46 | | - - 临时保存用户存储的表单 |
| 53 | +对于 IndexedDB 来说,我们就可以使用对象来存储了。 |
47 | 54 |
|
48 | | -数据存储并不是一件很难的事。我们只需要想一个 key,再想一个 value 就可以保存这个值了,如 localStorge 的setItem 和 getItem 就可以轻松达到这个要求了。而对于常用的数据格式来说,加上个 ``JSON.stringify`` 来转换对象为字符 串,从 localStorage 中读取数据时,再用 ``JSON.parse`` 去解析即可。 |
| 55 | + |
49 | 56 |
|
50 | | -或者的情况下,我们可需要在不同的存储介质中保持他们了,这个时候只需要不同的适配器即可。 |
| 57 | +不同的情况下,我们可需要在不同的存储介质中保持他们了,这个时候只需要不同的适配器即可。我们可以使用不同的库来,如支持使用不同介质的 localForge,IndexedDB、WebSQL、localStorage。又或者是支持不同浏览器的 store.js。 |
51 | 58 |
|
52 | | -我们可以使用不同的库来,如支持使用不同介质的 localForge,IndexedDB、WebSQL、localStorage ,又或者是支持不同浏览器的 store.js |
| 59 | +在客户端上存储数据的时候,就那么几种情况: |
53 | 60 |
|
54 | | -问题的另外一个难点在于存储什么,怎么存储。 |
| 61 | + - 单条数据。主要用于存储一些简单的数据,如用户 Token、功能开关、临时数据等等。 |
| 62 | + - 一个模型的数据集合。 |
| 63 | + - 多个模型的数据集合。 |
55 | 64 |
|
| 65 | +而后,复杂的地方就是处理这些数据模型。 |
56 | 66 |
|
57 | | -未来 |
| 67 | +模型的变化 |
58 | 68 | --- |
59 | 69 |
|
60 | | -区块链 |
| 70 | +前端从后台拿到数据后,这些数据对于后台来说,就是一个模型。对于后台来说,这就是从资源库中读取单个的 Model 或者 Model 相关的集合放到一起,再用某种 toJSON 方法将他们转向 JSON。前端拿到这些数据,稍微做一些处理就可以显示到页面上。 |
| 71 | + |
| 72 | +在一些复杂的例子里,我们需要做一些特殊的处理。当我们从后台拿到了两种不同类型的模型,但是他们继承了同一个类,结果返回了两种不同的结果。而在前台出于业务的需要,我们又需要将这些模型转为统一的形式。如在一个组织下里存在两个不同的账号体系,他们分别由不同的系统(或组织)来管理: |
| 73 | + |
| 74 | +对于 A(普通的用户) 来说,用户名就是它的手机号,而 Full Name 字段是它的真实名字。 |
| 75 | +对于 B (管理员)来说,公司相关的邮箱才是它的用户名,mobile 才是它的手机号。 |
| 76 | + |
| 77 | +虽然对于 A 来说,还可能存在一些额外的手机号字段。但是,用户名才是它真正意义上的手机号,可以用来登录、重置密码等等的操作。 |
| 78 | + |
| 79 | +这个时候,应该要由后台作一层转发代理,转换这些数据,以向前端提供一个一致性的数据。后台做了一层适配,并提供一个特殊的标志,用于区分不同的用户角色。可是问题到了这里,可能只解决了一半。并带了一些新的问题,我们需要不断地处理这些逻辑。 |
| 80 | + |
| 81 | +而当我们创建用户的时候,我们就需要不同的模型来做这件事。不同的客户端模型,反而变得更加容易了。一个比较典型的场景是:招聘网站。招聘网站分为了两种角色,公司和个人。这两种模型唯一的相似之处,怕是有一个唯一的标识符吧。 |
0 commit comments