当前位置: 首页 > news >正文

Jest与Cypress终极指南:前端测试选型、实战与融合策略

1. 项目概述:为什么我们需要这份“终极指南”?

干了这么多年前端,我越来越觉得,测试不是“可选项”,而是“必选项”。尤其是在今天,应用越来越复杂,迭代越来越快,一个不经意的改动可能就会引发连锁反应。但一提到测试,很多开发者就头疼:单元测试、集成测试、端到端测试……框架那么多,Jest、Cypress、Mocha、Puppeteer,到底该选哪个?怎么用?更让人纠结的是,Jest和Cypress这两个名字经常被放在一起讨论,但它们解决的问题层面其实大不相同。我见过不少团队,要么把所有测试都塞给Jest,结果端到端测试写得痛苦不堪;要么一上来就用Cypress写所有逻辑测试,导致测试运行缓慢且脆弱。

这份“终极指南”和“速查手册”的诞生,正是为了解决这个核心痛点。它不是一个简单的功能列表对比,而是基于我多年在真实项目中搭建、维护和优化测试套件的实战经验,为你梳理出的一条清晰路径。我们将深入Jest和Cypress的骨髓,对比它们的设计哲学、适用场景、核心API和最佳实践,并最终给你一套可以“抄作业”的速查手册。无论你是刚接触测试的新手,还是想优化现有测试策略的老手,都能在这里找到答案:在什么情况下,用哪个工具,以及具体怎么用,才能最高效地保障你的代码质量。

2. 核心理念拆解:Jest与Cypress的本质区别

在开始对比具体功能前,我们必须先理解两者的根本定位。把它们简单地视为“两个测试工具”是最大的误解。实际上,它们是针对不同测试层级、拥有不同运行时环境的“专业选手”。

2.1 Jest:专注且强大的“代码逻辑检察官”

Jest 是一个基于 Node.js 环境的 JavaScript 测试框架。它的核心关注点是你的代码逻辑本身。想象一下,你写了一个函数calculateDiscount(price, isMember),Jest 的工作就是帮你验证:给定不同的priceisMember值,这个函数返回的结果是否正确。它运行在 Node.js 进程中,与浏览器环境完全隔离。

核心设计哲学:

  • 零配置启动:这是Jest最大的卖点之一。开箱即用,内置断言库、Mock系统、覆盖率报告和快照测试,你几乎不需要额外安装和配置其他库。
  • 隔离与速度:Jest 默认会并行运行测试,并且每个测试文件都在独立的沙盒环境中执行,保证了测试的独立性和运行速度。它通过高效的文件系统监听和智能测试筛选(只运行与改动文件相关的测试)来优化开发体验。
  • 模拟一切:Jest 提供了极其强大的 mocking 能力,可以模拟函数、模块、甚至定时器。这让你能将被测单元与其依赖隔离开,专注于测试单元本身的逻辑。

一句话总结:Jest 是你验证“代码是否按预期执行计算和逻辑”的首选工具,它运行在构建时或开发环境中。

2.2 Cypress:掌控真实浏览器的“用户体验守护者”

Cypress 是一个端到端(E2E)测试框架。它的核心关注点是你的应用在真实浏览器中,作为一个整体,是否按预期与用户交互。它关心的是:用户点击这个按钮,模态框会弹出吗?填写表单并提交后,页面是否会跳转并显示成功消息?Cypress 测试运行在一个真实的、无头的(或可视的)浏览器中。

核心设计哲学:

  • 一切在浏览器中运行:Cypress 测试代码和你的应用程序代码运行在同一个浏览器循环中。这消除了传统 Selenium 等工具的“网络延迟”问题,使得测试更稳定、命令执行更快。
  • 时间旅行与实时重载:Cypress 的 Test Runner 界面是其杀手锏。你可以看到每个命令执行时的应用状态、网络请求、甚至 Console 输出。测试失败时,能立刻看到当时的快照,极大简化了调试过程。
  • 面向现代Web开发:它对单页应用(SPA)有原生级的支持,能自动等待元素出现、网络请求完成,避免了在测试中到处写setTimeout的尴尬。

一句话总结:Cypress 是你验证“用户与完整应用交互的流程是否畅通”的首选工具,它运行在真实的浏览器环境中。

注意:一个常见的误区是试图用 Jest 去做 Cypress 的工作(比如用 JSDOM 模拟浏览器操作),或者用 Cypress 去写大量的单元测试。这就像用螺丝刀去敲钉子,用锤子去拧螺丝,不仅效率低下,而且会让测试变得脆弱难维护。正确的姿势是让它们各司其职。

3. 深度功能对比与选型指南

理解了核心理念,我们来一场面对面的“功能对决”。我会从多个维度进行对比,并给出清晰的选型建议。

3.1 测试类型与覆盖范围

这是最根本的选型依据。

维度JestCypress
主要测试类型单元测试 (Unit)集成测试 (Integration)端到端测试 (E2E)组件测试 (Component Testing)
测试范围单个函数、模块、类或一组相关模块的逻辑。完整的用户流程,跨越多个页面或视图,涉及网络、数据库(如有)等外部依赖。
测试目标代码逻辑的正确性、边界条件、错误处理。用户界面的交互、功能流程的完整性、跨模块集成。
类比检查汽车的发动机(代码逻辑)是否工作正常。坐进汽车,实际驾驶(用户操作)看能否从A点开到B点。

选型建议:

  • 你需要测试一个工具函数、一个 React/Vue 组件的业务逻辑(非UI渲染)、一个API服务层?选 Jest。
  • 你需要测试用户从登录、搜索商品、加入购物车到支付的完整流程?选 Cypress。

3.2 运行环境与执行速度

环境决定了能力边界,也直接影响开发体验。

维度JestCypress
运行环境Node.js 进程。可以使用 JSDOM 模拟一个基础的浏览器DOM环境。真实的浏览器(基于Chromium内核)。测试代码与应用代码同源执行。
执行速度非常快。在内存中运行,并行执行,适合在每次保存代码时运行。相对较慢。需要启动浏览器、加载页面、执行操作。适合在提交前或CI/CD流水线中运行。
资源访问可以直接访问和操作文件系统、环境变量、服务器端代码。受浏览器沙盒限制,不能直接访问文件系统或数据库。需要通过插件或API间接操作。
调试使用标准的 Node.js 调试工具,或 Jest 提供的--inspect标志。体验极佳。内置的 Test Runner 提供时间旅行调试、实时DOM查看、Console输出、网络请求监控。

实操心得:我们团队的策略是“分层测试,各取所长”。在本地开发时,Jest 测试(单元/集成)会在每次文件变化时自动运行,提供即时反馈。而 Cypress E2E 测试则配置为在git push前或 nightly build 中运行,作为最后一道质量防线。不要把缓慢的E2E测试放到开发的热路径上,那会严重拖慢开发效率。

3.3 API 与语法风格

两者的API设计也反映了其不同的哲学。

Jest: 简洁、函数式、以匹配器(Matchers)为中心Jest 的断言读起来像自然语言,得益于其丰富的匹配器。

// Jest 测试示例 describe('购物车计算逻辑', () => { test('应正确计算含税总价', () => { const cart = new ShoppingCart(); cart.addItem({ price: 100, quantity: 2 }); cart.addItem({ price: 50, quantity: 1 }); const total = cart.calculateTotal(0.1); // 10% 税率 // 使用匹配器进行断言 expect(total).toBe(275); // (100*2 + 50*1) * 1.1 = 275 expect(cart.items).toHaveLength(2); expect(cart).toHaveProperty('isEmpty', false); }); test('空购物车总价应为0', () => { const cart = new ShoppingCart(); expect(cart.calculateTotal(0.1)).toBe(0); expect(cart.isEmpty).toBeTruthy(); }); });

Cypress: 链式调用、命令式、以操作为中心Cypress 的命令返回一个 Promise-like 对象,支持链式调用,模拟用户操作序列。

// Cypress 测试示例 describe('用户登录流程', () => { it('成功登录后应跳转到仪表盘', () => { // 1. 访问登录页 cy.visit('/login'); // 2. 填写表单 cy.get('input[name=email]').type('user@example.com'); cy.get('input[name=password]').type('securePassword123'); // 3. 提交表单并断言 cy.get('form').submit(); // Cypress 会自动等待网络请求和页面跳转 cy.url().should('include', '/dashboard'); cy.get('.welcome-message').should('contain', '欢迎回来'); }); it('登录失败应显示错误信息', () => { cy.visit('/login'); cy.get('input[name=email]').type('wrong@email.com'); cy.get('input[name=password]').type('wrongpass'); cy.get('form').submit(); // 断言错误提示元素出现并包含特定文本 cy.get('.error-toast') .should('be.visible') .and('contain', '邮箱或密码错误'); }); });

选型建议:语法本身没有优劣,取决于场景。Jest 的语法更适合描述“状态”和“结果”,而 Cypress 的语法天然适合描述“动作”和“流程”。

4. 实战配置与核心环节实现

光说不练假把式。我们来看看如何在项目中实际设置和使用它们。

4.1 Jest 项目配置与核心特性实战

初始化与基础配置:对于现代前端项目(如使用 Create React App, Vue CLI),Jest 通常已预配置好。对于手动配置的项目:

npm install --save-dev jest @types/jest

package.json中添加:

{ "scripts": { "test": "jest", "test:watch": "jest --watch", "test:coverage": "jest --coverage" }, "jest": { "testEnvironment": "jsdom", // 如需测试涉及DOM的代码(如React组件) "collectCoverageFrom": ["src/**/*.{js,jsx,ts,tsx}", "!src/**/*.d.ts"], "setupFilesAfterEnv": ["<rootDir>/jest.setup.js"] // 每个测试文件运行前的初始化脚本 } }

核心特性1: Mocking(模拟)Mocking 是单元测试的灵魂。Jest 让它变得非常简单。

// 模拟一个模块 import axios from 'axios'; jest.mock('axios'); // 自动模拟整个axios模块 test('fetchUser 调用API并返回数据', async () => { const mockUser = { name: 'John Doe' }; axios.get.mockResolvedValue({ data: mockUser }); // 模拟成功的响应 const user = await fetchUser(1); // 你的业务函数 expect(axios.get).toHaveBeenCalledWith('/api/users/1'); expect(user).toEqual(mockUser); }); // 模拟一个函数 const utils = { calculateAge: (birthYear) => new Date().getFullYear() - birthYear, }; jest.spyOn(utils, 'calculateAge').mockReturnValue(30); // 无论传入什么,都返回30 test('使用模拟年龄', () => { expect(utils.calculateAge(1990)).toBe(30); // 始终返回30 });

核心特性2: 快照测试(Snapshot Testing)非常适合测试UI组件或配置对象的输出是否意外改变。

// React 组件测试示例 (需配合 react-test-renderer 或 @testing-library/react) import renderer from 'react-test-renderer'; import Button from './Button'; test('Button 组件渲染正确', () => { const tree = renderer.create(<Button label="点击我" />).toJSON(); expect(tree).toMatchSnapshot(); // 第一次运行会生成一个快照文件 }); // 如果后续组件渲染输出改变,测试会失败。你需要检查是预期变更还是bug,如果是预期变更,运行 `jest --updateSnapshot` 更新快照。

注意事项:快照测试不能替代基于断言的测试。它更像一个“变更报警器”。滥用快照(如对大对象或经常变化的UI)会导致快照难以维护。最佳实践是只对小的、稳定的输出使用快照。

4.2 Cypress 项目配置与最佳实践

初始化与目录结构:

npm install --save-dev cypress npx cypress open # 首次运行会初始化项目结构

这会创建cypress/目录,包含:

  • e2e/: 存放你的端到端测试用例文件(.cy.js)。
  • fixtures/: 存放静态测试数据(如.json文件)。
  • support/:commands.js用于自定义命令,e2e.js是测试运行前的全局入口。
  • cypress.config.js: Cypress 主配置文件。

配置示例 (cypress.config.js):

const { defineConfig } = require('cypress'); module.exports = defineConfig({ e2e: { baseUrl: 'http://localhost:3000', // 你的应用开发服务器地址 viewportWidth: 1280, viewportHeight: 720, specPattern: 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}', supportFile: 'cypress/support/e2e.js', setupNodeEvents(on, config) { // 可以在这里配置插件任务,如读取环境变量、连接数据库等 }, }, });

核心实践1: 使用自定义命令避免代码重复如果某个操作(如登录)在多个测试中重复,将其抽象为自定义命令。

// cypress/support/commands.js Cypress.Commands.add('login', (email, password) => { cy.session([email, password], () => { // 使用 session 命令加速重复登录 cy.visit('/login'); cy.get('input[name=email]').type(email); cy.get('input[name=password]').type(password); cy.get('form').submit(); cy.url().should('include', '/dashboard'); }); }); // 在测试中使用 it('登录后可以访问个人资料', () => { cy.login('user@example.com', 'password123'); cy.visit('/profile'); // ... 其他断言 });

核心实践2: 数据管理与隔离E2E测试要避免测试间相互影响。使用cy.request()@cypress/fixtures来准备和清理测试数据。

// 使用 fixtures 加载静态数据 beforeEach(() => { cy.fixture('user.json').as('userData'); // 将fixture数据别名化 }); it('使用fixture数据创建用户', function() { // 注意使用 function() 以访问 `this` const user = this.userData; cy.visit('/signup'); cy.get('input[name=name]').type(user.name); // ... 填充其他字段 }); // 使用 cy.request() 与后端API交互,准备动态数据 beforeEach(() => { cy.request('POST', '/api/test/reset-database'); // 假设你有一个测试专用的重置接口 cy.request('POST', '/api/test/create-user', { username: 'testuser' }); });

5. 速查手册:场景化命令与配置对照

这是可以直接“抄作业”的部分。我整理了最常见的任务在 Jest 和 Cypress 中分别如何实现。

5.1 常用断言/验证速查

任务描述Jest 写法Cypress 写法
等于某个值expect(value).toBe(42);cy.wrap(value).should('eq', 42);
深度等于对象/数组expect(obj).toEqual({a:1});cy.wrap(obj).should('deep.equal', {a:1});
为真/假expect(isTrue).toBeTruthy();cy.wrap(isTrue).should('be.true');
包含子串/元素expect(str).toContain('hello');
expect(arr).toContain('item');
cy.wrap(str).should('include', 'hello');
cy.get('ul li').should('contain', 'item');
匹配正则表达式expect(str).toMatch(/\d+/);cy.wrap(str).should('match', /\d+/);
数组长度expect(arr).toHaveLength(3);cy.get('ul li').should('have.length', 3);
元素可见/存在(不适用,Jest不直接操作DOM)cy.get('.btn').should('be.visible');
cy.get('.btn').should('exist');
元素具有属性/类(不适用)cy.get('input').should('have.attr', 'type', 'email');
cy.get('div').should('have.class', 'active');
URL匹配(不适用)cy.url().should('include', '/dashboard');
cy.url().should('eq', 'http://localhost:3000/');

5.2 异步操作处理速查

场景Jest 处理方式Cypress 处理方式
测试异步函数使用async/await或返回 PromiseCypress 命令自动处理异步,无需额外语法。
等待元素出现(不适用,通常用findBy*from RTL)cy.get('.loader', { timeout: 10000 }).should('not.exist');
cy.contains('数据加载成功', { timeout: 10000 }).should('be.visible');
等待网络请求Mock 掉请求,不实际等待。cy.intercept('GET', '/api/users').as('getUsers');
cy.visit('/users');
cy.wait('@getUsers');// 等待该请求完成
重试机制无内置重试,需手动实现或使用库。所有命令自带重试和超时机制,这是Cypress稳定的关键。直到断言成功或超时。

5.3 常用配置项速查

配置项Jest (jest.config.js)Cypress (cypress.config.js)
测试文件匹配testMatch: ['**/__tests__/**/*.js']specPattern: 'cypress/e2e/**/*.cy.js'
忽略文件/目录testPathIgnorePatterns: ['/node_modules/']excludeSpecPattern: '*.hot-update.js'
环境变量通过setupFiles加载.env文件,或使用process.env.VAR在配置中通过env: { key: 'value' }设置,测试中通过Cypress.env('key')访问。
全局前置/后置globalSetup,globalTeardown(文件)
setupFilesAfterEnv(每个测试文件前)
setupNodeEvents函数中的on('task', ...)可用于运行Node代码。支持before,beforeEach,afterEach,after(Mocha风格)。
测试报告器reporters: ['default', 'jest-junit']内置多种报告器,或使用cypress-mochawesome-reporter等插件。
并行运行maxWorkers: '50%'(使用--maxWorkers或配置)需要付费的Cypress Cloud服务来实现真正的并行和负载均衡。

6. 常见问题与排查技巧实录

在实际使用中,你一定会遇到各种“坑”。这里记录了我踩过的一些典型问题和解决方法。

6.1 Jest 常见问题

问题1: “Cannot find module” 或导入错误。

  • 原因:Jest 运行在 Node 环境,可能无法直接解析 Webpack 或 Babel 的特殊别名(@/)或非 JavaScript 文件(如图片、CSS)。
  • 解决:
    1. 对于路径别名,在jest.config.js中配置moduleNameMapper
      moduleNameMapper: { '^@/(.*)$': '<rootDir>/src/$1', // 将 @/ 映射到 src/ '\\.(css|less|scss|sass)$': 'identity-obj-proxy', // 模拟CSS模块 '\\.(jpg|jpeg|png|gif|svg)$': '<rootDir>/__mocks__/fileMock.js', // 模拟图片文件 }
    2. 确保安装了必要的 transformer,如babel-jest,并确保项目根目录有正确的 Babel 配置(如babel.config.js)。

问题2: 测试因“定时器”而表现不稳定或失败。

  • 原因:代码中使用了setTimeout,setInterval, 或Date.now(),在测试环境中时间行为不一致。
  • 解决:使用 Jest 的 Fake Timers。
    jest.useFakeTimers(); // 在 describe 或 test 前调用 test('定时器测试', () => { const callback = jest.fn(); setTimeout(callback, 1000); expect(callback).not.toHaveBeenCalled(); jest.advanceTimersByTime(1000); // “快进”时间 expect(callback).toHaveBeenCalled(); });

问题3: 如何测试涉及浏览器 API(如window,localStorage)的代码?

  • 原因:Node.js 环境没有这些 API。
  • 解决:设置testEnvironment: 'jsdom'。Jest 会使用 JSDOM 库来模拟一个基础的浏览器环境。对于更复杂的场景,可能需要手动在测试前设置全局变量。
    // 在测试文件或 setupFiles 中 global.localStorage = { store: {}, getItem(key) { return this.store[key]; }, setItem(key, value) { this.store[key] = value.toString(); }, clear() { this.store = {}; } };

6.2 Cypress 常见问题

问题1: 测试失败:“元素未找到”或“元素已分离”。

  • 原因:这是 Cypress 测试中最常见的问题,通常是因为应用状态在 Cypress 命令执行间隙发生了变化(如动态渲染、路由跳转)。
  • 排查与解决:
    1. 增加超时时间:cy.get('.my-element', { timeout: 10000 })
    2. 使用更稳定的选择器:避免使用.btn:nth-child(3)这种易变的选择器。优先使用>// 在自定义命令或 beforeEach 钩子中 beforeEach(() => { cy.session('myUser', () => { cy.visit('/login'); // ... 执行登录操作 }); // 会话恢复后,访问需要登录的页面 cy.visit('/dashboard'); });这能极大提升测试套件的运行速度。

问题3: 如何处理跨域问题?

  • 原因:Cypress 默认限制访问与当前baseUrl不同源的超级域名。
  • 解决:cypress.config.js中设置chromeWebSecurity: false。但更好的做法是,在测试环境中让你的前端和后端API使用同源(例如,通过开发服务器代理API请求),或者使用cy.request()直接与后端API通信来绕过浏览器限制。

问题4: CI/CD 环境中 Cypress 运行失败,但本地却成功。

  • 排查思路:
    1. 环境差异:检查 CI 环境的环境变量(如 API 地址、密钥)是否与本地一致。
    2. 资源加载:CI 服务器可能网速较慢或资源不同。增加命令超时,或使用cy.intercept()确保关键资源加载完成。
    3. 数据状态:确保 CI 每次运行前都有一个干净的、已知的数据库状态。使用cy.request()调用测试专用的重置接口。
    4. 视频和截图:在 CI 配置中启用video: truescreenshotOnRunFailure: true。失败时的视频和截图是定位问题的黄金资料。
    5. 使用 Cypress Cloud:虽然付费,但其提供的并行化、负载均衡和失败重试功能,能显著提升CI环境的测试稳定性和速度。

7. 融合策略与进阶架构

在大型项目中,Jest 和 Cypress 不是二选一,而是协同作战。我推荐一种经典的“测试金字塔”实践策略。

测试金字塔模型:

  1. 底层(大量):Jest 单元测试。覆盖所有工具函数、工具类、组件逻辑、状态管理(如 Redux reducer)。目标是快速、独立、高覆盖率。这构成了质量的基础。
  2. 中层(适量):Jest 集成测试 + Cypress 组件测试。
    • Jest集成测试:测试多个模块协同工作,例如一个 React 容器组件与其子组件、服务的交互。可以使用@testing-library/react等库。
    • Cypress组件测试:Cypress 10+ 提供了强大的组件测试功能,能在一个真实的浏览器环境中单独挂载并测试你的UI组件(如 Vue/React组件),交互测试比Jest+JSDOM更真实。
  3. 顶层(少量):Cypress 端到端测试。只覆盖最关键、最核心的用户业务流程(如注册-登录-核心功能-下单)。目标是验证整个系统作为一个整体是否工作。数量要精,维护成本高。

在同一个项目中组织它们:

my-project/ ├── src/ │ ├── __tests__/ # Jest 单元/集成测试(与源码相邻) │ │ ├── utils.test.js │ │ └── components/ │ │ └── Button.test.js │ └── ... ├── cypress/ │ ├── e2e/ # Cypress 端到端测试 │ │ ├── login.cy.js │ │ └── checkout.cy.js │ ├── component/ # Cypress 组件测试(可选) │ │ └── Button.cy.js │ └── ... ├── jest.config.js ├── cypress.config.js └── package.json

package.json中配置脚本:

{ "scripts": { "test:unit": "jest", // 运行所有Jest测试 "test:unit:watch": "jest --watch", "test:e2e": "cypress run", // 无头模式运行所有E2E测试 "test:e2e:open": "cypress open", // 打开Cypress Test Runner "test:component": "cypress run --component", // 运行组件测试 "test": "npm run test:unit && npm run test:e2e" // 一键运行所有测试 } }

这套组合拳打下来,你的前端应用就拥有了从代码逻辑到用户体验的全方位质量防护网。记住,没有“银弹”,最好的测试策略永远是让合适的工具去做它最擅长的事。Jest 是你的逻辑卫士,Cypress 是你的流程哨兵,两者结合,方能构筑坚不可摧的质量防线。

http://www.gsyq.cn/news/1579798.html

相关文章:

  • 9332张真实火灾场景图,火焰与烟雾独立标注,VOC格式开箱即用
  • Python的__getattribute__审计追踪
  • MATLAB图像融合效果打分工具:Q0/Qe/Qw/QABF/VIF五种客观评价指标一键计算
  • 工信局在开展产业招商时如何判断技术项目的可行性?
  • Python自动化测试全攻略:从环境搭建到CI/CD集成
  • XSS漏洞深度解析:从原理到防御的完整指南
  • Android自由框选截图工具:支持屏幕局部截取并自动存入SD卡
  • Windows系统文件cscobj.dll丢失找不到问题解决
  • 全域视觉超融合架构 重塑营区空间透明化智能管理范式 镜像视界·空间元境营区全域视觉一体化智控总体技术方案
  • MindsDB:知识工作者的 AI 平台,39K Star
  • 解决 PyTorch 在 AMD 平台编译报错的完整指南
  • 论文写作的开挂模式!全能AI论文工具,成稿速度超迅速
  • 算苗3D-TokenPU与昇腾384超节点-AI算力芯片三国杀
  • 计算机毕业设计之jsp共享单车管理系统的设计与实现
  • 医用超声图像处理算法:压缩技术详解
  • 股票智能分析系统5分钟部署
  • AI写论文的宝藏工具!这4款AI论文生成神器,高效完成论文
  • 手把手教你在 AMD 新本上部署本地 AI,从零开始不踩坑
  • 日常中的小家电设备如何能够精准向适配器索要电源呢
  • CNC编程效率低?麟思数控10秒出程序解困
  • Windows任务栏透明化:为什么传统方案失效而TranslucentTB能成功?
  • 为什么选择biliTickerBuy:5个让你轻松搞定B站购票的核心功能
  • 如何快速搭建跨平台游戏串流服务器:Sunshine终极配置指南
  • 基于“端-边-云”架构的工业互联网组建与运维实战(附避坑指南)
  • 萨科微slkor6月18日每日芯闻,国际芯闻:
  • 维护开源项目时,如何把一条 Issue 回复写清楚
  • AI Shell对话OBS,存储管理“说”着搞定
  • Vulkan 还是 ROCm,AMD 显卡跑大模型的后端之争终结篇
  • 终极指南:三步免费解锁WeMod专业版功能 - Wand-Enhancer完整教程
  • UUV_AUV六自由度模型(运动学+动力学+扰动)(Matlab代码实现)