面试及场景题

68 阅读10分钟

一、构建工具与模块化

  1. Vite 相比 Webpack 的核心优势是什么?为什么大型项目也开始用 Vite?
  • 优势

    • 开发环境基于浏览器原生 ES 模块(ESM),无需打包,启动速度极快(毫秒级)。

    • 热更新(HMR)基于模块依赖图,只更新修改的模块,而非整个包。

    • 生产环境使用 Rollup 打包,输出更精简的代码(Tree-shaking 更彻底)。

    • 大型项目适配:Vite 2.x+ 支持多页面应用、依赖预构建(node_modules 预打包为 ESM)、缓存策略优化,解决了早期大型项目的性能瓶颈。

  1. Webpack 的 Tree-shaking 原理是什么?为什么需要 package.json 配置 sideEffects
  • 原理:基于 ES 模块的静态分析(import/export),标记未被引用的代码,打包时剔除。

  • sideEffects 作用:告知 Webpack 哪些文件有副作用(如全局样式、polyfill),避免被误删。例如:

{ "sideEffects": ["*.css", "src/utils/polyfill.js"] }
  1. ES 模块(ESM)与 CommonJS(CJS)的区别?如何在项目中混用?
  • 区别

    • ESM 是静态的(编译时确定依赖),支持 import() 动态导入、export 命名导出;
    • CJS 是动态的(运行时确定依赖),使用 require()/module.exports,同步加载。
  • 混用方案

    • Webpack/Vite 会自动处理模块转换(ESM 转 CJS 或反之)。

    • 注意:CJS 中 require() ESM 模块会得到 { default: ... } 包裹的对象,需显式取 default。

  1. pnpm 相比 npm/yarn 的优势?为什么能解决「幽灵依赖」问题?
  • 优势

    • 采用硬链接 + 符号链接的存储方式,依赖只存一份,节省磁盘空间。

    • 安装速度比 npm 快 2-3 倍(依赖复用率高)。

    • 严格的依赖隔离,避免「幽灵依赖」(未在 package.json 声明却能被引用的依赖)。

  • 解决幽灵依赖:pnpm 会创建严格的依赖目录结构,只有 package.json 声明的依赖才会被链接到 node_modules,避免意外引用子依赖。

  1. 如何配置 Vite 实现多页面应用(MPA)?
  • 在 vite.config.js 中配置 build.rollupOptions.input:
// vite.config.js
export default {
  build: {
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'index.html'),
        admin: resolve(__dirname, 'admin/index.html')
      }
    }
  }
};
    • 每个页面对应独立的 HTML 文件,共享 node_modules 和配置。
  1. 模块化设计中,如何避免循环依赖?
  • 预防措施

    • 拆分公共逻辑到独立模块(如将 A 和 B 都依赖的逻辑抽为 C)。

    • 采用「依赖注入」模式,通过参数传递依赖,而非模块内直接导入。

    • 延迟导入(在函数内部 import() 动态加载,避免模块初始化时的依赖)。

二、代码规范与质量

  1. ESLint、Prettier、EditorConfig 的作用及区别?如何协同工作?
  • ESLint:检测代码语法错误和风格问题(如未使用变量、不符合规范的命名)。

  • Prettier:专注代码格式化(如缩进、换行、引号),不关心语法逻辑。

  • EditorConfig:统一不同编辑器的基础格式(如缩进空格数、换行符)。

  • 协同方案

    • ESLint 禁用格式化相关规则(用 eslint-config-prettier),避免与 Prettier 冲突。

    • 用 prettier-eslint 将 Prettier 格式化后的代码再交给 ESLint 修复。

  1. husky + lint-staged 如何保证提交代码质量?
  • husky:管理 Git 钩子(如 pre-commit、pre-push),在提交 / 推送前执行脚本。

  • lint-staged:只对暂存区(staged)的文件执行检查(避免全量检查耗时)。

  • 配置示例

// package.json
{
  "husky": { "hooks": { "pre-commit": "lint-staged" } },
  "lint-staged": { "*.{js,vue}": ["eslint --fix", "prettier --write"] }
}
  1. 如何在团队中统一 Node 版本?
  • 使用 .nvmrc(配合 nvm)或 .node-version(配合 fnm)指定版本:
# .nvmrc
v18.18.0
  • 结合 engines 字段在 package.json 中声明,并通过 npm install 时的 engine-strict 配置强制校验:
{ "engines": { "node": ">=18.0.0" } }
  1. TypeScript 如何配置才能兼顾类型严格性和开发效率?
  • 核心配置 tsconfig.json:
{
  "compilerOptions": {
    "strict": true, // 开启严格模式(推荐)
    "noImplicitAny": true, // 禁止隐式 any 类型
    "strictNullChecks": true, // 严格 null 检查(避免 null 错误)
    "skipLibCheck": true, // 跳过第三方库类型检查(提升速度)
    "esModuleInterop": true // 兼容 CJS 和 ESM
  }
}
  • 对复杂类型暂时用 any 时,添加 // @ts-ignore 并注明原因,后续优化。
  1. 如何检测项目中的未使用依赖?
  • 使用 depcheck 工具自动分析:
npx depcheck
  • 结合 CI 流程,在提交时触发检查,避免冗余依赖增大包体积。

三、性能优化与构建优化

  1. 前端打包体积过大?如何分析并优化?
  • 分析工具

    • Vite:vite build --report 生成打包分析报告。

    • Webpack:webpack-bundle-analyzer 可视化包结构。

  • 优化方案

    • 代码分割(splitChunks 提取公共依赖)。

    • 动态导入(import() 懒加载非首屏组件)。

    • 替换大体积库(如用 lodash-es 替代 lodash,支持 Tree-shaking)。

    • 图片压缩(vite-plugin-imagemin)、字体子集化。

  1. 如何实现代码分割(Code Splitting)?有哪些场景?
  • 实现方式

    • 路由级分割(Vue Router 配合 () => import('./Page.vue'))。

    • 组件级分割(const HeavyComponent = () => import('./Heavy.vue'))。

    • Webpack/Vite 自动分割(splitChunks 配置提取公共库)。

  • 场景:首屏优化(只加载必要代码)、大型组件(如富文本编辑器)、按需加载的功能模块(如图表)。

  1. 生产环境如何优化静态资源加载?
  • CDN 加速:将 JS/CSS/ 图片部署到 CDN,利用边缘节点缓存。

  • 缓存策略

    • 文件名加哈希(app.[hash].js),配合 Cache-Control: max-age=31536000 长期缓存。

    • HTML 不缓存(Cache-Control: no-cache),确保每次请求最新版本。

  • 压缩与合并:Gzip/Brotli 压缩(服务器或构建工具配置)、合并小文件减少请求数。

  1. 如何优化 Vite 的构建速度?
  • 启用依赖预构建缓存(node_modules/.vite,默认开启)。

  • 缩小 include/exclude 范围,避免不必要的文件处理。

  • 生产环境禁用 sourcemap(build.sourcemap: false)。

  • 使用 esbuild 作为 JS 压缩工具(build.minify: 'esbuild')。

  1. 图片资源在工程化中的最佳实践?
  • 格式选择:小图标用 SVG;照片用 WebP/AVIF(兼容性不足时降级为 JPEG);透明图用 PNG-8。

  • 按需加载

    • 用 loading="lazy" 延迟加载非首屏图片。

    • 响应式图片(srcset 配合不同分辨率):

<img src="small.jpg" srcset="medium.jpg 800w, large.jpg 1200w" alt="示例">
  • 构建处理:用 vite-plugin-svg-icons 整合 SVG 图标,vite-plugin-imagemin 自动压缩。
  1. 如何避免第三方库对项目打包体积的影响?
  • CDN 引入:通过 script 标签加载(如 React、Vue),排除在打包范围外:
// vite.config.js
export default {
  externals: { vue: 'Vue', 'vue-router': 'VueRouter' }
};
  • 按需导入:使用 babel-plugin-import 自动导入组件库的按需模块(如 Element Plus、Ant Design)。

  • 替代轻量库:用 date-fns 替代 moment.js,lodash-es 替代全量 lodash。

四、CI/CD 与自动化部署

  1. 什么是 CI/CD?如何用 GitHub Actions 实现前端自动化部署?
  • CI(持续集成) :代码提交后自动执行构建、测试,确保代码质量。

  • CD(持续部署) :通过 CI 后自动部署到测试 / 生产环境。

  • GitHub Actions 配置示例(部署到 GitHub Pages):

# .github/workflows/deploy.yml
name: Deploy
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm install && npm run build
      - uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./dist
  1. 前端项目如何实现灰度发布?
  • 方案 1:CDN 灰度:配置 CDN 按比例(如 10% 用户)指向新版本资源,监控无问题后全量切换。

  • 方案 2:路由分发:后端根据用户 ID/IP 分段,将部分用户路由到新版本前端服务。

  • 方案 3:特性开关(Feature Flag) :在代码中通过配置控制功能是否启用,动态切换版本。

  1. Docker 部署前端项目的优势?如何编写 Dockerfile?
  • 优势:环境一致性(避免「本地能跑,线上崩」)、隔离性强、部署流程标准化。

  • Dockerfile 示例

# 构建阶段
FROM node:18-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# 生产阶段(nginx 部署)
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
  1. 如何实现前端项目的回滚机制?
  • 版本化部署:每次部署生成唯一版本号(如 Git commit hash),保留历史版本文件。

  • 快速切换:通过修改 CDN 配置或 Nginx 路由,指向历史版本的静态资源目录。

  • 自动化回滚:在 CI/CD 中配置监控(如错误率超标),自动触发回滚到上一稳定版本。

  1. 如何在 CI 流程中集成前端测试?
  • 在 GitHub Actions/GitLab CI 中添加测试步骤:
# .github/workflows/test.yml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm install
      - run: npm run lint  # ESLint 检查
      - run: npm run test  # 单元测试(Jest/Vitest)
      - run: npm run test:e2e  # E2E 测试(Cypress)

配置测试不通过则阻断后续部署流程。

五、测试与质量保障

  1. 单元测试、集成测试、E2E 测试的区别及适用场景?
  • 单元测试:测试独立函数 / 组件(如工具函数、UI 组件),用 Jest/Vitest,快且隔离。

  • 集成测试:测试模块间交互(如组件组合、API 调用),用 @vue/test-utils,验证协作逻辑。

  • E2E 测试:模拟用户操作测试完整流程(如登录→下单),用 Cypress/Playwright,覆盖真实场景。

  1. 如何用 Vitest 测试 Vue3 组件?
  • 安装依赖:npm install vitest @vue/test-utils --save-dev

  • 测试示例(按钮点击事件):

// Button.test.js
import { describe, it, expect, mount } from 'vitest';
import Button from './Button.vue';
describe('Button', () => {
  it('emits "click" event when clicked', async () => {
    const wrapper = mount(Button);
    await wrapper.trigger('click');
    expect(wrapper.emitted('click')).toBeTruthy();
  });
});
  1. 前端自动化测试的覆盖率目标如何设定?
  • 核心模块:业务逻辑(如支付、权限)覆盖率 ≥ 80%,避免关键路径 bug。

  • 工具函数:覆盖率 ≥ 90%,确保基础功能稳定。

  • UI 组件:聚焦交互逻辑(如点击、输入),覆盖率 ≥ 70%,样式相关可降低要求。

  • 避免盲目追求 100% 覆盖率(如简单的 getter/setter 无需过度测试)。

  1. 如何模拟 API 请求进行测试?
  • 使用 vitest-mock-extended 或 msw(Mock Service Worker)模拟接口:
// 用 msw 模拟 GET /api/user
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const server = setupServer(
  rest.get('/api/user', (req, res, ctx) => {
    return res(ctx.json({ name: 'test' }));
  })
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

六、架构与工程化进阶

  1. 微前端的核心价值是什么?基于 Module Federation 如何实现?
  • 核心价值:按业务拆分独立应用(如电商的商品、订单、支付),团队独立开发 / 部署,共享基础库。

  • Module Federation 实现(Webpack/Vite 支持):

    • 主应用配置 exposes 暴露组件,子应用配置 remotes 引入主应用模块:
// 主应用 vite.config.js
export default {
  plugins: [
    federation({
      name: 'host',
      remotes: { app1: 'app1@http://localhost:5001/assets/remoteEntry.js' }
    })
  ]
};
  1. Monorepo 适合什么场景?如何用 pnpm workspace 配置?
  • 适合场景:多包项目(如组件库、工具库)、团队共享代码(如多个应用共用 UI 组件)。

  • pnpm workspace 配置

# pnpm-workspace.yaml
packages:
  - 'packages/*'  # 子包目录
  - 'apps/*'      # 应用目录
  • 优势:统一依赖管理、跨包引用无需发布、原子提交(一次提交更新多个包)。
  1. 如何设计前端工程化的目录结构?
  • 推荐结构(按功能 / 业务拆分):
src/
  ├── assets/        # 静态资源(图片、字体)
  ├── components/    # 公共组件(按功能分组:form、table)
  ├── composables/   # 组合函数(useFetch、useAuth)
  ├── hooks/         # 自定义钩子
  ├── router/        # 路由配置
  ├── store/         # 状态管理(Pinia)
  ├── services/      # API 服务
  ├── utils/         # 工具函数
  ├── views/         # 页面组件(按路由划分)
  └── App.vue
  1. 前端工程化如何应对多环境(开发、测试、生产)配置?
  • 方案 1:环境变量(Vite 示例):

    • 创建 .env.development、.env.production 等文件,用 VITE_ 前缀定义变量:
# .env.production
VITE_API_URL=https://api.example.com
  • 代码中访问:import.meta.env.VITE_API_URL。

  • 方案 2:配置中心:复杂项目用远程配置中心(如 Apollo),动态拉取环境配置,无需重新打包。