Skip to content

场景技巧 第二部分

如何处理前端请求的竞态问题?

前端请求的竞态问题,是指多个前端请求同时发起,导致服务器资源竞争,导致请求响应延迟,甚至出现请求超时的情况。例如搜索框场景,在用户快速输入的情况下,A 请求的结果还未返回,B 请求就已经发起,那么当 A 请求结果拿到时就变成 B 的结果了。

处理方式如下:

  1. 取消上一个请求,防止结果不匹配。主要是利用AbortController API在请求发起时来取消上一个未完成的请求,以此来保证请求的顺序性。
js
let controller;

async function search(query) {
  if (controller) {
    controller.abort(); // 取消上一个请求
  }

  controller = new AbortController();

  try {
    const response = await fetch(`/api/search?q=${query}`, {
      signal: controller.signal,
    });
    const data = await response.json();
    // 处理并展示结果
  } catch (error) {
    if (error.name !== "AbortError") {
      console.error("请求失败:", error);
    }
  }
}

// 示例:监听输入事件
const input = document.querySelector("input");
input.addEventListener("input", (e) => {
  search(e.target.value);
});

2.使用请求标识。为每个请求分配唯一标识,并在请求响应时检查是否与当前请求匹配,只有最新的结果会被处理,以此来避免请求结果的错乱。

js
let latestRequestId = 0;

async function search(query) {
  const requestId = ++latestRequestId;

  const response = await fetch(`/api/search?q=${query}`);
  const data = await response.json();

  if (requestId === latestRequestId) {
    // 处理并展示结果
  }
}

// 示例:监听输入事件
const input = document.querySelector("input");
input.addEventListener("input", (e) => {
  search(e.target.value);
});

3.请求防抖。在用户输入时,延迟请求的发送,减少请求发送的频率。

js
function debounce(func, delay) {
  let timeoutId;
  return function (...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(this, args), delay);
  };
}

async function search(query) {
  const response = await fetch(`/api/search?q=${query}`);
  const data = await response.json();
  // 处理并展示结果
}

const debouncedSearch = debounce(search, 300);

// 示例:监听输入事件
const input = document.querySelector("input");
input.addEventListener("input", (e) => {
  debouncedSearch(e.target.value);
});

推荐方案是结合请求取消和防抖,这个适用于多数的搜索场景,简单代码示例如下:

js
let controller;
let timeoutId;

function debounce(func, delay) {
  return function (...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(this, args), delay);
  };
}

async function search(query) {
  if (controller) {
    controller.abort();
  }

  controller = new AbortController();

  try {
    const response = await fetch(`/api/search?q=${query}`, {
      signal: controller.signal,
    });
    const data = await response.json();
    // 处理并展示结果
  } catch (error) {
    if (error.name !== "AbortError") {
      console.error("请求失败:", error);
    }
  }
}

const debouncedSearch = debounce(search, 300);

// 示例:监听输入事件
const input = document.querySelector("input");
input.addEventListener("input", (e) => {
  debouncedSearch(e.target.value);
});

如果处理 TypeScript 项目中复杂的类型定义?

对于TypeScript 项目中复杂的类型定义,可以使用一些高级特性来处理,例如泛型、条件类型、类型推断等。

ts
// 使用条件类型实现类型安全的API响应
type ApiResponse<T> = {
  success: true;
  data: T;
} | {
  success: false;
  error: string;
};
// 泛型函数处理API响应
function handleResponse<T>(response: ApiResponse<T>): T {
  if (response.success) {
    return response.data;
  }
  throw new Error(response.error);
}
//实用工具类: 部分可选
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

如何制定大型项目的 CSS 架构方案?

1.首先明确架构的设计原则。

  • 可预测性原则:样式行为应直观可预期。

  • 可复用性原则:避免重复,建立可复用模式。

  • 可维护性原则:易于修改和扩展。

  • 低耦合性原则:组件样式独立,减少相互影响。

  • 性能优先原则:控制选择器复杂度和文件体积。

2.分层架构设计。将样式分层,例如基础层、布局层、组件层、页面层等。

  • 基础层:包含设计变量、CSS Reset、基础样式和通用工具类等。

  • 布局层:包含网格系统(Grid/Flexbox)、主要的页面结构、响应式断点管理等。

  • 组件层:原子组件(按钮、输入框等)、组合组件(卡片、导航栏等)、业务组件(表单、表格等)以及状态类(激活、禁用、选中等)。

  • 页面层:特定页面的特定样式。注意:尽量少用页面特定样式。

3.技术选型策略。比较预处理器、CSS模块化、原子化CSS等技术的适用场景和优缺点,选择合适的技术方案。

4.文件组织结构。

md
styles/

├── base/              # 基础层
│   ├── _variables.scss
│   ├── _reset.scss
│   └── _typography.scss

├── layout/            # 布局层
│   ├── _grid.scss
│   └── _containers.scss

├── components/        # 组件层
│   ├── _buttons.scss
│   └── _card.scss

├── utilities/         # 工具类
│   ├── _spacing.scss
│   └── _display.scss

├── pages/             # 页面层(尽量少用)
│   └── _product.scss

└── main.scss          # 主入口文件

5.性能优化策略。使用打包工具拆分CSS、移除未使用的CSS。

6.团队协作规范。

  • 命名约定:遵循BEM命名规范,例如.block__element--modifier

  • 样式编写顺序,定位->盒模型->排版->视觉->动画。

  • 避免过度嵌套(例如Sass中不超过3层),避免使用!important,选择器特异性不超过(0,2,0)(即同时有两个类选择器作用于同一个元素)。

Last updated: