UNPKG

12.7 kBMarkdownView Raw
1---
2title: DataBinder
3category: Components
4chinese: 数据交互方案
5---
6
7ICE 前后端数据绑定、交互方案。
8
9## 介绍和使用方法
10
11### 目标
12
13灵感来源于 Webx,基于一定的**约定**帮你在组件上绑定一些数据和用来更新数据的 API,让你专注于 render 方法中界面显示逻辑,从而屏蔽掉 AJAX、state 管理等开发成本。
14
15如果你希望你的接口更自由,或者你希望自行把数据维护在组件 state 内,则都不推荐使用本方案。
16
17### 15 分钟快速上手教程
18
1915 分钟视频演示快速对比 Data-Binder 与 AJAX 方案对比,详细文档在下面。
20
21[点击这里查看视频](https://cloud.video.taobao.com/play/u/654982961/p/1/e/6/t/1/50636752.mp4)
22
23### 使用方法
24
25**1. 在某个 Class 上面配置当前需要的 DataSource**
26
27DataBinder 采用 decorator(类似 Java 注解)的方式使用,即在 class 上面调用并配置相关信息即可生效。
28
29**DataSource** 是 ICE DataBinder 解决方案中最重要的概念,组件、页面中某一块依赖数据的功能会被作为一个 DataSource 模块,用一个模块 Key 来区分,每一个数据模块可以配置多种数据获取方式以及默认数据等,然后注入到组件中被使用(详细的说明在下面)。比如我们最常见的通过 AJAX 获取、操作数据:
30
31```jsx
32@DataBinder({
33 '模块名 key': {
34 url: 'xxxx.json',
35 method: 'post',
36 // 请求附带的 request 参数,method post 下是 data 参数
37 data: {
38 page: 1
39 },
40 // AJAX 部分的参数完全继承自 axios ,参数请详见:https://github.com/axios/axios
41 // 下面是请求会返回的默认数据
42 defaultBindingData: {
43 // ...字段需要与 xxxx.json 接口返回的字段一一对应
44 }
45 }
46})
47class ListView extends Component {
48 ...
49}
50```
51
52例如:
53
54```jsx
55@DataBinder({
56 account: {
57 url: '/getAccountInfo.json',
58 type: 'post',
59 data: {
60 uid: '123123'
61 },
62 defaultBindingData: {
63 // 配置接口返回数据的字段的初次默认值
64 userName: '',
65 userAge: 0
66 }
67 }
68})
69class ListView extends Component {
70 ...
71}
72```
73
74详细的解释下:
75
76* 模块名 key **必填** 用来将数据和接口约束在某一个范围下,通常按照接口数据划分或者按照功能区块。
77* `url, type, data, etc.` **选填** 配置当前模块接口相关信息,基于 [axios](https://github.com/axios/axios) 支持其文档所有参数。该参数可选,部分模块可能无需 AJAX 交互,或者无法写死配置需要通过其他接口来获取。
78* `defaultBindingData` **选填** 该字段配置当前模块数据初始化默认值,如果当前模块有异步接口配置,则模块的字段需要与接口返回的数据字段一一对应。该参数可选,因为有些接口只需要提交成功即可,无需 UI 变化。
79
80**2. 通过 `this.props.bindingData.模块key` 来获取绑定的数据**
81
82对某个 React class 添加 DataBinder 绑定配置之后,DataBinder 会在组件上添加一个 props `bindingData` 用来存放配置的所有数据,模块 key 为你对应的 DataSource key 的前部分,比如:配置 `account` 可以通过 `this.props.bindingData.account` 获取到被绑定的数据,第一次为 `defaultBindingData` 里面配置的数据。
83
84因此你可以在你 render 部分的代码编写如下代码调用:
85
86```jsx
87@DataBinder({...})
88class ListView extends Component {
89
90 render() {
91 const { account } = this.props.bindingData;
92
93 return (
94 <div>
95 <p>用户名:{account.userName}</p>
96 <p>年龄:{account.userAge}</p>
97 </div>
98 );
99 }
100}
101```
102
103**3. 通过 `this.props.updateBindingData` 来更新模块数据**
104
105DataBinder 除了为组件添加一个 props 之外,还向组件内部注入一个 API 用来更新模块数据:
106
107`this.props.updateBindingData(key, params, callback);` 第一个参数是模块 key ,字符串类型,用来标记更新哪一个 DataSource,需要保留全名(例如:`account`)。第二个参数是 DataSource 的配置,对象类型,调用时它会和默认定义的数据进行一个 merge 操作,并发起 AJAX 请求。通常你只需要传递 `{data: {...}}` 数据即可,`data` 用来描述对接口发请求时附加的参数。第三个参数是 `callback`,是一个函数,当请求结束之后调用,方便你处理额外逻辑。
108
109> 注意:updateBindingData 里面传递的参数,跟顶部配置的初始化参数是一个 deepmerge 的合并操作。
110
111比如一个翻页组件,当页码变化时获取新数据可以这样做:
112
113```jsx
114@DataBinder({
115 accountTable: {
116 url: '/getAccountTableList.json',
117 type: 'post',
118 data: {
119 page: 1,
120 pageSize: 10,
121 },
122 defaultBindingData: {
123 page: 1,
124 pageSize: 10,
125 total: 0,
126 lists: [],
127 },
128 },
129})
130class ListView extends Component {
131 changePage = (pageNo) => {
132 this.props.updateBindingData('accountTable', {
133 data: {
134 page: pageNo,
135 },
136 });
137 };
138
139 render() {
140 const { accountTable } = this.props.bindingData;
141
142 return (
143 <div>
144 当前 Table 数据:{accountTable.lists}
145 <Pagination
146 current={accountTable.page}
147 pageSize={accountTable.pageSize}
148 total={accountTable.total}
149 onChange={this.changePage}
150 />
151 </div>
152 );
153 }
154}
155```
156
157DataBinder 不会在组件初始化的时候帮你自动请求一次,因为有些 DataSource 不需要默认就请求一次。如果你需要在初始化的异步请求数据,就需要在合适的生命周期中主动调用该方法,比如组件即将渲染的时候拉取数据:
158
159```jsx
160@DataBinder({...})
161class ListView extends Component {
162 componentDidMount() {
163 // 拉取第一页的数据
164 this.props.updateBindingData('accountTable', {
165 data: {
166 page: 1
167 }
168 });
169 }
170}
171```
172
173**4. 处理 Loading 逻辑和效果**
174
175AJAX 是异步的,为了更好的用户体验,推荐添加一个 Loading 效果组件来给用户请求中的反馈。
176
177每一个 DataSource 模块的数据附带了一个私有属性 `__loading` 来标记当前模块是否正在请求过程中,这样你可以在组件 render 中读取这个数据来判断是否正在加载数据。比如 Table 组件内部封装了一个 Loading 的效果,需要使用 `isLoading` props 进行配置,那么就可以写:
178
179```jsx
180const { accountTable } = this.props.bindingData;
181
182<Table dataSource={accountTable.lists} isLoading={accountTable.__loading}>
183 ...
184</Table>;
185```
186
187你也可以使用 `Loading` 组件进行 loading 效果的模拟,参照文档可以写出如下代码:
188
189```jsx
190import DataBinder from '@icedesign/data-binder';
191import { Loading } from '@icedesign/base';
192
193@DataBinder({
194 account: {
195 // 接口返回数据:{status: 'SUCCESS', data: {foo: 'bar'}}
196 url: '/getdefaultBindingData.json',
197 defaultBindingData: {
198 foo: null,
199 },
200 },
201})
202class ListView extends Component {
203 componentDidMount() {
204 this.props.updateBindingData('account');
205 }
206
207 refresh = () => {
208 this.props.updateBindingData('account');
209 };
210
211 render() {
212 const { account } = this.props.bindingData;
213
214 return (
215 <div>
216 <Loading
217 state={account.__loading ? 'on' : 'off'}
218 shape="fusion-reactor"
219 >
220 <div>当前 foo 的值为:{account.foo}</div>
221 </Loading>
222 <div style={{ marginTop: 20 }}>
223 <Button onClick={this.refresh}>点击重新请求</Button>
224 </div>
225 </div>
226 );
227 }
228}
229```
230
231效果如图示:
232
233![](//img.alicdn.com/tfs/TB1qLUVPXXXXXXqaXXXXXXXXXXX-706-372.gif)
234
235此外,根模块也有一个 `__loading` 属性(即:`this.props.bindingData.__loading`),用来标记当前注册的所有模块中是否有某个模块在发送 AJAX 请求。这样可以便于进行全局的提示,比如一个 AJAX 请求全局提示标记等。
236
237## 参数配置
238
239### DataBinder decorator 用法
240
241调用方法:
242
243```js
244@DataBinder({
245 模块key: {
246 url: 'xxx.json',
247 //... AJAX axios 配置
248 responseFormatter: (responseHandler, res, originResponse) => {
249 // 做一些数据转换
250 const newRes = {
251 status: res.code !== 0 ? 'SUCCESS' : 'ERROR',
252 message: res.successMsg,
253 };
254 // 回传给处理函数
255 // 不做回传处理会导致数据更新逻辑中断
256 responseHandler(newRes, originResponse);
257 },
258 defaultBindingData: {
259 foo: 'bar',
260 },
261 },
262})
263class ListView extends React.Component {
264 render() {
265 const key = this.props.bindingData.key;
266
267 return <div>{key.foo}</div>;
268 }
269}
270```
271
272* `dataSouce` 内容为符合 [axios](https://github.com/axios/axios) 的请求参数。
273* `responseFormatter` 用来做老接口数据转换用,老接口如果不按照现有模式需要进行一层数据转换处理。
274* `defaultBindingData` 内容为接口对应字段的默认数据,在 render 中使用 `this.props.bindingData` 获取。
275
276## 接口 API
277
278以下 API 会注入到 Class 中,通过 `this.props.xxxx` 的方式调用。
279
280| API 名 | 说明 | 是否有参数 | 参数类型 | 参数值 | 备注 |
281| ------------------------------ | ------------------------------ | ---------- | ----------------------------------------------- | ------ | ------------------------------------------------------------ |
282| `this.props.updateBindingData` | 获取更新 DataSource 的数据 | true | key: string, params: object, callback: function | | `this.props.updateBindingData('account', {data: {page: 5}})` |
283| `this.props.getDataSource` | 获取某个 DataSource 的默认配置 | true | key: string | | |
284
285## 后端接口协议
286
287配置的 AJAX 接口需要按照一定的协议规则实现:
288
289**request:**
290
291业务接口自定。
292
293**response:**
294
295```json
296{
297 "status": "SUCCESS",
298 "message":
299 "接口请求完成的提示,可有可无,status 为非 SUCCESS 时会显示报错的 UI",
300 "data": {
301 "foo":
302 "data 是具体的数据,需要与该接口定义的 defaultBindingData 字段结构保持一致"
303 }
304}
305```
306
307## 自定义 requestClient
308
309如果你的项目 ajax 模块进行了统一配置和通用处理的封装,或者使用 ws 或者其它的 RPC 手段进行网络通信,DataBinder 允许对请求客户端进行自定义。
310在 DataBinder 传递第二个参数对象,并指定 requestClient 为一个返回 promise 的请求函数。该 Promise resolve 的值为 response 对象,该 response 对象必须包含一个 data 字段,值为返回的数据。
311
312**DEMO**
313
314```jsx
315import jsonp from 'jsonp';
316
317/**
318 * 自定义的 json request client
319 */
320function request(opts) {
321 return new Promise((resolve, reject) => {
322 jsonp(opts.url, { name: 'callback' }, (err, data) => {
323 if (err) {
324 reject(err);
325 } else {
326 resolve({ data });
327 }
328 })
329 });
330}
331
332@DataBinder({
333 account: {
334 // 这里的所有字段会作为参数传递给 requestClient
335 url: 'https://ice.alicdn.com/assets/mock/53141.jsonp.js',
336 }
337}, { requestClient: request })
338export default class extends React.Component {
339 // ...
340}
341```
342
343
344## 常见需求
345
346#### 发送数组类型数据,key 自动加了 `[]` 怎么办?
347
348当你传输的 data 中有个 key 数据(例如:items)为数组格式时,提交给后端 key 会自动添加 `[]`(例如:items[]=xxx、items[]=yyy)。如果你不需要这种功能,希望使用原本的 key 进行提交,可以添加下面配置解决:
349
350```js
351{
352 serializeArray: false;
353}
354```
355
356#### 接口是老版本接口,不符合 DataBinder 接口协议如何处理?
357
358配置 DataSource 时,添加 `responseFormatter` 配置进行数据处理,然后返回符合规范的数据。
359
360#### 自定义请求成功、失败的提示和逻辑
361
362在 DataSource 配置部分自定义 success、error callback 实现,以 success 为例:
363
364```js
365@DataBinder({
366 key: {
367 url: 'xxx.json',
368 success: (res, defaultCallback, originResponse) => {
369 console.log('请求成功了,返回的数据为', res)
370 // 执行默认的逻辑请求成功逻辑
371 // 通常为弹出反馈 Toast
372 defaultCallback();
373 // originResponse 内容请参见:https://github.com/axios/axios#response-schema
374 },
375 defaultBindingData: {
376 foo: 'bar'
377 }
378 }
379})
380class ...
381```
382
383error callback 的参数和逻辑同 success。
384
385