- 做个简单的自我介绍
- 你作为前端负责人,然后有没有站在项目或者是业务角度去推进一个项目的进度?推动业务或者是对业务。服务增长进行推动的一些技术手段是比较优先的,针对项目的某些痛点进行哪些优化,或者是了解的哪些业务,作为一个开发技术的角度,去发现他可能哪些地方是会有点问题。我作为前端负责人然后包括就推动他解决完成我的业务流程的优化,包括整合。从技术方面,我怎么去完善我整个的开发链路,或者是说别的效应之类的,更支持业务的有效增长。(录音的,他就是这么问,感觉我当时疯狂在总结他想表达什么)
- 表单设计器是怎么实现的,描述一下你的Schama怎么设计的,前端渲染又是怎么实现的?(简历里面有写可视化表单配置)
- 表单校验为什么没有听到你的设计,你是怎么实现表单校验的特别的自定义表单校验规则?(忘记说了所以他提问)
- 我看你在这个项目做了一些性能优化方面的事情,你可以描述一下吗?(vue3+vite的项目)
- 图片优化,你知道怎么判断浏览器支不支持webp格式的图片
- 你平时有用performance吗?说说你怎么判断你的图片懒加载,你的图片压缩是否生效
- 我们聊一下常见的性质指标,像FCP,LCP,CLS,INP这些了解过吗?展开说说
- 你说说路由懒加载的原理 ?
2. 你作为前端负责人,然后有没有站在项目或者是业务角度去推进一个项目的进度?推动业务或者是对业务。服务增长进行推动的一些技术手段是比较优先的,针对项目的某些痛点进行哪些优化,或者是了解的哪些业务,作为一个开发技术的角度,去发现他可能哪些地方是会有点问题。我作为前端负责人然后包括就推动他解决完成我的业务流程的优化,包括整合。从技术方面,我怎么去完善我整个的开发链路,或者是说别的效应之类的,更支持业务的有效增长。(录音的,他就是这么问,感觉我当时疯狂在总结他想表达什么)
回答:如何作为前端负责人推动项目进度和业务增长
作为前端负责人,我会从以下几个方面来推动项目进度、优化业务流程,并通过技术手段促进业务增长:
一、项目推进:确保开发高效有序进行
-
拆解业务需求,提高开发效率
- 站在全局视角,将业务需求拆解成前端可落地的任务。
- 采用任务优先级排序(如 MoSCoW 法),确保核心业务功能优先上线。
- 制定前后端对接标准,避免接口定义不清导致的开发延误。
-
提升协作效率,优化开发流程
- 建立标准化的前端架构,如使用 Monorepo 方案、组件库等,减少重复开发。
- 通过 CI/CD(持续集成/持续部署) 自动化代码检查、测试、部署,提升交付效率。
- 采用Mock 数据 + 接口文档管理(如 Swagger) ,前后端并行开发,减少等待时间。
- 制定代码规范(如 ESLint、Prettier、Git Hook),降低代码维护成本。
二、业务增长:技术驱动业务价值
-
优化性能,提升用户体验
- 采用 SSR / CSR 结合 提升首屏渲染速度,提高用户留存率。
- 通过 图片/静态资源优化(CDN、Lazy Load、WebP) ,减少页面加载时间。
- 利用 骨架屏、渐进式加载,降低用户感知延迟,提高转化率。
-
数据埋点与 A/B 测试,驱动业务决策
- 在关键业务流程中 埋点(如用户点击、表单提交、跳出率),分析用户行为,优化产品设计。
- 结合 A/B 测试,验证 UI 变化、交互优化对转化率的影响。
- 通过日志分析(ELK、Sentry),快速发现和解决影响用户体验的技术问题。
-
低代码 / 组件库,提高开发复用度
- 搭建前端组件库(如基于 Vue3 + TS 设计的 UI 组件),减少重复开发,提高开发效率。
- 结合低代码平台(如拖拽式表单生成器),支持运营快速配置页面,减少前端介入成本。
三、发现并优化项目痛点
-
识别并解决性能瓶颈
- 通过Performance API、Lighthouse、WebPageTest 定位页面性能问题。
- 解决长任务阻塞(Long Tasks) ,使用 Web Worker、requestIdleCallback 等优化主线程占用。
- 采用 Code Splitting(按需加载)、Preload/Prefetch,优化资源加载顺序。
-
优化前后端接口交互
- 避免瀑布式请求,改为批量请求、GraphQL 方案。
- 使用 HTTP 缓存(ETag、Cache-Control)、本地缓存(IndexedDB、localStorage) 减少请求。
- 降低白屏时间,提前预加载 API 数据,减少用户等待。
四、技术完善与效能提升
-
技术选型与架构升级
- Vue2 向 Vue3 迁移,利用 Composition API + Pinia 提高代码复用性和可维护性。
- Webpack 升级 Vite,提升热更新速度,优化开发体验。
- 采用微前端架构(Qiankun、Module Federation),支持多团队并行开发,提升可扩展性。
-
开发链路优化,提升稳定性
- 自动化测试(Jest + Cypress) 确保业务逻辑稳定。
- 前端错误监控(Sentry + 埋点) ,快速发现线上异常。
- 灰度发布 + 版本回滚,降低新功能上线风险。
总结
作为前端负责人,不仅要提升开发效率,更要站在业务角度思考如何用技术驱动增长。核心方法包括:
- 优化前端架构和开发流程,加速项目推进。
- 通过性能优化、数据分析等手段,提升用户体验和转化率。
- 识别业务痛点,主动提出改进方案,从技术层面促进业务增长。
- 提升开发效能和稳定性,为业务扩展做好技术保障。
这不仅仅是写代码的问题,而是如何让技术为业务创造价值。
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-model或value实现双向绑定 - 处理
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>
三、可视化配置(拖拽式)
-
拖拽组件面板
- 预定义组件列表(如输入框、下拉框、日期选择器等)。
- 拖拽组件到设计区域,生成对应 Schema 结构。
-
属性编辑面板
- 选中表单项后,可在侧边栏修改
label、props、rules等配置。
- 选中表单项后,可在侧边栏修改
-
Schema 生成与存储
- 设计器完成后,导出 JSON Schema,存入数据库或本地存储。
- 预览模式下,解析 Schema,实时渲染表单。
四、总结
- Schema 设计:采用 JSON 定义表单结构,包括
fields、props、rules等。 - 动态渲染:Vue 通过
v-for遍历 Schema,动态绑定组件。 - 可视化配置:拖拽表单项,实时生成 Schema,实现低代码化开发。
这一方案适用于在线表单设计器、后台管理系统、低代码平台等场景。
4.表单校验为什么没有听到你的设计,你是怎么实现表单校验的特别的自定义表单校验规则?(忘记说了所以他提问)
5. 我看你在这个项目做了一些性能优化方面的事情,你可以描述一下吗?(vue3+vite的项目)
Vue3 + Vite 项目的性能优化可以从多个层面入手,包括代码优化、渲染优化、构建优化、网络优化、运行时优化等。下面是详细的优化策略:
1. 代码优化
1.1 避免不必要的组件重新渲染
Vue3 采用响应式系统,但不合理使用 reactive、ref 等可能导致组件频繁渲染,影响性能。
✅ 正确使用 ref 和 reactive
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-alpha、webp-animation、webp-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 记录
-
打开 DevTools(F12 或 Ctrl + Shift + I)
-
切换到 "Performance"(性能) 面板
-
点击 "Start profiling and reload page" (录制页面加载)
-
等待页面加载完成后,停止录制
-
在 Network(网络)部分,查找
img资源:- 懒加载成功:图片在滚动到可视区域后才加载
- 懒加载失败:所有图片在页面加载时就已经全部请求了
✅ 示例分析:
- 如果懒加载生效,图片的请求应该在 滚动后才出现,而不是一开始就全部加载。
方法 2:Network 面板检查
-
在 Network(网络)面板,勾选 "Img" 过滤只显示图片请求
-
刷新页面
-
检查图片的加载时机
- 懒加载生效:部分图片的
Initiator是 "lazy load" ,并且是在滚动后才加载 - 懒加载未生效:所有图片在页面加载时就请求了
- 懒加载生效:部分图片的
✅ 示例分析:
lazy关键字出现,表示浏览器支持<img loading="lazy">IntersectionObserver方式:如果看到script触发的fetch,说明是 JS 控制懒加载
方法 3:观察图片占位符
-
在页面上,检查图片的
src和data-src:<img src="placeholder.jpg" data-src="real-image.webp" class="lazyload"> -
使用 "Elements" 面板
- 如果
src还是占位符,而data-src在滚动后才替换成真实地址,说明懒加载生效了
- 如果
📌 2. 判断图片压缩是否生效
方法 1:Network 面板检查图片大小
-
打开 Network(网络) 面板,筛选 "Img" 资源
-
检查图片的 "Size"(大小)
- 如果 WebP 格式启用,图片应该比原始 JPG/PNG 小
- 压缩后图片的 "Content-Length"(响应大小)应显著减少
-
对比 "Transferred" vs. "Content-Length"
- Transferred 是实际下载的大小
- Content-Length 是原始文件大小
- 如果
Transferred远小于Content-Length,说明服务器启用了 Gzip 或 Brotli 压缩
✅ 示例分析:
- 图片原始大小 500 KB,WebP 版本只有 150 KB → 压缩生效
- 服务器返回
Content-Encoding: gzip或br→ 开启了服务器端压缩
方法 2:检查 Accept 头部是否支持 WebP
-
在 Network > Img 选择一张图片
-
进入 Headers(请求头部)
-
查看
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/webp→ WebP 成功启用
方法 3:实际对比图片质量
-
右键图片 → 打开图片
-
检查文件扩展名
- 如果是
.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-Type和Accept - 检查压缩是否生效 → 对比
Size和Content-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,使用
defer或async让 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)。
- 异步加载非关键 JS(使用
-
减少 LCP 元素的延迟
-
提前
preloadLCP 资源,如:<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 使用 defineAsyncComponent 或 import() 进行动态加载,主要依赖 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.vue、Settings.vue - 后台管理系统,多个独立模块
❌ 不适合懒加载的场景
- 首屏关键页面(例如
Home.vue),否则会增加首屏加载时间 - 高频访问的组件(可以用
webpackPrefetch预加载)