-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.html
418 lines (276 loc) · 20 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
<!DOCTYPE html>
<!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
<!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
<!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<title>NO END FOR LEARNING</title>
<meta name="author" content="ZHU Benwei">
<meta name="description" content="不同的页面也可能有相同领域,不同状态的数据,也算是一个Domain吧? 没有BFF的情况下 基于Redux的应用程序中最常见的state结构是一个简单的JavaScript对象,它最外层的每个key中拥有特定域的数据。 然而,BFF可以是来自不同Domain的数据库合并后返回到前端的。 …">
<!-- http://t.co/dKP3o1e -->
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="canonical" href="http://benweizhu.github.io">
<link href="/favicon.png" rel="icon">
<link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
<link href="/atom.xml" rel="alternate" title="NO END FOR LEARNING" type="application/atom+xml">
<script src="/javascripts/modernizr-2.0.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>!window.jQuery && document.write(unescape('%3Cscript src="/javascripts/libs/jquery.min.js"%3E%3C/script%3E'))</script>
<script src="/javascripts/octopress.js" type="text/javascript"></script>
<!--Fonts from Google"s Web font directory at http://google.com/webfonts -->
<link href="//fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<link href="//fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
</head>
<body >
<header role="banner"><hgroup>
<h1><a href="/">NO END FOR LEARNING</a></h1>
<h2>Writing blog if you feel tired | 学海无涯 苦写博客</h2>
</hgroup>
</header>
<nav role="navigation"><ul class="subscription" data-subscription="rss">
<li><a href="/atom.xml" rel="subscribe-rss" title="subscribe via RSS">RSS</a></li>
</ul>
<form action="https://www.google.com/search" method="get">
<fieldset role="search">
<input type="hidden" name="sitesearch" value="benweizhu.github.io">
<input class="search" type="text" name="q" results="0" placeholder="Search"/>
</fieldset>
</form>
<ul class="main-navigation">
<li><a href="/">Blog</a></li>
<li><a href="/blog/archives">Archives</a></li>
</ul>
</nav>
<div id="main">
<div id="content">
<div class="blog-index">
<article>
<header>
<h1 class="entry-title"><a href="/blog/2018/05/08/deep-thinking-in-react-12/">React的思考(十二)- 组织state和reducer(4)</a></h1>
<p class="meta">
<time class='entry-date' datetime='2018-05-08T23:45:26+08:00'><span class='date'><span class='date-month'>May</span> <span class='date-day'>8</span><span class='date-suffix'>th</span>, <span class='date-year'>2018</span></span> <span class='time'>11:45 pm</span></time>
| <a href="/blog/2018/05/08/deep-thinking-in-react-12/#disqus_thread"
data-disqus-identifier="http://benweizhu.github.io/blog/2018/05/08/deep-thinking-in-react-12/">Comments</a>
</p>
</header>
<div class="entry-content"><h2>不同的页面也可能有相同领域,不同状态的数据,也算是一个Domain吧?</h2>
<h2>没有BFF的情况下</h2>
<p>基于Redux的应用程序中最常见的state结构是一个简单的JavaScript对象,它最外层的每个key中拥有特定域的数据。</p>
<p>然而,BFF可以是来自不同Domain的数据库合并后返回到前端的。</p>
<h2>暴露的服务和接口</h2>
<p>思考后端的操作逻辑,微服务的场景,按照业务的Restful API,Redux的这种设计模式导致这种CRUD和Service的冲突</p>
<h2>Normalize State</h2>
<h2>BFF层的数据结构</h2>
<h2>一个action要操作多个reducer中的state</h2>
<h2>redux的state和页面的关系</h2>
<h2>开发过程中state的演进过程</h2>
</div>
</article>
<article>
<header>
<h1 class="entry-title"><a href="/blog/2018/05/04/deep-thinking-in-react-11/">React的思考(十一)- 组织state和reducer(3)</a></h1>
<p class="meta">
<time class='entry-date' datetime='2018-05-04T23:09:38+08:00'><span class='date'><span class='date-month'>May</span> <span class='date-day'>4</span><span class='date-suffix'>th</span>, <span class='date-year'>2018</span></span> <span class='time'>11:09 pm</span></time>
| <a href="/blog/2018/05/04/deep-thinking-in-react-11/#disqus_thread"
data-disqus-identifier="http://benweizhu.github.io/blog/2018/05/04/deep-thinking-in-react-11/">Comments</a>
</p>
</header>
<div class="entry-content"><p>以React为首的“数据驱动”的开发方式,让我们从过去的DOM操作,慢慢转化成对应用状态和数据的管理,这些状态和数据在应用的生命周期中被持久化,被应用管理和使用,怎么样有效的在SPA中管理这些数据变的特别重要。</p>
<h2>前后端不同的数据模型</h2>
<p>在后端开发中,我们设计数据库或者对象模型,通常会根据领域模型来建立不同的数据表和对象,以反映我们对客观现实的抽象,这种抽象在MVC的世界里通常由Model表示。</p>
<p>而在前端开发中,我们一般会从UI的角度出发,去设计前端展示所需要的视图对象模型,我们也称之为View Model。</p>
<p>而往往在大多数情况下,后端的数据模型Model和前端的数据模型View Model是不对等的。</p>
<h2>来自后方的数据</h2>
<p>当一个应用的生命周期启动,应用会调用后台的API获取后端数据,然后以某种方式转换成前端所需数据模型,最后展示在应用的页面上。</p>
<p>作为一个后端的开发人员,通常以什么样的方式暴露后端的资源给别的应用使用呢?</p>
<p>如果需要API的通用度比较高,一般最简单直接的方法是开放领域资源的CRUD操作(你可以是RESTful API也可以是别的方式)。</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>https://api.example.com/v1/zoos
</span><span class='line'>https://api.example.com/v1/animals
</span><span class='line'>https://api.example.com/v1/employees
</span><span class='line'>
</span><span class='line'>GET /zoos:列出所有动物园
</span><span class='line'>POST /zoos:新建一个动物园
</span><span class='line'>GET /zoos/ID:获取某个指定动物园的信息
</span><span class='line'>PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)</span></code></pre></td></tr></table></div></figure>
<p>我们目前就暂且以这种方式为例,目测这种方式暴露的API比较常见,后面我们再讨论其他的。</p>
<h2>combineReducer下的拆分</h2>
<p>让我们再回到Redux中,基于Redux的应用程序中,比较常见的state结构是一个简单的JavaScript对象,它最外层的每个key中拥有特定域的数据,这其实是Redux官方文档上的一句话,我在其他的一些博客上也看到了采用基于业务领域的方式组织state结构。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="p">{</span>
</span><span class='line'> <span class="s2">"zoos"</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'> <span class="p">...</span>
</span><span class='line'> <span class="p">},</span>
</span><span class='line'> <span class="s2">"animals"</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'> <span class="p">...</span>
</span><span class='line'> <span class="p">},</span>
</span><span class='line'> <span class="s2">"employees"</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'> <span class="p">...</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>在combineReducer的帮助下,构建上面这个结构,其实就是拆分成多个reducer,拆分之后的reducer独立负责管理该特定切片state的更新。好处是,它提供了一种非常直接的代码逻辑拆分管理的方式,职责独立,物理位置独立(文件独立)。</p>
<h2>响应同一个操作</h2>
<p>有了这样一层结构,我们该如何操作它呢?</p>
<p>现在我们假设有这样一个操作,它既需要更新zoos下面的数据,又需要更新animals下面的数据。</p>
<p>如果放在后端代码中,你会怎么做?</p>
<p>我的思路会是写一个函数A,在这个函数里面调用zoos和animals的service或者repository的方法完成更新操作,那么,真正使用的时候,只需要调用这个函数A即可,思路很明朗直接。</p>
<p>在combineReducer和redux结合情况下,我们就需要转换一下思路了,不同业务领域下的数据被放置到了不同的reducer,而你能做的只是发送action。</p>
<p>combineReducer神奇的地方就是,被发送的action会被所有的reducer接收到。(有点像发布订阅模式)</p>
<p>这样一个过去直觉上同步有序的操作过程,在redux中,被分发到多个拆分之后的reducer中,每个reducer都去响应这个action,在需要的情况下独立的更新他们自己的切片state,最后组合成新的state。</p>
<h2>计算衍生(derived)数据</h2>
<p>后端的业务数据被存储在了redux的store中,然而它是以后端model的形式保存在那。我们的前端页面需要的数据模型,一般和后端model不完全一样,也许是多个后端model组合在一起才得到可以使用的view model,这种情况很常见。</p>
<p>这个时候,我们就需要从后端数据模型中计算衍生数据,得到我们最终需要的View Model。</p>
<p>一般的做法是在mapStateToProps中进行,mapStateToProps中的state是根节点上的state,所以可以拿到所有的领域数据,此时我们就能根据它衍生出我们需要的视图模型。</p>
<p>而在这里一般都会推荐使用reselect库来做,好处是:</p>
<p>第一,能够借机将这个计算衍生数据的逻辑拆分到另一个模块中 <br/>
第二,它能帮你记住之前的计算过的数据,避免二次计算,同时避免无意义的重新渲染(关于什么时候重新渲染,在前面已经介绍过了)。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="kr">import</span> <span class="p">{</span> <span class="nx">createSelector</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'reselect'</span>
</span><span class='line'>
</span><span class='line'><span class="kr">const</span> <span class="nx">getVisibilityFilter</span> <span class="o">=</span> <span class="p">(</span><span class="nx">state</span><span class="p">)</span> <span class="o">=></span> <span class="nx">state</span><span class="p">.</span><span class="nx">visibilityFilter</span>
</span><span class='line'><span class="kr">const</span> <span class="nx">getTodos</span> <span class="o">=</span> <span class="p">(</span><span class="nx">state</span><span class="p">)</span> <span class="o">=></span> <span class="nx">state</span><span class="p">.</span><span class="nx">todos</span>
</span><span class='line'>
</span><span class='line'><span class="kr">export</span> <span class="kr">const</span> <span class="nx">getVisibleTodos</span> <span class="o">=</span> <span class="nx">createSelector</span><span class="p">(</span>
</span><span class='line'> <span class="p">[</span> <span class="nx">getVisibilityFilter</span><span class="p">,</span> <span class="nx">getTodos</span> <span class="p">],</span>
</span><span class='line'> <span class="p">(</span><span class="nx">visibilityFilter</span><span class="p">,</span> <span class="nx">todos</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
</span><span class='line'> <span class="k">switch</span> <span class="p">(</span><span class="nx">visibilityFilter</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">case</span> <span class="s1">'SHOW_ALL'</span><span class="o">:</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">todos</span>
</span><span class='line'> <span class="k">case</span> <span class="s1">'SHOW_COMPLETED'</span><span class="o">:</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">todos</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">t</span> <span class="o">=></span> <span class="nx">t</span><span class="p">.</span><span class="nx">completed</span><span class="p">)</span>
</span><span class='line'> <span class="k">case</span> <span class="s1">'SHOW_ACTIVE'</span><span class="o">:</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">todos</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">t</span> <span class="o">=></span> <span class="o">!</span><span class="nx">t</span><span class="p">.</span><span class="nx">completed</span><span class="p">)</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>
<h2>总结</h2>
<p>这么分析下来,每个部分的职责都很清晰,开发的模式也比较明确,然而理想和设计在业务不复杂的时候都很美好,现实往往比它们骨感很多,这一篇文章只是给了一个简单的例子,没有分析复杂的场景。</p>
<p>在下一篇,我们可以在此基础上分析开发中的复杂场景,一起思考下怎么样管理是合适的。同时,我们也来思考另一种新的前端架构BFF下的Redux应该怎么管理,当后端API不再是资源的CRUD,而是面向业务的API操作时,Redux又应该怎么管理state。</p>
</div>
</article>
<div class="pagination">
<a class="prev" href="/posts/2">← Older</a>
<a href="/blog/archives">Blog Archives</a>
</div>
</div>
<aside class="sidebar">
<section>
<h1 style="margin-left: 5px;">About Me</h1>
<div style="margin-left: 8px;">Working in <a href="https://www.thoughtworks.com/" style="text-decoration: blink;font-weight: bolder;">ThoughtWorks</a> Wuhan</div>
<div style="margin-left: 8px;margin-top: 10px;">A Java/RoR/NodeJS-FE/Full Stack Developer</div>
<div style="margin-left: 8px;margin-top: 10px;">A Gradle Plugin Contributor</div>
<div style="margin-top: 10px;"><a href='http://book.douban.com/subject/26609447/' target="_blank" style="text-decoration: blink;font-weight: bolder;">《实战Gradle》</a>译者之一</div>
<div style="margin-left: 8px;;margin-top: 10px;">计算机应用技术学术型硕士-<span style="font-weight: bold;">信息安全方向</span></div>
<div style="margin-top: 10px;margin-left: 8px;">关注:Gradle,Spring,前端开发,自动化构建,自动化测试,Devops,信息安全</div>
<div style="margin-top: 10px;margin-left: 8px;">我正在写的书:<a href='https://benweizhu.gitbooks.io/gradle-best-practice/content/' target="_blank" style="text-decoration: blink;font-weight: bolder;">《Gradle最佳实践》</a></div>
</section>
<section>
<h1>Recent Posts</h1>
<ul id="recent_posts">
<li class="post">
<a href="/blog/2018/05/08/deep-thinking-in-react-12/">React的思考(十二)- 组织state和reducer(4)</a>
</li>
<li class="post">
<a href="/blog/2018/05/04/deep-thinking-in-react-11/">React的思考(十一)- 组织state和reducer(3)</a>
</li>
<li class="post">
<a href="/blog/2018/05/03/deep-thinking-in-react-10/">React的思考(十)- 组织state和reducer(2)</a>
</li>
<li class="post">
<a href="/blog/2018/05/01/deep-thinking-in-react-9/">React的思考(九)- 组织state和reducer(1)</a>
</li>
<li class="post">
<a href="/blog/2018/04/27/deep-thinking-in-react-8/">React的思考(八)- Redux的Middleware(下)异步的世界</a>
</li>
</ul>
</section>
<section>
<h1>GitHub Repos</h1>
<ul id="gh_repos">
<li class="loading">Status updating…</li>
</ul>
<script type="text/javascript">
$(document).ready(function(){
if (!window.jXHR){
var jxhr = document.createElement('script');
jxhr.type = 'text/javascript';
jxhr.src = '/javascripts/libs/jXHR.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(jxhr, s);
}
github.showRepos({
user: 'benweizhu',
count: 3,
skip_forks: true,
target: '#gh_repos'
});
});
</script>
<script src="/javascripts/github.js" type="text/javascript"> </script>
</section>
</aside>
</div>
</div>
<footer role="contentinfo"><p>
Copyright © 2018 - ZHU Benwei -
<span class="credit">Powered by <a href="http://octopress.org">Octopress</a></span>
</p>
</footer>
<script type="text/javascript">
var disqus_shortname = 'benweiblog';
var disqus_script = 'count.js';
(function () {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/' + disqus_script;
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
}());
</script>
<script type="text/javascript">
(function(){
var twitterWidgets = document.createElement('script');
twitterWidgets.type = 'text/javascript';
twitterWidgets.async = true;
twitterWidgets.src = '//platform.twitter.com/widgets.js';
document.getElementsByTagName('head')[0].appendChild(twitterWidgets);
})();
</script>
</body>
</html>