Help us learn about your current experience with the documentation. Take the survey.

前端开发问题排查指南

遇到问题了?也许这个能帮到你 ¯\_(ツ)_/¯。

问题排查

本指南未包含您遇到的问题

如果您遇到的前端开发问题不在本指南中,请考虑更新本指南,添加您遇到的问题和可能的解决方案。这样,未来的开发者就能带着您的经验和知识,更成功地面对这些挑战。

测试问题

出现 Property or method 'nodeType' is not defined 错误,但您并未使用 nodeType

这个问题可能发生在 Vue 组件测试中,当某个断言失败时,Jest 尝试在控制台中打印差异时会抛出错误。有记录表明,使用 toEqual 且属性为数组也可能是导致此问题的因素。

请观看此视频获取深入的分析和调查。

解决方案 - 尝试克隆带有 Vue 监听器的对象

- expect(wrapper.findComponent(ChildComponent).props()).toEqual(...);
+ expect(cloneDeep(wrapper.findComponent(ChildComponent).props())).toEqual(...)

解决方案 - 尝试使用 toMatchObject 替代 toEqual

- expect(wrapper.findComponent(ChildComponent).props()).toEqual(...);
+ expect(wrapper.findComponent(ChildComponent).props()).toMatchObject(...);

toMatchObject 实际上改变了断言的性质,如果期望值中缺少某些项,它不会失败。

脚本问题

在 GitLab 仓库中运行脚本时出现 core-js 错误

以下命令假设您已在 ~/workspace/gdk 目录中设置了 GitLab 仓库。在 GitLab 仓库中运行脚本(如代码转换)时,可能会遇到类似这样的 core-js 问题:

~/workspace/gdk/gitlab/node_modules/core-js/modules/es.global-this.js:7
$({
^
TypeError: $ is not a function
    at Object.<anonymous> (~/workspace/gdk/gitlab/node_modules/core-js/modules/es.global-this.js:6:1)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Module._compile (~/workspace/gdk/gitlab/node_modules/pirates/lib/index.js:99:24)
    at Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Object.newLoader [as .js] (~/workspace/gdk/gitlab/node_modules/pirates/lib/index.js:104:7)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Object.<anonymous> (~/workspace/gdk/gitlab/node_modules/core-js/modules/esnext.global-this.js:2:1)

解决方案 - 尝试将脚本移至单独的仓库,并指向 GitLab 仓库中的文件

使用 Vue 组件问题

渲染使用 GlFilteredSearch 的组件,且该组件或其父组件使用 Vue Apollo

在尝试渲染我们的 GlFilteredSearch 组件时,您可能会在组件的 provide 函数中收到错误:

cannot read suggestionsListClass of undefined

目前,vue-apollo 尝试在组件生命周期的 beforeCreate 部分手动调用组件的 provide()。这意味着当 provide() 引用了 props(这些 props 实际上是在 created 之后才设置)时,就会出错。

有关更多上下文,请参阅此已关闭的 MR

解决方案 - 尝试将 apolloProvider 提供给顶级 Vue 实例选项

如果 VueApollo 看到在 $options 中提供了 apolloProvider,它将跳过手动运行 provide()

  new Vue(
    el,
+   apolloProvider: {},
    render(h) {
      return h(App);
    },
  );

Apollo Client 问题排查

写入缓存时出现控制台错误

如果您看到类似 Missing field 'descriptionHtml' while writing result 的错误,这意味着我们在向 Apollo 客户端缓存写入时没有遵循 GraphQL 响应结构。您似乎在 Web 应用程序中遇到了 GraphQL 错误(“Missing field ‘description’"),这可能与您处理 Apollo Client 缓存和数据更新的方式有关。错误堆栈跟踪提供了问题发生的 Apollo Client 代码特定部分的线索。

核心问题

错误 “Missing field ‘description’” 表明您的 GraphQL 查询期望响应中有一个名为 “description” 的字段,但您从后端收到的数据(或 Apollo Client 处理的数据)缺少该字段。这导致 Apollo Client 尝试用不完整的数据更新存储时失败。

要调试此问题,请按照以下步骤操作:

  1. 打开错误堆栈的开发者控制台
Missing field 'description' while writing result {
  "type": "DESCRIPTION",
  "lastEditedAt": null,
  "lastEditedBy": null,
  "taskCompletionStatus": null,
  "__typename": "WorkItemWidgetDescription"
}
  1. 仔细检查您的 GraphQL 查询,确保它请求了 “description” 字段。如果未包含,Apollo Client 将无法在响应中找到它。
  2. 后端可能没有在 “WorkItemWidgetDescription” 类型的响应中返回 “description” 字段。验证您的后端 API 是否按预期正确发送数据。
  3. 使用 cache.readQuery 方法检查 Apollo Client 缓存的内容。验证相关查询的缓存数据中是否存在 “description” 字段。
  4. 打开错误堆栈跟踪,表明问题可能与 Apollo Client 如何向其缓存写入数据有关。缓存可能未正确更新,导致缺少字段。
  5. 在您的 Apollo Client 代码中添加控制台日志(例如,在写入缓存之前和之后),以跟踪正在处理的数据并识别 “description” 字段可能缺失的位置。

解决方案

确保您在 Apollo Client 代码中使用正确的 writeQuerywriteFragment 方法来更新缓存,包含完整的数据,包括 “description” 字段。

您应该能够在堆栈跟踪中看到此问题的来源方法。确保在写入缓存时添加 “description” 字段。

使用相同变量的查询未被缓存

Apollo GraphQL 查询在以下几种情况下可能不会被缓存:

  1. 缓存未命中、部分缓存、查询失效或变更: 如果查询只返回部分数据,或者存在缓存未命中(当请求的部分数据不在缓存中时),Apollo 可能无法有效缓存结果。

    如果与查询相关的数据已被失效或更新,缓存可能没有有效信息。例如:

    使用变更(mutations)时,除非您配置了 refetchQueries 或在变更后手动更新缓存,否则缓存可能不会自动更新。

    例如:在第一个查询中,您有一些字段在后续查询中未被请求

query workItemTreeQuery($id: WorkItemID!, $pageSize: Int = 100, $endCursor: String) {
  workItem(id: $id) {
    namespace {
      id
    }
    userPermissions {
      deleteWorkItem
      updateWorkItem
    }
  }
}
query workItemTreeQuery($id: WorkItemID!, $pageSize: Int = 100, $endCursor: String) {
  workItem(id: $id) {
    namespace {
      id
+     fullPath
    }
    userPermissions {
      deleteWorkItem
      updateWorkItem
+     adminParentLink
+     setWorkItemMetadata
+     createNote
+     adminWorkItemLink
    }
  }
}
  1. fetchPolicy 设置: Apollo Client 使用 fetchPolicy 来控制查询如何与缓存交互。根据策略的不同,如果 fetchPolicy 设置为 no-cache,查询可能会完全绕过缓存。此策略确保查询的任何部分都不会写入缓存。每个查询直接从服务器获取数据,不将任何结果存储在缓存中,因此会多次获取查询。

  2. 当从不同的 Apollo Client 实例触发相同的查询时。可能是触发这两个查询的客户端来自不同的客户端实例。

  3. 缺少 id__typename: Apollo Client 使用 id__typename 来唯一标识实体并缓存它们。如果这些字段在您的查询响应中缺失,Apollo 可能无法正确缓存结果。

  4. 复杂或嵌套查询: 某些查询可能过于复杂或涉及嵌套查询,Apollo Client 可能难以正确缓存。如果返回的数据结构不能很好地映射到缓存架构,可能会发生这种情况,需要手动缓存管理。

  5. 分页查询: 对于涉及分页的查询(如使用 fetchMore 的查询),除非显式更新缓存,否则 Apollo 可能无法正确缓存结果。

在所有这些情况下,您可能需要配置 Apollo 的缓存策略或手动更新缓存,以有效处理查询缓存。