组件
前端组件是独立的 Vue 应用或 Vue 组件树,可以添加到页面上来处理部分功能。
在构建组件时,我们应该遵循下面描述的几个原则。
必须使用 Vue Apollo
所有组件都应使用相同的技术栈(Vue + Apollo Client)。 为此,我们必须将 Vue Apollo 添加到应用根目录(如果我们使用组件作为组件)或直接提供给组件。对于侧边栏组件,使用 issuable Apollo Client 和 Apollo Provider:
import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
import { apolloProvider } from '~/graphql_shared/issuable_client';
function mountConfidentialComponent() {
new Vue({
apolloProvider,
components: {
SidebarConfidentialityWidget,
},
/* ... */
});
}必需的注入
所有可编辑的侧边栏组件都应使用 SidebarEditableItem 来处理折叠/展开状态。此组件需要在应用根目录中提供 canUpdate 属性。
避免全局状态映射
我们 aim to make widgets as reusable as possible. That’s why we should avoid adding any external state bindings to widgets or to their child components. This includes Vuex mappings and mediator stores.
组件职责
组件负责获取和更新其设计的实体(指派人、迭代等)。 这意味着组件应该始终获取数据(如果数据尚未在 Apollo 缓存中)。 即使我们为组件提供初始值,它也应该在后台执行 GraphQL 查询 以存储在 Apollo 缓存中。
最终,当我们拥有 Apollo Client 缓存作为全局应用状态时,我们将不需要 将初始数据传递给侧边栏组件。然后它将能够从缓存中检索数据。
使用 GraphQL 查询和变更
我们需要组件能够灵活地处理不同的实体(epics、issues、merge requests 等)。 因为我们需要为不同的侧边栏使用不同的 GraphQL 查询和变更,我们创建了 mappings:
export const assigneesQueries = {
[TYPE_ISSUE]: {
query: getIssueParticipants,
mutation: updateAssigneesMutation,
},
[TYPE_MERGE_REQUEST]: {
query: getMergeRequestParticipants,
mutation: updateMergeRequestParticipantsMutation,
},
};为了处理查询更新的相同逻辑,我们别名查询字段。例如:
group或project变成workspaceissue、epic或mergeRequest变成issuable
不幸的是,Apollo 为别名字段分配了 typename 为 undefined,所以我们需要显式获取 __typename:
query issueConfidential($fullPath: ID!, $iid: String) {
workspace: project(fullPath: $fullPath) {
__typename
issuable: issue(iid: $iid) {
__typename
id
confidential
}
}
}与其他 Vue 应用通信
如果我们需要将组件状态的变化(例如,成功变更后) 通知给父应用,我们应该发出一个事件:
updateAssignees(assigneeUsernames) {
return this.$apollo
.mutate({
mutation: this.$options.assigneesQueries[this.issuableType].mutation,
variables: {...},
})
.then(({ data }) => {
const assignees = data.issueSetAssignees?.issue?.assignees?.nodes || [];
this.$emit('assignees-updated', assignees);
});
}有时,我们想要监听不同 Vue 应用(如 NotesApp)的变化。
在这种情况下,我们可以使用一个无渲染组件,它导入客户端并监听特定查询:
import { fetchPolicies } from '~/lib/graphql';
import { confidentialityQueries } from '~/sidebar/constants';
import { defaultClient as gqlClient } from '~/graphql_shared/issuable_client';
created() {
if (this.issuableType !== IssuableType.Issue) {
return;
}
gqlClient
.watchQuery({
query: confidentialityQueries[this.issuableType].query,
variables: {...},
fetchPolicy: fetchPolicies.CACHE_ONLY,
})
.subscribe((res) => {
this.setConfidentiality(issuable.confidential);
});
},
methods: {
...mapActions(['setConfidentiality']),
},合并请求组件
参考特定的合并请求组件框架文档。