关于组件数据依赖加载方式的一些疑问

#1

因为简聊做了数据界面分离, 这个问题就显得比较清晰了.
我大概解释一下问题的来源, 看看有没有遇到过或者想到好的解决方案…

首先按照 GraphQL, 一个 Component 渲染, 会依赖一些数据, 在 GraphQL 里是用代码声明出来的
我们目前还没有的用 GraphQL, 所以只能是手动进行处理(这里还存在一些数据结构不对应的问题)

以往我们做的是按照 Backbone View 方式, View 初始化时从 didMount 开始加载数据,
数据加载完成然后渲染对应的 View… 然而, 这种分散的数据加载非常不利于优化
比如说希望一次同时发起需要的请求来加快抓取的速度, 就需要实现同时加载新视图所有依赖的数据
所以当前的方案的是: 手动生成数据依赖 -> 加载依赖数据 -> 渲染界面

问题出在手动生成数据依赖上, 我们发现这个代码非常啰嗦, 并不方便开发新功能
同时存在的问题是, 有时一次数据加载实际上是 A 和 B 两步, 而且 B 还依赖 A 作为参数
所以实际代码里一个是手动写数组生成依赖, 还有是先读取一部分依赖再读取另一部分.

我正在考虑这个过程的代码如何抽象成为一个方案?

#2

看起来是没有用rest而是用单个访问点

那合并一个时间段内的请求怎么样

比如每20ms内的数据请求合并在一个HTTP请求中

#3

我的 API 是 Restful 的, 然而打开简聊你可以看到加载的请求并不仅仅是一个.
后面的没看懂…

#4

我理解错了

这个是指什么?如果是restful的接口,对不同的资源不是肯定会有不同时的HTTP请求吗?

#5

这个没错, 因为需要的数据通过 Restful API 只能分开抓, 然而这种代码不好控制, 也不好优化.

#7

之前回复的时候对这个探讨理解不深,不过今天在重构一部分代码的时候回忆起了这个帖子,想分享下我现在的想法。不知道你们项目用的是什么,我的个人项目用的是Redux。

首先是我一些理解:

视图始终是先通过计算store中的数据,然后根据结果render。

View :: Model -> View

对应到React+Redux,再利用react-redux中的connect,这个思路变的很清晰。

function mapStateToProps (state) {
   return { ... }
}

@connect(mapStateToProps, {})
class Container extends Component {
  render () {
    ...
  }
}

通过mapStateToProps进行一些计算,再交给react组件来render。

然后回到实际情况:我的一个View依赖数据A,然后需要根据数据A的结果来请求数据B。对于呈现的内容而言,暂时没有请求到的数据用spinner占位或者就先空着。

由于View :: Model -> View这是个同步的过程,那么现在的代码差不多是这样的:

function mapStateToProps (state, props) {
  const cid = props.params.cid
  const {
    company: {
      data: companyData, loadedCompany,
      loadingCompany, loadFailedCompany
    },
    project: {
      data: projectData,
      companyProjectsLoading,
      companyProjectsLoaded
    }
  } = state

  // 数据A
  const currentCompany = companyData[cid]
  const needFetchCompany = !isDefined(currentCompany)
  const companyFetching = has(loadingCompany, cid)
  const companyFetchFailed = has(loadFailedCompany, cid)

  // 数据B,它是依赖数据A的
  const projectsUnderCompany = currentCompany &&
    sortBy(currentCompany.projects.map(resolveProp(projectData)).filter(isDefined), 'name') || []
  const needFetchProjectsUnderCompany = currentCompany &&
    !has(companyProjectsLoading, cid) && !has(companyProjectsLoaded, cid) &&
    currentCompany.projects.length > projectsUnderCompany

  return ({
    currentCompany, needFetchCompany,
    companyFetching, companyFetchFailed,

    projectsUnderCompany,
    needFetchProjectsUnderCompany
  })
}

由于整个模型是同步的,只能通过一些flag来表示加载状况,问题就是依赖关系稍稍复杂些就变的难以维护了。目前在考虑一个方案,想法是这样的:

  • 对reducer维护的state的结构做一些约定,这样的话可以写一些util函数用来统一地判断数据是处于fetching还是loaded或者failed
  • mapStateToProps计算的一部分结果可以抽象一个统一的Maybe类型(或者Either),交给View来呈现
  • 我现在统一通过needFetchProjectsUnderCompany这种标志位来判断是否可以开始或者需要fetch数据,包括对之前结果有依赖的请求也是,而React组件中统一用一个ensureFetchData方法来对这类标志位进行处理(即发送相应的请求),那么可能可以抽象出一个HOC来做这个事情,不过还在实践中。

这是我今天重新思考项目中一部分代码得出的结果。另外对目前简聊使用的方案表示好奇。 :blush:

#8

Reducer 细节一些内容没看懂, 我们跟 Redux 的差别已经越来越越大的. 数据依赖的加载, 简聊是等到所有数据加载完成, 然后才开始渲染的, 这一点和你描述的内容有区别.

数据依赖的部分实际上和 Relay 关系非常紧密, 只是 Relay 能借助 GraphQL 精确分析出依赖的数据, 简聊完全没有那样一套完整的方案来处理, 当前依赖手工分析依赖.