Open JimmyLv opened 5 years ago
variables
中的 productId 来自 $route.param
,使用 Nuxt 但 SSR 也有效。
query 部分完全可以由各依赖的子组件们来提供对应数据片段,即 fragments。
之前的整个 query 内容实在太大了!一个页面根本不需要这么多冗余数据,只取UI需要的数据,按需查询。
fragment PreSale on BffSpu {
preSaleAttribute {
startTime
endTime
scheduledDeliveryTime
}
spu {
isPreSale
}
}
fragment SEO on Product {
seo {
seoTitle
seoKeywords
seoDescription
}
}
fragment Promotion on PromotionActivity {
name
label
description
ruleDesc
id
beginTime
endTime
gifts {
name
number
}
}
fragment Brand on Product {
brand {
imageUrl
code
name
description
}
}
fragment Category on ProductCategory {
name
code
path
}
query productDetail($codes: [String!]!) {
shop {
bffSpusByCodes(codes: $codes) {
type
...PreSale
spu {
id
code
title
onShelves
productType
...SEO
promotions {
...Promotion
}
...Brand
categories {
...Category
}
images {
url
}
subTitle
description
inventory
sales
skus {}
bffSkus {}
}
}
}
ref: Using fragments - ApolloQuery | Vue Apollo
<!-- MessageList.vue -->
export default {
fragments: {
message: gql`
fragment message on Message {
id
user {
id
name
}
text
created
}
`
}
}
apollo: {
messages: gql`
query GetMessages {
messages {
...message
}
}
${this.$options.fragments.message}
`
}
但其实,为了不必要的 fragment 层级考虑,还有两种额外的处理方式,方便管理&解除耦合关系,同时也符合 vuex 中央集权式的理念。
当然,这也需要后端的支持,因为此时请求的是 /bff/graphqls
的复数 API url,而不再是 /bff/graphql
,客户端需要使用 BatchHttpLink:
import { BatchHttpLink } from 'apollo-link-batch-http'
const httpLinkOptions = {
uri,
fetch,
credentials: 'same-origin',
headers: {},
}
const httpLink = createHttpLink(httpLinkOptions)
const batchLink = new BatchHttpLink({
...httpLinkOptions,
uri: `${uri}s`,
})
query fetchDynamicOrganismData($codes: ProductCodeInput) {
... on Query {
... on Query {
Product: shop {
currency
decimalPlaces
productByCode(codes: $codes) {
id
code
title
onShelves
images {
url
__typename
}
salePrice {
amount
__typename
}
skus {
id
code
isEnabled
__typename
}
__typename
}
__typename
}
__typename
}
... on Query {
ProductCarousel: shop {
currency
decimalPlaces
productByCode(codes: $codes) {
id
code
title
onShelves
images {
url
__typename
}
salePrice {
amount
__typename
}
listPrice {
amount
__typename
}
skus {
id
code
isEnabled
__typename
}
__typename
}
__typename
}
__typename
}
__typename
}
}
用于合并时的代码(有待改进,现在过多 ... on Query
层级的重复):
import gql from 'fraql'
const getDynamicFragments = organismsList =>
Object.values(organisms)
.filter(comp =>
comp.fragment &&
!!comp.fragment.dynamic &&
organismsList.find(type => type === comp.name))
.map(comp => comp.fragment.dynamic)
.reduce((a, b) => gql`
fragment _ on Query {
${a}
${b}
}
`)
而且需要通过 this.$root.$children.filter(child => !!child.$options.fragment);
获取每个子组件的fragment片段。
两者对比可以看出,option 1 明显要简单直接一些:
潜在的优化包括:option 1 能够做到 query 的分时,从而让组件自由选择在 SSR 端或 client 端进行数据请求,从而更精确控制请求性能或是 SEO 数据。 反而 option 2 不能做到这一点,因为子组件只提供 fragment 而无法主动发送请求,这个 fragment 是static的等着被人来使用,而且父组件(一般是 route 层级,即 Nuxt 的 pages/xxx )使用的时候也只能选择一种使用方式。
关于 GraphQL 作为后端 BFF 的定位和实践,可能面临的问题:
团队配置:这一点主要是考虑到 HC,每个团队都配比了后端开发(一般技术栈为 Java),那么其职责无外乎数据的合并或筛选,即业务逻辑组合。所以 GraphQL 的后端选择是否需要切换至 Apollo Server 即 Node 技术栈呢?而且 BFF 的概念本身就属于(大)前端,由前端来写会更加舒适,况且为了 SEO和性能,一般都已经有了一台 Node Server 用于 SSR,与其并存的 GraphQL Apollo Server 也是非常合适并且节省资源和运维成本的。
(中台)服务依赖:如果中台团队足够出色,API设计丰富且符合 REST 规范(90%做不到),那么就不需要所谓的洗数据这一说法了吧。反之来说,团队配置当中的 Java 后端开发,一般就是在洗中台服务所提供的"脏"数据,梳理数据流程使其符合前端业务需要。工作量主要浪费的就在于这里,也就是说(领导理想情况下)让中台想省下来的功夫,最终又落到了前台后端开发这里。中台服务本身由于层层封装,或者是压根儿没有封装完全是数据表格映射/透传而已(俗称“捅”),实际上调用中台服务比直接操作数据库还痛苦。
Apollo Server 本身的定位:
graphql mutation 要以input为入参 pages/xxx apollo.js components.js query就该离得最近, 子组件也由自己去取 或者就使用fragememts吧,如果想合并请求,或者是考虑page route 入参 SSR渲染,只可能发生在fetch或者async data,但是这样的话 apollo怎么到data呢?
::: tip You have access to this in options like variables, even on the server! :::