Skip to content

Commit

Permalink
feat: 数据域组件扩充 trackExpression 属性用于显式的设置跟踪上层数据变化 Close: baidu#7152
Browse files Browse the repository at this point in the history
  • Loading branch information
2betop committed Jun 14, 2023
1 parent 56cb441 commit fc33a00
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 19 deletions.
71 changes: 70 additions & 1 deletion docs/zh-CN/concepts/datascope-and-datachain.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,31 @@ page

> **注意:** 当前例子中,对数据域中数据的获取使用的是 **\${xxx}** 模板语法,但是在不同的组件配置项中,获取数据的语法会有差异,我们会在后续的[模板](./template)[表达式章节](./expression)中一一介绍。
### 具备数据域的组件

- App
- Page
- Cards
- Chart
- CRUD
- CRUD2
- Dialog
- Drawer
- List
- Page
- PaginationWrapper
- Service
- Wizard
- Combo
- InputArray
- Table
- Table2

有个特殊情况是 CRUD 中 filter,实际上是个 form,所以 CRUD 中有两层数据域,第一层是 CRUD 本身,同时查询条件表单中也有一层数据域。

### 常见误解

需要注意,只有少数几个容器组件会创建新的数据域,除了最顶层的 Page,还有 CRUD、Dialog、IFrame、Form、Service 等
需要注意,只有少数几个容器组件会创建新的数据域,具体查看[具备数据域的组件](#具备数据域的组件)列表

常见的错误写法是给容器组件加 data 属性,比如:

Expand Down Expand Up @@ -377,6 +399,53 @@ page

> 具有类似特征的组件还有`Formula`
## 更新数据链

通常顶层数据域数据更新,孩子中具备数据域的组件都会更新,如果不更新会拿不到最新的值。从功能来看这个更新代价其实是很大的,有性能损耗,比如如果我在顶层更新了个变量 `name`,所有的孩子都会重新刷新一遍。
目前 amis 中,具备数据域的组件,默认会检测两层节点的数据是否发生变化(上层数据域和上上层数据域),来决定当前层的数据要不要更新。存在两个问题:

1. 当前组件也许并不关心上层数据是否变化,没必要进行这些刷新操作
2. 当前组件关系上上层的数据变化,但是在此拿不到最新的。(比如:放在 service 中的 crud,crud 中 filter 用了 service 的接口返回数据,但是拿不到最新的)

amis 从 3.2.0 版本开始针对[具备数据域的组件](#具备数据域的组件)新增了 `trackExpression` 属性,用来主动配置当前组件需要关心的上层数据。

针对以上问题,则可以通过这样配置来解决

1. `trackExpression` 配置成 `"none"` 也就是说不追踪任何数据。
2. `trackExpression` 配置成 `"${xxxVariable}"` 这样 xxxVariable 变化了更新当前组件的数据链。

关于 `trackExpression` 的语法,请查看表达式篇章,可以监听多个变量比如: `"${xxx1},${xxx2}"`,还可以写表单时如 `"${ xxx ? xxx : yyy}"`

amis 内部是通过运算这个表达式的结果来判断。所以表达式中千万不要用随机函数,或者用当前时间等,否则每次都会更新数据链。另外如果变量是数组,或者对象,会转成统一的字符串 `[object Array]` 或者 `[object Object]` 这个其实会影响检测的,所以建议转成 json 字符串如。 `${xxxObject | json}`。还有就是既然是监控上层数据,表达式中不要写当前层数据变量,是取不到的。

```schema
{
"data": {
"name": "amis"
},
"type": "page",
"body": [
{ "label": "请修改输入框", "type": "input-text", "name": "name"},
{
"type": "switch",
"label": "同步更新",
"name": "syncSwitch"
},
{
"type": "crud",
"filter": {
"trackExpression": "${syncSwitch ? name : ''}",
"body": [
"my name is ${name}"
]
}
}
]
}
```

## URL 参数

url 中的参数会进入顶层数据域,比如下面的例子,可以点击[这里](./datascope-and-datachain?word=myquery#url-参数)看效果。
Expand Down
3 changes: 2 additions & 1 deletion packages/amis-core/src/SchemaRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ export const RENDERER_TRANSMISSION_OMIT_PROPS = [
'id',
'inputOnly',
'label',
'renderLabel'
'renderLabel',
'trackExpression'
];

const componentCache: SimpleMap = new SimpleMap();
Expand Down
36 changes: 21 additions & 15 deletions packages/amis-core/src/WithStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
syncDataFromSuper,
isSuperDataModified
} from './utils/helper';
import {dataMapping} from './utils/tpl-builtin';
import {dataMapping, tokenize} from './utils/tpl-builtin';
import {RootStoreContext} from './WithRootStore';

export function HocStoreFactory(renderer: {
Expand Down Expand Up @@ -191,13 +191,16 @@ export function HocStoreFactory(renderer: {
if (
shouldSync === true ||
prevProps.defaultData !== props.defaultData ||
isObjectShallowModified(prevProps.data, props.data) ||
//
// 特殊处理 CRUD。
// CRUD 中 toolbar 里面的 data 是空对象,但是 __super 会不一样
(props.data &&
prevProps.data &&
props.data.__super !== prevProps.data.__super)
(props.trackExpression
? tokenize(props.trackExpression, props.data!) !==
tokenize(props.trackExpression, prevProps.data!)
: isObjectShallowModified(prevProps.data, props.data) ||
//
// 特殊处理 CRUD。
// CRUD 中 toolbar 里面的 data 是空对象,但是 __super 会不一样
(props.data &&
prevProps.data &&
props.data.__super !== prevProps.data.__super))
) {
store.initData(
extendObject(props.data, {
Expand All @@ -209,9 +212,12 @@ export function HocStoreFactory(renderer: {
}
} else if (
shouldSync === true ||
isObjectShallowModified(prevProps.data, props.data) ||
(props.syncSuperStore !== false &&
isSuperDataModified(props.data, prevProps.data, store))
(props.trackExpression
? tokenize(props.trackExpression, props.data!) !==
tokenize(props.trackExpression, prevProps.data!)
: isObjectShallowModified(prevProps.data, props.data) ||
(props.syncSuperStore !== false &&
isSuperDataModified(props.data, prevProps.data, store)))
) {
if (props.store && props.store.data === props.data) {
store.initData(
Expand Down Expand Up @@ -246,9 +252,8 @@ export function HocStoreFactory(renderer: {
store.initData(createObject(props.scope, props.data));
}
} else if (
(shouldSync === true ||
!props.store ||
props.data !== props.store.data) &&
!props.trackExpression &&
(!props.store || props.data !== props.store.data) &&
props.data &&
props.data.__super
) {
Expand All @@ -273,9 +278,10 @@ export function HocStoreFactory(renderer: {
}
// nextProps.data.__super !== props.data.__super) &&
} else if (
!props.trackExpression &&
props.scope &&
props.data === props.store!.data &&
(shouldSync === true || prevProps.data !== props.data)
prevProps.data !== props.data
) {
// 只有父级数据变动的时候才应该进来,
// 目前看来这个 case 很少有情况下能进来
Expand Down
2 changes: 1 addition & 1 deletion packages/amis-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export interface BaseApiObject {

/**
* 当开启自动刷新的时候,默认是 api 的 url 来自动跟踪变量变化的。
* 如果你希望监控 url 外的变量,请配置 traceExpression
* 如果你希望监控 url 外的变量,请配置 trackExpression
*/
trackExpression?: string;

Expand Down
2 changes: 1 addition & 1 deletion packages/amis/src/Schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ export interface SchemaApiObject {

/**
* 当开启自动刷新的时候,默认是 api 的 url 来自动跟踪变量变化的。
* 如果你希望监控 url 外的变量,请配置 traceExpression
* 如果你希望监控 url 外的变量,请配置 trackExpression
*/
trackExpression?: string;

Expand Down

0 comments on commit fc33a00

Please sign in to comment.