3月27日SHEIN商城外贸公司-深圳

125 阅读24分钟
  1. 做个简单的自我介绍
  2. 你作为前端负责人,然后有没有站在项目或者是业务角度去推进一个项目的进度?推动业务或者是对业务。服务增长进行推动的一些技术手段是比较优先的,针对项目的某些痛点进行哪些优化,或者是了解的哪些业务,作为一个开发技术的角度,去发现他可能哪些地方是会有点问题。我作为前端负责人然后包括就推动他解决完成我的业务流程的优化,包括整合。从技术方面,我怎么去完善我整个的开发链路,或者是说别的效应之类的,更支持业务的有效增长。(录音的,他就是这么问,感觉我当时疯狂在总结他想表达什么)
  3. 表单设计器是怎么实现的,描述一下你的Schama怎么设计的,前端渲染又是怎么实现的?(简历里面有写可视化表单配置)
  4. 表单校验为什么没有听到你的设计,你是怎么实现表单校验的特别的自定义表单校验规则?(忘记说了所以他提问)
  5. 我看你在这个项目做了一些性能优化方面的事情,你可以描述一下吗?(vue3+vite的项目)
  6. 图片优化,你知道怎么判断浏览器支不支持webp格式的图片
  7. 你平时有用performance吗?说说你怎么判断你的图片懒加载,你的图片压缩是否生效
  8. 我们聊一下常见的性质指标,像FCP,LCP,CLS,INP这些了解过吗?展开说说
  9. 你说说路由懒加载的原理 ?

2. 你作为前端负责人,然后有没有站在项目或者是业务角度去推进一个项目的进度?推动业务或者是对业务。服务增长进行推动的一些技术手段是比较优先的,针对项目的某些痛点进行哪些优化,或者是了解的哪些业务,作为一个开发技术的角度,去发现他可能哪些地方是会有点问题。我作为前端负责人然后包括就推动他解决完成我的业务流程的优化,包括整合。从技术方面,我怎么去完善我整个的开发链路,或者是说别的效应之类的,更支持业务的有效增长。(录音的,他就是这么问,感觉我当时疯狂在总结他想表达什么)

回答:如何作为前端负责人推动项目进度和业务增长

作为前端负责人,我会从以下几个方面来推动项目进度、优化业务流程,并通过技术手段促进业务增长:


一、项目推进:确保开发高效有序进行

  1. 拆解业务需求,提高开发效率

    • 站在全局视角,将业务需求拆解成前端可落地的任务。
    • 采用任务优先级排序(如 MoSCoW 法),确保核心业务功能优先上线。
    • 制定前后端对接标准,避免接口定义不清导致的开发延误。
  2. 提升协作效率,优化开发流程

    • 建立标准化的前端架构,如使用 Monorepo 方案、组件库等,减少重复开发。
    • 通过 CI/CD(持续集成/持续部署) 自动化代码检查、测试、部署,提升交付效率。
    • 采用Mock 数据 + 接口文档管理(如 Swagger) ,前后端并行开发,减少等待时间。
    • 制定代码规范(如 ESLint、Prettier、Git Hook),降低代码维护成本。

二、业务增长:技术驱动业务价值

  1. 优化性能,提升用户体验

    • 采用 SSR / CSR 结合 提升首屏渲染速度,提高用户留存率。
    • 通过 图片/静态资源优化(CDN、Lazy Load、WebP) ,减少页面加载时间。
    • 利用 骨架屏、渐进式加载,降低用户感知延迟,提高转化率。
  2. 数据埋点与 A/B 测试,驱动业务决策

    • 在关键业务流程中 埋点(如用户点击、表单提交、跳出率),分析用户行为,优化产品设计。
    • 结合 A/B 测试,验证 UI 变化、交互优化对转化率的影响。
    • 通过日志分析(ELK、Sentry),快速发现和解决影响用户体验的技术问题。
  3. 低代码 / 组件库,提高开发复用度

    • 搭建前端组件库(如基于 Vue3 + TS 设计的 UI 组件),减少重复开发,提高开发效率。
    • 结合低代码平台(如拖拽式表单生成器),支持运营快速配置页面,减少前端介入成本。

三、发现并优化项目痛点

  1. 识别并解决性能瓶颈

    • 通过Performance API、Lighthouse、WebPageTest 定位页面性能问题。
    • 解决长任务阻塞(Long Tasks) ,使用 Web Worker、requestIdleCallback 等优化主线程占用。
    • 采用 Code Splitting(按需加载)、Preload/Prefetch,优化资源加载顺序。
  2. 优化前后端接口交互

    • 避免瀑布式请求,改为批量请求、GraphQL 方案
    • 使用 HTTP 缓存(ETag、Cache-Control)、本地缓存(IndexedDB、localStorage) 减少请求。
    • 降低白屏时间,提前预加载 API 数据,减少用户等待。

四、技术完善与效能提升

  1. 技术选型与架构升级

    • Vue2 向 Vue3 迁移,利用 Composition API + Pinia 提高代码复用性和可维护性。
    • Webpack 升级 Vite,提升热更新速度,优化开发体验。
    • 采用微前端架构(Qiankun、Module Federation),支持多团队并行开发,提升可扩展性。
  2. 开发链路优化,提升稳定性

    • 自动化测试(Jest + Cypress) 确保业务逻辑稳定。
    • 前端错误监控(Sentry + 埋点) ,快速发现线上异常。
    • 灰度发布 + 版本回滚,降低新功能上线风险。

总结

作为前端负责人,不仅要提升开发效率,更要站在业务角度思考如何用技术驱动增长。核心方法包括:

  1. 优化前端架构和开发流程,加速项目推进。
  2. 通过性能优化、数据分析等手段,提升用户体验和转化率
  3. 识别业务痛点,主动提出改进方案,从技术层面促进业务增长。
  4. 提升开发效能和稳定性,为业务扩展做好技术保障。

这不仅仅是写代码的问题,而是如何让技术为业务创造价值。

3. 表单设计器是怎么实现的,描述一下你的Schama怎么设计的,前端渲染又是怎么实现的?(简历里面有写可视化表单配置)

表单设计器的实现方案

表单设计器的核心是 Schema 驱动的动态表单,可以通过拖拽、配置生成 JSON Schema,并根据 Schema 渲染表单。


一、Schema 设计

Schema 主要用于描述表单的结构,包括字段类型、布局、校验规则、默认值等。

1. Schema 示例

{
  "formId": "userForm",
  "layout": "horizontal",
  "fields": [
    {
      "type": "input",
      "label": "姓名",
      "key": "name",
      "required": true,
      "props": {
        "placeholder": "请输入姓名",
        "maxLength": 20
      },
      "rules": [
        { "required": true, "message": "姓名不能为空" }
      ]
    },
    {
      "type": "select",
      "label": "性别",
      "key": "gender",
      "required": true,
      "props": {
        "options": [
          { "label": "男", "value": "male" },
          { "label": "女", "value": "female" }
        ]
      }
    },
    {
      "type": "checkbox",
      "label": "爱好",
      "key": "hobbies",
      "props": {
        "options": [
          { "label": "阅读", "value": "reading" },
          { "label": "运动", "value": "sports" }
        ]
      }
    },
    {
      "type": "datePicker",
      "label": "出生日期",
      "key": "birthdate",
      "props": {
        "format": "YYYY-MM-DD"
      }
    }
  ]
}

二、前端渲染实现

1. 解析 Schema

前端根据 Schema 动态渲染组件,核心思路:

  • 遍历 fields,根据 type 选择对应组件
  • 绑定 v-modelvalue 实现双向绑定
  • 处理 props 传递给组件
  • 解析 rules 并绑定校验

2. 渲染逻辑

使用 Vue 3 + <script setup> 进行渲染:

<script setup>
import { ref, reactive } from "vue";
import { ElInput, ElSelect, ElOption, ElCheckbox, ElCheckboxGroup, ElDatePicker, ElForm, ElFormItem } from "element-plus";

// 传入的 Schema
const formSchema = reactive({
  formId: "userForm",
  layout: "horizontal",
  fields: [
    { type: "input", label: "姓名", key: "name", required: true, props: { placeholder: "请输入姓名" } },
    { type: "select", label: "性别", key: "gender", props: { options: [{ label: "男", value: "male" }, { label: "女", value: "female" }] } },
    { type: "checkbox", label: "爱好", key: "hobbies", props: { options: [{ label: "阅读", value: "reading" }, { label: "运动", value: "sports" }] } },
    { type: "datePicker", label: "出生日期", key: "birthdate", props: { format: "YYYY-MM-DD" } }
  ]
});

// 表单数据
const formData = ref({});
</script>

<template>
  <ElForm :model="formData">
    <ElFormItem v-for="field in formSchema.fields" :key="field.key" :label="field.label">
      <ElInput v-if="field.type === 'input'" v-model="formData[field.key]" v-bind="field.props" />
      
      <ElSelect v-else-if="field.type === 'select'" v-model="formData[field.key]">
        <ElOption v-for="option in field.props.options" :key="option.value" :label="option.label" :value="option.value" />
      </ElSelect>
      
      <ElCheckboxGroup v-else-if="field.type === 'checkbox'" v-model="formData[field.key]">
        <ElCheckbox v-for="option in field.props.options" :key="option.value" :label="option.label" :value="option.value" />
      </ElCheckboxGroup>
      
      <ElDatePicker v-else-if="field.type === 'datePicker'" v-model="formData[field.key]" v-bind="field.props" />
    </ElFormItem>
  </ElForm>
</template>

三、可视化配置(拖拽式)

  1. 拖拽组件面板

    • 预定义组件列表(如输入框、下拉框、日期选择器等)。
    • 拖拽组件到设计区域,生成对应 Schema 结构。
  2. 属性编辑面板

    • 选中表单项后,可在侧边栏修改 labelpropsrules 等配置。
  3. Schema 生成与存储

    • 设计器完成后,导出 JSON Schema,存入数据库或本地存储。
    • 预览模式下,解析 Schema,实时渲染表单。

四、总结

  • Schema 设计:采用 JSON 定义表单结构,包括 fieldspropsrules 等。
  • 动态渲染:Vue 通过 v-for 遍历 Schema,动态绑定组件。
  • 可视化配置:拖拽表单项,实时生成 Schema,实现低代码化开发。

这一方案适用于在线表单设计器、后台管理系统、低代码平台等场景。

4.表单校验为什么没有听到你的设计,你是怎么实现表单校验的特别的自定义表单校验规则?(忘记说了所以他提问)

5. 我看你在这个项目做了一些性能优化方面的事情,你可以描述一下吗?(vue3+vite的项目)

Vue3 + Vite 项目的性能优化可以从多个层面入手,包括代码优化、渲染优化、构建优化、网络优化、运行时优化等。下面是详细的优化策略:


1. 代码优化

1.1 避免不必要的组件重新渲染

Vue3 采用响应式系统,但不合理使用 reactiveref 等可能导致组件频繁渲染,影响性能。

正确使用 refreactive
  • reactive 适用于对象,但不要深层嵌套
  • ref 适用于基本数据类型
const state = reactive({
  name: 'Vue',
  count: 0
});

// 更推荐
const name = ref('Vue');
const count = ref(0);
避免 reactive 过度包装

如果 reactive 仅用于存储简单值,可以改用 shallowReactive

const user = shallowReactive({
  name: 'Tom',
  age: 20
});

shallowReactive 只会使第一层属性响应式,提高性能。


1.2 使用 computed 代替多次 watch

computed 默认有缓存,只有在依赖发生变化时才重新计算,而 watch 每次都会触发回调

const expensiveComputed = computed(() => {
  return items.filter(item => item.active);
});

watch 更高效。


1.3 使用 v-memo 进行优化

Vue3 提供了 v-memo 指令,可以缓存子组件的渲染:

<div v-memo="[someReactiveProp]">
  <ExpensiveComponent />
</div>

如果 someReactiveProp 没有变化,则 ExpensiveComponent 不会重新渲染。


1.4 列表渲染优化

v-for 绑定 key
<li v-for="item in list" :key="item.id">{{ item.name }}</li>

避免使用 index 作为 key,否则列表顺序改变时会导致 Vue 误判,导致 DOM 重新渲染。

长列表优化
  • 分页加载
  • 使用虚拟列表(如 vue-virtual-scroller
import { DynamicScroller } from 'vue-virtual-scroller';

<DynamicScroller
  :items="bigList"
  :min-item-size="50"
>
  <template v-slot="{ item }">
    <div>{{ item.name }}</div>
  </template>
</DynamicScroller>

适用于超大数据量的渲染,减少 DOM 负担。


1.5 v-if vs v-show

  • v-if 会销毁/重建组件,适用于不常切换的内容。
  • v-show 只是切换 display: none,适用于频繁切换的内容。
<div v-if="isVisible">内容</div>  <!-- 适用于偶尔显示 -->
<div v-show="isVisible">内容</div> <!-- 适用于频繁显示 -->

2. 构建优化

2.1 代码分割

路由懒加载
const About = () => import('@/views/About.vue');

const router = createRouter({
  routes: [
    { path: '/about', component: About }
  ]
});
动态组件按需加载
<component :is="asyncComponent" />
const asyncComponent = defineAsyncComponent(() => import('./MyComponent.vue'));

减少首屏加载时间。


2.2 Tree Shaking

避免全局引入
// ❌ 不推荐
import _ from 'lodash';

// ✅ 推荐
import { debounce } from 'lodash-es';

使用 lodash-es 支持按需引入,避免打包无用代码。


2.3 代码压缩

vite.config.ts 配置 terser

export default defineConfig({
  build: {
    minify: 'terser',
    terserOptions: {
      compress: { drop_console: true }
    }
  }
});

移除 console.log,减少 JS 体积。


3. 渲染优化

3.1 Teleport 提升渲染效率

<Teleport> 可以将弹窗渲染到 body,避免影响父组件的渲染:

<Teleport to="body">
  <Modal />
</Teleport>

3.2 避免 v-html 的安全风险

如果必须使用,先进行XSS 过滤

import DOMPurify from 'dompurify';
const safeHtml = DOMPurify.sanitize(unsafeHtml);

4. 网络优化

4.1 图片优化

使用 WebP

WebP 比 PNG/JPEG 体积更小:

<img src="image.webp" alt="example">
使用 srcset 响应式图片
<img src="default.jpg"
     srcset="image-480w.jpg 480w, image-800w.jpg 800w"
     sizes="(max-width: 600px) 480px, 800px">

4.2 静态资源缓存

配置 Cache-Control
server: {
  headers: {
    'Cache-Control': 'max-age=31536000, immutable'
  }
}

减少重复下载。


5. 运行时优化

5.1 使用 Web Worker

Web Worker 可以处理复杂计算,避免阻塞 UI:

const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' });
worker.postMessage({ data: largeData });

5.2 requestIdleCallback 进行低优先级任务

requestIdleCallback(() => {
  expensiveComputation();
});

当浏览器空闲时才执行任务,避免影响用户体验。


6. 生产环境优化

6.1 生产环境模式

vite build --mode production

确保构建时使用 production,减少调试代码。


6.2 移除 console.log

export default defineConfig({
  build: {
    terserOptions: {
      compress: {
        drop_console: true
      }
    }
  }
});

减少 JS 体积,提高运行效率。


总结

优化点方式
代码优化computed 代替 watch,避免 reactive 过度嵌套
渲染优化v-memo 缓存组件,虚拟列表
构建优化Tree Shaking,按需引入,代码分割
网络优化图片优化,CDN,加速缓存
运行时优化Web Worker,requestIdleCallback

通过这些优化措施,可以显著提升 Vue3 + Vite 项目的性能,提高用户体验! 🚀

6. 图片优化,你知道怎么判断浏览器支不支持webp格式的图片

判断浏览器是否支持 WebP 格式的图片,可以使用以下几种方法:


方法 1:JavaScript + Canvas

WebP 是一种现代图片格式,大多数现代浏览器都支持它。可以通过 canvas.toDataURL 方法来检测:

function supportsWebP(callback) {
  const img = new Image();
  img.onload = () => callback(true);
  img.onerror = () => callback(false);
  img.src =
    "data:image/webp;base64,UklGRhYAAABXRUJQVlA4WAoAAAAQAAAAQAAQAAAQAAQUxQAA==";
}

supportsWebP((isSupported) => {
  if (isSupported) {
    console.log("WebP is supported!");
  } else {
    console.log("WebP is NOT supported!");
  }
});

原理

  • 生成一个 WebP 格式的 Base64 图片 URL
  • img 元素尝试加载
  • 如果加载成功,说明浏览器支持 WebP

方法 2:使用 Modernizr

Modernizr 是一个专门检测浏览器特性的库,可以快速检测 WebP 支持情况:

<script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/3.11.2/modernizr.min.js"></script>
<script>
  if (Modernizr.webp) {
    console.log("WebP is supported!");
  } else {
    console.log("WebP is NOT supported!");
  }
</script>

优点:代码简洁,支持 webp-alphawebp-animationwebp-lossless 细分特性。


方法 3:CSS 方式(推荐用于背景图片)

如果需要根据 WebP 支持情况加载不同的背景图片,可以使用 CSS:

/* 默认使用 JPG */
.bg-image {
  background-image: url('fallback.jpg');
}

/* 如果支持 WebP,使用 WebP */
@supports (background-image: url('data:image/webp;base64,UklGRhYAAABXRUJQVlA4WAoAAAAQAAAAQAAQAAAQAAQUxQAA==')) {
  .bg-image {
    background-image: url('image.webp');
  }
}

方法 4:HTML + <picture>(推荐用于 <img>

现代浏览器支持 <picture> 标签,可以让浏览器自动选择支持的图片格式

<picture>
  <source srcset="image.webp" type="image/webp">
  <source srcset="image.jpg" type="image/jpeg">
  <img src="image.jpg" alt="Example Image">
</picture>

原理

  • 如果浏览器支持 WebP,会选择 image.webp
  • 如果不支持 WebP,会选择 image.jpg

这种方式适用于 HTML 代码级别,可以省去 JS 判断的复杂性。


最佳实践

方法适用场景优点缺点
Canvas+JavaScript需要动态检测 WebP 支持情况无需外部库,兼容性好需要 JS 逻辑
Modernizr依赖 Modernizr 库的项目代码简洁,细分支持类型需要额外加载 Modernizr
CSS @supports适用于背景图片纯 CSS,性能好仅限 CSS 作用域
HTML <picture><img> 标签加载优化自动选择最佳格式,最推荐仅适用于 img

如果你在HTML 结构内使用图片,建议使用 <picture>
如果是背景图片,可以使用 CSS @supports
如果你需要JavaScript 逻辑动态处理,则可以用 Canvas 方案。

🚀 综合推荐:HTML <picture> + JavaScript 兼容检测,能覆盖最多情况!

7.你平时有用performance吗?说说你怎么判断你的图片懒加载,你的图片压缩是否生效

是的,我平时会使用浏览器的 Performance(性能)工具来分析页面加载情况,包括图片懒加载图片压缩的效果。以下是具体的检查方法:


📌 1. 判断图片懒加载是否生效

方法 1:使用 Chrome DevTools 的 Performance 记录

  1. 打开 DevTools(F12 或 Ctrl + Shift + I)

  2. 切换到 "Performance"(性能) 面板

  3. 点击 "Start profiling and reload page" (录制页面加载)

  4. 等待页面加载完成后,停止录制

  5. Network(网络)部分,查找 img 资源:

    • 懒加载成功:图片在滚动到可视区域后才加载
    • 懒加载失败:所有图片在页面加载时就已经全部请求了

示例分析

  • 如果懒加载生效,图片的请求应该在 滚动后才出现,而不是一开始就全部加载。

方法 2:Network 面板检查

  1. Network(网络)面板,勾选 "Img" 过滤只显示图片请求

  2. 刷新页面

  3. 检查图片的加载时机

    • 懒加载生效:部分图片的 Initiator"lazy load" ,并且是在滚动后才加载
    • 懒加载未生效:所有图片在页面加载时就请求了

示例分析

  • lazy 关键字出现,表示浏览器支持 <img loading="lazy">
  • IntersectionObserver 方式:如果看到 script 触发的 fetch,说明是 JS 控制懒加载

方法 3:观察图片占位符

  1. 在页面上,检查图片的 srcdata-src

    <img src="placeholder.jpg" data-src="real-image.webp" class="lazyload">
    
  2. 使用 "Elements" 面板

    • 如果 src 还是占位符,而 data-src 在滚动后才替换成真实地址,说明懒加载生效了

📌 2. 判断图片压缩是否生效

方法 1:Network 面板检查图片大小

  1. 打开 Network(网络) 面板,筛选 "Img" 资源

  2. 检查图片的 "Size"(大小)

    • 如果 WebP 格式启用,图片应该比原始 JPG/PNG 小
    • 压缩后图片的 "Content-Length"(响应大小)应显著减少
  3. 对比 "Transferred" vs. "Content-Length"

    • Transferred 是实际下载的大小
    • Content-Length 是原始文件大小
    • 如果 Transferred 远小于 Content-Length,说明服务器启用了 Gzip 或 Brotli 压缩

示例分析

  • 图片原始大小 500 KB,WebP 版本只有 150 KB → 压缩生效
  • 服务器返回 Content-Encoding: gzipbr开启了服务器端压缩

方法 2:检查 Accept 头部是否支持 WebP

  1. Network > Img 选择一张图片

  2. 进入 Headers(请求头部)

  3. 查看 Accept 头部:

    Accept: image/avif,image/webp,image/apng,image/*,*/*;q=0.8
    
    • 如果 image/webp 存在,说明浏览器支持 WebP
    • 但如果服务器返回的仍然是 image/jpeg,说明服务器未正确处理 WebP 请求

示例分析

  • Accept 包含 image/webp,但服务器返回 image/jpeg → 需要优化服务器配置
  • Accept 包含 image/webp,服务器返回 Content-Type: image/webpWebP 成功启用

方法 3:实际对比图片质量

  1. 右键图片打开图片

  2. 检查文件扩展名

    • 如果是 .webp.avif,说明启用了现代格式
    • 如果还是 .png.jpg,可能没有启用 WebP 转换

示例分析

  • image.webp 出现 → WebP 成功生效
  • image.jpg 仍然返回 → 未启用 WebP

🎯 3. 额外优化建议

✅ 开启 WebP 自适应格式

<picture>
  <source srcset="image.webp" type="image/webp">
  <source srcset="image.jpg" type="image/jpeg">
  <img src="image.jpg" alt="Example">
</picture>
  • 兼容性好,支持 WebP 的浏览器会加载 WebP,否则回退到 JPG。

✅ 使用 srcset 提供多分辨率图片

<img 
  src="default.jpg"
  srcset="image-480w.jpg 480w, image-800w.jpg 800w"
  sizes="(max-width: 600px) 480px, 800px"
  alt="Example">
  • 这样浏览器可以根据设备分辨率选择最佳图片,提高加载效率。

✅ 服务器端开启 WebP 支持

如果使用 NGINX,可以配置 content negotiation 让支持 WebP 的浏览器自动请求 WebP 格式:

location ~* ^.+.(png|jpe?g)$ {
  add_header Vary Accept;
  expires 1y;
  access_log off;
  try_files $uri$webp_ext $uri =404;
}
  • 如果浏览器支持 WebP,会返回 .webp
  • 如果不支持,则返回原始 .png.jpg

📌 总结

目标方法
检查图片懒加载Performance 面板(查看滚动后加载)、Network 资源检查(懒加载时机)、检查 data-src 替换情况
检查 WebP 是否生效Network 面板(检查 Content-Type)、Headers 查看 Accept 字段对比文件扩展名
检查压缩是否生效Network "Size" vs "Transferred"Content-Length 对比启用服务器 WebP 转换
优化策略<picture> 兼容回退、srcset 响应式图片、NGINX WebP 自适应

📢 结论

  • 检查懒加载 → Performance & Network 面板
  • 检查 WebP 启用 → 看 Content-TypeAccept
  • 检查压缩是否生效 → 对比 SizeContent-Length

这样可以快速定位问题,并进行优化!🚀

8.我们聊一下常见的性质指标,像FCP,LCP,CLS,INP这些了解过吗?展开说说

是的,我对这些 Web 性能指标(Core Web Vitals)比较了解,它们是 Google 提出的关键衡量标准,用来评估网页的用户体验质量。我们可以展开讨论它们的定义、计算方式、优化策略等。


📌 1. FCP(First Contentful Paint,首次内容绘制)

FCP 是指网页加载过程中,浏览器首次渲染出任何内容的时间点。
它标志着用户从空白屏幕到看到页面上的某些可见元素(如文本、图片、SVG、Canvas 等)所需的时间。

如何衡量?

  • FCP 通常发生在 HTML 解析并且浏览器可以绘制出 第一个可见内容 时。
  • Performance 面板中,FCP 事件会标记在 Timings 轨道。

优化策略

  • 优化关键渲染路径(CRP) :减少阻塞渲染的 CSS 和 JS,使用 deferasync 让 JS 异步加载。
  • 减少服务器响应时间(TTFB) :优化数据库查询、启用 CDN 缓存,减少 TTFB(Time to First Byte)
  • 优化资源加载:使用 preload 加载关键资源,如字体 rel="preload"
  • 使用更快的服务器(如 HTTP/2、HTTP/3)。
  • 压缩 CSS 和 JS,减少阻塞渲染的资源。

推荐标准

  • 优秀:< 1.8s
  • 需要改进:1.8s - 3s
  • 较差:> 3s

📌 2. LCP(Largest Contentful Paint,最大内容绘制)

LCP 指的是网页加载过程中,最大可见内容元素(如图片、大块文本等)完全呈现的时间。
它比 FCP 更接近用户真正感知的“页面加载完成”体验。

如何衡量?

  • LCP 计算的是 页面内最大可见元素 的加载时间:

    • <img> 图片
    • <video> 海报
    • 块级文本元素(如 <h1><p>
    • background-image(但 JS 生成的内容不算)
  • Performance 面板的 Timings 轨道找到 LCP 事件

优化策略

  • 优化图片加载

    • 使用 WebP、AVIF 等高效格式,减少 LCP 资源大小。
    • 采用 lazy-loading 避免加载非首屏的图片。
    • 使用 CDN 加速 LCP 资源的加载(特别是大背景图)。
  • 减少阻塞渲染

    • 异步加载非关键 JS(使用 async / defer)。
    • 优化 CSS(使用 critical CSS 提前加载首屏关键 CSS)。
  • 减少 LCP 元素的延迟

    • 提前 preload LCP 资源,如:

      <link rel="preload" as="image" href="hero.jpg">
      
    • 确保 LCP 资源优先加载,而不是被其他请求阻塞。

推荐标准

  • 优秀:< 2.5s
  • 需要改进:2.5s - 4s
  • 较差:> 4s

📌 3. CLS(Cumulative Layout Shift,累积布局偏移)

CLS 衡量的是网页加载过程中,元素的意外布局偏移(Layout Shift)总和。
这决定了页面的视觉稳定性,避免用户误点击或者滑动内容突然跳动的问题。

如何衡量?

  • 当可见元素的位置在没有用户交互的情况下 突然发生偏移,就会记录一次 Layout Shift

  • CLS 计算公式

    CLS = 影响面积 * 移动距离
    
    • 影响面积:受影响的元素占据视口的比例
    • 移动距离:元素偏移的距离占视口高度的比例
  • Performance 面板中的 Experience > Layout Shifts 轨道可查看。

优化策略

  • 为图片/广告/iframe 预留空间

    <img src="example.jpg" width="600" height="400">
    
    • 这样浏览器能提前计算布局,避免加载后突变。
  • 使用 font-display: swap 解决字体加载跳动

    @font-face {
      font-family: 'MyFont';
      src: url('font.woff2') format('woff2');
      font-display: swap;
    }
    
    • 避免自定义字体加载时页面闪烁(FOUT/FOUT 问题)。
  • 避免懒加载导致的布局偏移

    • 使用 min-height 预留空间。

推荐标准

  • 优秀:< 0.1
  • 需要改进:0.1 - 0.25
  • 较差:> 0.25

📌 4. INP(Interaction to Next Paint,交互到下一次绘制)

INP 评估的是用户交互(点击、输入等)到下一帧绘制的时间,衡量交互响应速度。
它是 FID(First Input Delay,首次输入延迟) 的进阶版本,关注整个页面交互体验。

如何衡量?

  • 计算 用户交互事件(click、tap、keydown) 之后,页面多久才有视觉更新(下一次 Paint)。

  • INP 计算公式

    INP = 最大的交互延迟时间
    
  • Performance 面板中,查找 Event Timing 相关事件。

优化策略

  • 减少主线程阻塞

    • 避免长时间 JavaScript 执行,减少 长任务(Long Task)

    • 使用 Web Worker 处理复杂计算,如:

      const worker = new Worker('worker.js');
      worker.postMessage({ data: largeData });
      
  • 优化 requestAnimationFrame

    • 保证 UI 更新不卡顿:

      requestAnimationFrame(() => {
        // 界面更新逻辑
      });
      
  • 减少 DOM 操作

    • 使用 Virtual DOM(如 Vue、React)。

    • 避免直接操作大量 DOM,使用 documentFragment

      const fragment = document.createDocumentFragment();
      for (let i = 0; i < 1000; i++) {
        const div = document.createElement('div');
        fragment.appendChild(div);
      }
      document.body.appendChild(fragment);
      

推荐标准

  • 优秀:< 200ms
  • 需要改进:200ms - 500ms
  • 较差:> 500ms

📌 总结

指标作用影响因素推荐标准
FCP首个内容绘制服务器响应、CSS、JS 阻塞< 1.8s
LCP最大内容绘制图片加载、关键 CSS、TTFB< 2.5s
CLS布局稳定性图片/广告加载跳动、字体渲染< 0.1
INP交互延迟长 JS 任务、主线程阻塞< 200ms

这些 Core Web Vitals 直接影响 SEO 排名用户体验,优化它们能大幅提升网页性能。你有没有遇到过具体的 Web Vitals 优化问题?😃

9.你说说路由懒加载的原理 ?

📌 路由懒加载的原理

在 Vue 3 + Vite 项目中,路由懒加载(Route Lazy Loading)是指按需加载路由组件,而不是在应用初始化时一次性加载所有组件。这种方式可以减少首屏加载时间,优化性能。


1. 为什么要使用路由懒加载?

如果项目较大,所有页面组件都在打包时一次性加载,会导致:

  • 首屏加载过慢:主包体积过大,用户需要等待很长时间才能看到页面。
  • 不必要的资源加载:即使用户从未访问某些页面,这些组件仍然会被加载,占用网络和内存资源。

懒加载(Lazy Loading)可以解决这些问题,让路由组件按需加载,提高应用的首屏渲染速度


2. 路由懒加载的实现

Vue 3 使用 defineAsyncComponentimport() 进行动态加载,主要依赖 Webpack 或 Vite 的**代码分割(Code Splitting)**功能。

✅ 方式 1:动态 import()(推荐)

import { createRouter, createWebHistory } from 'vue-router';

const routes = [
  {
    path: '/',
    component: () => import('@/views/Home.vue'), // 懒加载
  },
  {
    path: '/about',
    component: () => import('@/views/About.vue'), // 只有访问时才加载
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

📌 解析:

  • component: () => import('@/views/About.vue')

    • import() 是一个动态导入,当用户访问 /about 页面时,Vue 才会请求 About.vue
    • 这个 import() 返回的是一个 Promise,Vue Router 知道它是一个异步组件
  • Vite 或 Webpack 会为每个 import() 创建一个独立的 JS chunk,只在需要时加载。

✅ 方式 2:defineAsyncComponent

Vue 3 提供 defineAsyncComponent 来手动定义异步组件:

import { defineAsyncComponent } from 'vue';

const AsyncAbout = defineAsyncComponent(() => import('@/views/About.vue'));

const routes = [
  {
    path: '/about',
    component: AsyncAbout, // 按需加载
  },
];

📌 适用场景:

  • 需要显示加载指示器(Loading)错误处理

    const AsyncAbout = defineAsyncComponent({
      loader: () => import('@/views/About.vue'),
      loadingComponent: LoadingComponent,
      errorComponent: ErrorComponent,
      delay: 200, // 200ms 后显示 loading
      timeout: 3000, // 3s 内未加载完成,显示 errorComponent
    });
    

    这样,加载过慢时会显示 Loading UI,超时则提示错误。


3. Vite 代码分割的原理

Vite 使用 ES Module 进行原生的动态导入,打包时:

  • 每个懒加载的 import() 组件会被单独拆分成一个 JS chunk
  • 只有当该路由被访问时,浏览器才会加载对应的 JS 文件。

📌 代码分割示例 假设你有以下路由:

const routes = [
  { path: '/', component: () => import('@/views/Home.vue') },
  { path: '/about', component: () => import('@/views/About.vue') },
  { path: '/contact', component: () => import('@/views/Contact.vue') },
];

Vite 构建时,会生成类似的分割文件:

dist/
│── assets/
│   ├── Home.123abc.js
│   ├── About.456def.js
│   ├── Contact.789ghi.js
│── index.html
│── main.js

📢 当用户访问 /about,浏览器才会请求 About.456def.js


4. 路由懒加载的优化

✅ (1) 配合 Vue Suspense 处理加载状态

Vue 3 支持 <Suspense> 组件,可以在异步组件未加载完成前显示占位符

<template>
  <Suspense>
    <template #default>
      <AsyncAbout />
    </template>
    <template #fallback>
      <LoadingComponent />
    </template>
  </Suspense>
</template>

<script setup>
import { defineAsyncComponent } from 'vue';

const AsyncAbout = defineAsyncComponent(() => import('@/views/About.vue'));
</script>

用户进入页面时,先看到 LoadingComponent,加载完成后再渲染真正的组件。


✅ (2) 预加载(Preloading)和预获取(Prefetching)

懒加载虽然减少了首屏加载时间,但如果用户切换页面时才下载 JS 资源,可能会导致页面加载有短暂延迟。可以用:

  • webpackPrefetch浏览器空闲时预获取资源(适合可能会访问的页面)
  • webpackPreload当前页面加载时就预加载(适合首屏关键页面)

示例

const AsyncAbout = () => import(
  /* webpackPrefetch: true */ '@/views/About.vue'
);

📢 用户未访问 /about,但浏览器空闲时会偷偷加载 About.js


✅ (3) 配合 Vue Router 的 defineAsyncComponent

在 Vue Router 4.x 中,除了 component: () => import(),你也可以用 defineAsyncComponent

import { createRouter, createWebHistory } from 'vue-router';
import { defineAsyncComponent } from 'vue';

const routes = [
  {
    path: '/about',
    component: defineAsyncComponent(() =>
      import('@/views/About.vue')
    ),
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

区别

  • component: () => import() 适用于 Vue Router 内部处理
  • defineAsyncComponent 适用于手动控制加载

5. 总结

技术作用
import() 动态导入按需加载,打包时自动拆分 chunk
defineAsyncComponent支持加载指示器和超时处理
Vue Suspense提供异步组件的 Loading UI
Prefetch & Preload预加载关键页面,减少切换等待

📢 什么时候使用懒加载?

适合懒加载的场景

  • 大型 SPA 项目,页面较多,资源多
  • 首屏不需要的组件,如 About.vueSettings.vue
  • 后台管理系统,多个独立模块

不适合懒加载的场景

  • 首屏关键页面(例如 Home.vue),否则会增加首屏加载时间
  • 高频访问的组件(可以用 webpackPrefetch 预加载)