-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add doc for Mask and Tagging #1335
base: trailhead_guidance
Are you sure you want to change the base?
Conversation
</script> | ||
<template> | ||
<Mask :visible="visible" :tags="currentTags" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mask 的使用方除了希望 Mask 遮住界面上几乎所有内容外,一般都还会展示一些别的内容,比如提示文案、箭头、引导用的卡通形象等,这些内容使用方应当如何渲染以确保它们不会被 Mask 遮住?一般这内容会希望相对被高亮显示的组件定位,是可以方便地做到的吗?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
好的,这确实也是需要考虑的地方。我们经过讨论决定通过Props的形式传入description
字段:
- 如果传了
description
,则代表当前是following
步骤,除 高亮目标组件 外还需要展示卡通形象、提示文案(description)和箭头。 - 如果没传
description
,则代表当前是coding
步骤,不需要额外素材信息展示,仅 高亮目标组件 即可。
对于 卡通形象、提示文案(description)和箭头 展示位置的处理,这里需要考虑:
- 蒙层中高亮元素的位置是需要通过Tagging精准高亮的。
- 卡通形象、提示文案(description)和箭头 的展示位置需要通过高亮目标
动态计算
的。
对于动态计算
的部分,这里可以考虑坐标系
的概念,在Mask建立二维坐标系,根据 高亮目标组件的位置,分别计算 卡通形象、提示文案和箭头 的位置,同理这三者之间也有位置关系。比如:
- 提示文案应在卡通形象上方
- 箭头指向不可能是卡通形象,只会指向 高亮目标组件
除此外还要考虑屏幕边界问题。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我们经过讨论决定通过Props的形式传入
description
字段:
你的意思是把“卡通形象、提示文案(description)和箭头”这些内容都做到 Mask 里边?
你说的位置计算逻辑不是现在关注的重点,我们现在应该关心的是,哪些逻辑是哪个模块的职责,在我们给定的职责划分下,接口是不是易用的
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
是的,原来是想在StepPlayer里做。现在发现在Mask里会更方便一点,因为Mask本身调用了Tagging,这样可以减少调用次数。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
把某个逻辑对应的职责从一个模块挪到另一个模块,是一个很重的动作,因为这会改变模块本身的定位,进而影响整体的模块拆分的合理性;建议当遇到问题的时候,先去想:
- 它是不是一个重要的问题,很多时候性能问题都不是问题
- 能不能通过优化接口(不改变定位)来达到目的
都不行才去考虑调整模块定位
这里如果只是多调用一次 Tagging 模块的接口,这个本身不是问题,又不是什么很重的操作;每个模块是不是关心了不该它关心的事情、干了它不该干的事情才是重要的
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我懂了,这个操作和Mask没什么关系,还是放回StepPlayer吧
```typescript | ||
/** 节点 */ | ||
export interface TagNode { | ||
e: Component; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e
是什么意思?这里可能混淆了几个概念: element / component / component-instance
可以参考 https://vuejs.org/guide/essentials/component-basics 捋清楚
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e
在接口层面表现为vue中的type Component
,在实例层面表现为component-instance
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vue中的type
Component
type Component
是一个 component 的类型,而不是一个 component instance 的类型
* @param component: 被添加的组件 | ||
* @param keys: 添加到的目标节点,不传时添加到根节点 | ||
*/ | ||
addTag(key: string, component: Component, keys?: string[]): void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
预期谁会调用这个方法?以及下边的 removeTag
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里考虑的是动态添加或者删除的内容,比如:
removeTag
: 移除一个精灵后需要把精灵在tagTree中的标注删除(如果存在子节点也要删除子节点)。addTag
: 新增一个精灵后,需要在tagTree中添加节点信息。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
你说的“精灵”是啥?
如果你说的就是 Builder project 中的 sprite,要知道标记的对象是 UI 节点,不是 sprite 这样的业务数据;比如你看一眼 src/components/editor/panels/sprite/SpritesPanel.vue
然后再考虑下标记的代码长什么样呢?以及 removeTag
、addTag
是什么样的代码在什么时候调用呢?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
好的,这块我也在想想。
getElementByKeys(keys: string[]): Component | null; | ||
|
||
/** 清空所有标注 */ | ||
clearAllTags(): void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
预期谁在什么情况下会调用 clearAllTags
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里考虑的是销毁App的时候,App组件卸载时调用,不过现在看来好像没有意义,因为App组件卸载时同样回收了AllTags
, 后续的提交中我会删除掉。
```vue | ||
<!-- 示例组件1 --> | ||
<template> | ||
<Father data-tag="father"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tagging 模块跟其他模块相比,还有个特殊的地方是它的实现会强依赖框架的能力。这里确定接口的同时需要同时确定对应的实现机制(否则很可能无法按预期的接口去实现)。
这里看起来是约定向 vue component 或 HTML element 上添加一个自定义的 HTML attribute?它会是如何被收集起来的呢?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
约定向 vue component 或 HTML element 上添加一个自定义的 HTML attribute
这种“以某种姿势来和模块交互”的约定其实也是模块对外 API 的一部分,也应当在 module_Tagging 中予以说明
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里看起来是约定向 vue component 或 HTML element 上添加一个自定义的 HTML attribute?
是的,通过在 vue component 或者 HTML element中添加 自定义属性data-tag=${key}
实现语义化标注
这种“以某种姿势来和模块交互”的约定其实也是模块对外 API 的一部分,也应当在 module_Tagging 中予以说明
我会在后续提交中加上的。
它会是如何被收集起来的呢?
这里考虑的实现是在初始化时,在跟组件中通过vue中getCurrentInstance()
方法获取组件实例,并递归调用,查询data-tag
的标注,生成tagTree
实现对标准和组件的收集。但getCurrentInstance()
获取的组件实例处理起来有些复杂处理时要考虑情况也比较多,或许不是唯一的方法。

通过
getCurrentInstance()
获取的组件实例
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
在初始化时,在跟组件中通过vue中
getCurrentInstance()
方法获取组件实例,并递归调用,查询data-tag
的标注,生成tagTree
实现对标准和组件的收集。但getCurrentInstance()
获取的组件实例处理起来有些复杂处理时要考虑情况也比较多,或许不是唯一的方法。
这个问题是,你没法感知“动态”内容中标注的节点;比如某个界面初始状态下不渲染,直到用户点了某个 button 后那个节点才渲染出来,但是你的 tagTree
是不会知道需要去更新的
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这确实也是一个问题
```vue | ||
<script lang="ts" setup> | ||
// 组件内注入 | ||
const tagging = inject("Tagging"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
一般来说一个 library 不会需要使用方自己去指定这个 inject key,因为这样使用方就需要自己去保证:
- inject 的地方和 provide 的地方 key 相同
- 这个 key 不会跟其他不相干 provice/inject 逻辑 key 冲突
你可以看下 pinia、i18n、vue-router 的 API,它们应该几乎都依赖了 provide/inject 的能力,但都不会把上述的负担交给使用方
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
好的,这个确实需要在注入,我会在后续提交中修改。
语义化标注待解决问题tag 的收集tag 跨层级
动态更新 tagTree
|
@minorcell 我之前提过 https://github.com/qbox/portal-base/tree/master/common/components/Role 这个,你上面提到的问题那边倒是都解决过,虽然是在 React 下,不过做法搬到 vue 下也是能用的;你可以先了解一下它的做法,作为一个兜底选项,不排除 vue 下可能有更好的做法 |
@nighca 这个链接我好像没办法访问,是404页面,在组织项目搜索中也搜不到。 |
如晨会提到的,考虑到 Tagging 信息的消费方不止一个,建议考虑这样的思路把收集 tagging 信息的根节点跟消费 tagging 信息的点分开: <TaggingRoot>
<!-- 整个应用 -->
</TaggingRoot> // 消费 Tagging 信息,典型诉求是根据某个 selector string 找到对应的 component-instance / element,
// 这里的函数名(`useTagging`)及其参数仅仅是示意,不一定要按这个来
const result = useTagging(/* ... */) 这样不至于强迫所有的消费逻辑都集中到跟 |
[Git-flow] Hi @minorcell, There are some suggestions for your information: Rebase suggestions
Which seems insignificant, recommend to use For other If you have any questions about this comment, feel free to raise an issue here: |
@nighca 寒星老师,我们晨会之后又讨论了一下,我提交文档后看到你在前一分钟的这个#1335 (comment) comment。我们讨论的结果和你的描述是类似的,只不过实现上不大一样。
如下示例内容: <template>
<TagConsumer ref="rootConsumerRef">
<Tag name="infoBox">
<div class="info-box">
<!-- contentConsumerRef 不会上报自己的tagTree -->
<TagConsumer ref="contentConsumerRef">
<Tag name="content"> ... </Tag>
</TagConsumer>
<Tag name="confirm"> ... </Tag>
</div>
</Tag>
<ClickToRender />
</TagConsumer>
</template> 它保存的信息结构大概如下,不是一棵tagTree,而是两颗独立的tagTree,消费方通过tagConsumer的ref即可调用接口方法: // rootConsumerRef的tagTree
{
name: "_root_",
instance: null,
children: [
{
name: "infoBox",
instance: ...,
children: [
{
name: "confirm",
instance: xxx,
children: []
}
]
}
]
}
// contentConsumerRef的tagTree
{
name: "_root_",
instance: null,
children: [
{
name: "content",
instance: xxx,
children: []
}
]
} |
你说的做法是把我提到的 root 跟 consumer 合成一个 API(
|
@nighca 寒星老师,根据你上面的描述,我的理解是:
对吗? |
是的
那样这个“只负责收集子
从实现的角度看是这样的;只是从使用者的角度看,更像是:每个 只是在实现上,为了方便最后组装回一棵树,所以可能会实现成每个 |
#1335 (comment) 我好像没注意到你在这里提到的“两棵独立的树”这个事情;为啥会设计成有两棵(或者说多棵)树? |
这个是基于之前的设想,可以存在多个 |
我有点回忆起来为啥 Role 那边是设计成那样了;它的设计考虑的应该是:每个 RoleConsumer 获得到的数据是(可以由其包含的内容)确定的,即使在 RoleConsumer 外部添加更多的内容(包括更多的 RoleConsumer、Role),也不会影响这个 RoleConsumer 能消费到的节点树。 只是它的设计跟你上面提到的“两颗独立的tagTree”的设计有点区别,它预期嵌套的两个 RoleConsumer 对应的 tree 也是父子的关系,即,如果 RoleConsumer A 被 RoleConsumer B 包含,那么 A 所消费的 Role 节点树也是 B 所消费的 Role 节点树的子树;从你举的例子来看,你的设计里边它俩并不会是子树的关系,而是被切割开了,两个独立的树 它的“多根节点”的设计是有道理的,在项目变复杂之后,尤其是局部的代码跟外层的代码由不同的团队在维护、最终组装起来时,让局部的代码行为尽量不被其他的代码影响是有好处的。不过我们这里暂时没有这个诉求,即使将来有这个诉求,让“RootTag”支持嵌套就好了,不会 break 现有的设定 |
This PR has been deployed to the preview environment. You can explore it using the preview URL. Warning Please note that deployments in the preview environment are temporary and will be automatically cleaned up after a certain period. Make sure to explore it before it is removed. For any questions, contact the Go+ Builder team. |
以下修改来自于:#1331 (comment)
Tagging
Mask
考虑了 tag选择器 的形式查找元素,通过传入key数组找到目标组件。