Day 9: 技术亮点讲解
第九天重点:提炼项目中的技术亮点,准备深度讲解
更新时间: 2025-01
📋 目录
今日目标
- 提炼 3-5 个技术亮点
- 每个亮点能深入讲解原理
- 准备亮点的演进过程
- 能够回答深度追问
Part A: 技术亮点的定义
1. 什么是技术亮点?
┌─────────────────────────────────────────────────────────────┐
│ 技术亮点特征 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 有深度 │
│ - 不是简单的 CRUD │
│ - 涉及到底层原理或复杂逻辑 │
│ - 需要深入思考才能解决 │
│ │
│ 2. 有价值 │
│ - 解决了实际问题 │
│ - 带来了可量化的收益 │
│ - 形成了可复用的方案 │
│ │
│ 3. 有创新 │
│ - 不是照搬现成方案 │
│ - 有自己的思考和改进 │
│ - 或者将现有技术创新应用 │
│ │
│ 4. 能展示能力 │
│ - 体现技术深度 │
│ - 体现解决问题的能力 │
│ - 体现学习和成长 │
│ │
└─────────────────────────────────────────────────────────────┘2. 亮点讲解结构
【亮点名称】简洁有力的标题
【背景价值】
- 为什么做这个?
- 解决什么问题?
- 带来什么价值?
【技术方案】
- 核心思路是什么?
- 关键技术点有哪些?
- 为什么选择这个方案?
【实现细节】
- 核心代码实现
- 难点处理
- 边界情况
【效果数据】
- 量化的结果
- 前后对比
【经验沉淀】
- 学到了什么?
- 可以复用什么?Part B: 技术亮点案例
亮点1:高性能虚拟滚动组件
背景价值
业务需求:
- 后台系统需要展示大量数据(10万+)
- 传统分页无法满足需求,用户需要流畅滚动浏览
- 现有的虚拟滚动库无法满足复杂表格需求
技术挑战:
- 支持动态行高
- 支持表格合并单元格
- 支持固定列
- 性能要求:60fps 流畅滚动技术方案
javascript
// 核心设计思路
// 1. 只渲染可视区域的元素
// 2. 使用 transform 代替修改 top 值
// 3. 动态计算可视区域范围
// 4. 缓冲区减少频繁更新
class VirtualScroller {
constructor(options) {
this.containerHeight = options.containerHeight;
this.itemHeightGetter = options.itemHeightGetter; // 支持动态高度
this.buffer = options.buffer || 5; // 缓冲区大小
this.items = [];
this.positions = []; // 缓存每项位置
this.init();
}
// 初始化位置缓存
init() {
this.positions = this.items.map((item, index) => {
const height = this.itemHeightGetter(item, index);
const prevPosition = this.positions[index - 1];
const top = prevPosition ? prevPosition.bottom : 0;
return {
index,
height,
top,
bottom: top + height,
};
});
this.totalHeight = this.positions[this.positions.length - 1]?.bottom || 0;
}
// 二分查找起始索引
findStartIndex(scrollTop) {
let low = 0;
let high = this.positions.length - 1;
while (low <= high) {
const mid = Math.floor((low + high) / 2);
const midBottom = this.positions[mid].bottom;
if (midBottom === scrollTop) {
return mid + 1;
} else if (midBottom < scrollTop) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return low;
}
// 计算可视范围
getVisibleRange(scrollTop) {
const startIndex = Math.max(0, this.findStartIndex(scrollTop) - this.buffer);
const endIndex = Math.min(
this.positions.length - 1,
this.findEndIndex(scrollTop + this.containerHeight) + this.buffer
);
return {
startIndex,
endIndex,
startOffset: this.positions[startIndex]?.top || 0,
};
}
// 获取可视数据
getVisibleItems(scrollTop) {
const { startIndex, endIndex, startOffset } = this.getVisibleRange(scrollTop);
return {
items: this.items.slice(startIndex, endIndex + 1),
startOffset,
totalHeight: this.totalHeight,
};
}
}
// Vue 组件封装
const VirtualList = defineComponent({
props: {
items: Array,
itemHeight: [Number, Function],
containerHeight: Number,
},
setup(props, { slots }) {
const scrollTop = ref(0);
const scroller = ref(null);
watchEffect(() => {
scroller.value = new VirtualScroller({
items: props.items,
itemHeightGetter: typeof props.itemHeight === 'function'
? props.itemHeight
: () => props.itemHeight,
containerHeight: props.containerHeight,
});
});
const visibleData = computed(() =>
scroller.value?.getVisibleItems(scrollTop.value)
);
const handleScroll = (e) => {
scrollTop.value = e.target.scrollTop;
};
return () => (
<div
class="virtual-list-container"
style={{ height: `${props.containerHeight}px`, overflow: 'auto' }}
onScroll={handleScroll}
>
<div
class="virtual-list-phantom"
style={{ height: `${visibleData.value?.totalHeight}px` }}
/>
<div
class="virtual-list-content"
style={{ transform: `translateY(${visibleData.value?.startOffset}px)` }}
>
{visibleData.value?.items.map((item, index) =>
slots.default?.({ item, index })
)}
</div>
</div>
);
},
});效果数据
测试数据:10 万条记录
优化前(全量渲染):
- 首次渲染时间:8s+
- 滚动帧率:5fps
- 内存占用:1.2GB
- 页面崩溃率:30%
优化后(虚拟滚动):
- 首次渲染时间:200ms
- 滚动帧率:60fps
- 内存占用:80MB
- 页面崩溃率:0%深度追问准备
Q: 为什么用 transform 而不是 absolute + top?
A: transform 不会触发重排(reflow),只触发重绘(repaint),
而且可以开启 GPU 加速,性能更好。
Q: 动态高度是怎么处理的?
A: 通过位置缓存数组记录每一项的 top 和 bottom,
滚动时用二分查找快速定位起始索引。
Q: 如果高度未知怎么办?
A: 可以先用预估高度渲染,渲染后获取真实高度更新位置缓存。亮点2:前端权限系统设计
背景价值
业务需求:
- 系统有多种角色(管理员、运营、客服等)
- 需要控制页面级、按钮级权限
- 权限可能动态变化,需要热更新
- 前后端权限需要一致
技术挑战:
- 权限粒度要细
- 性能要好(不能每次都请求接口)
- 维护要方便技术方案
javascript
// 权限架构设计
// 1. 基于 RBAC 模型
// 2. 前端三层控制:路由、组件、指令
// 3. 权限数据缓存 + 增量更新
// 权限服务
class PermissionService {
constructor() {
this.permissions = new Set();
this.routes = [];
this.initialized = false;
}
// 初始化权限
async init() {
if (this.initialized) return;
const [permissionList, routeList] = await Promise.all([
fetchPermissions(),
fetchRoutes(),
]);
this.permissions = new Set(permissionList);
this.routes = routeList;
this.initialized = true;
}
// 检查权限
hasPermission(permission) {
if (Array.isArray(permission)) {
return permission.some(p => this.permissions.has(p));
}
return this.permissions.has(permission);
}
// 权限热更新
async refresh() {
const newPermissions = await fetchPermissions();
this.permissions = new Set(newPermissions);
// 触发路由重新生成
this.regenerateRoutes();
}
// 生成动态路由
generateRoutes() {
return this.filterRoutes(this.routes);
}
filterRoutes(routes) {
return routes.filter(route => {
if (route.permission && !this.hasPermission(route.permission)) {
return false;
}
if (route.children) {
route.children = this.filterRoutes(route.children);
}
return true;
});
}
}
export const permissionService = new PermissionService();
// 路由守卫
router.beforeEach(async (to, from, next) => {
await permissionService.init();
if (to.meta?.permission) {
if (!permissionService.hasPermission(to.meta.permission)) {
return next('/403');
}
}
next();
});
// 权限指令
app.directive('permission', {
mounted(el, binding) {
const { value, modifiers } = binding;
if (!permissionService.hasPermission(value)) {
if (modifiers.disable) {
// 禁用模式:置灰
el.disabled = true;
el.style.opacity = '0.5';
el.style.cursor = 'not-allowed';
} else {
// 隐藏模式:移除
el.parentNode?.removeChild(el);
}
}
},
});
// 组件级权限
const PermissionWrapper = defineComponent({
props: {
permission: [String, Array],
fallback: {
type: Object,
default: null,
},
},
setup(props, { slots }) {
const hasPermission = computed(() =>
permissionService.hasPermission(props.permission)
);
return () => {
if (hasPermission.value) {
return slots.default?.();
}
return props.fallback || slots.fallback?.();
};
},
});
// 使用示例
<template>
<!-- 指令控制按钮 -->
<button v-permission="'user:delete'">删除用户</button>
<button v-permission.disable="'user:edit'">编辑用户</button>
<!-- 组件控制区块 -->
<PermissionWrapper permission="dashboard:view">
<DashboardPanel />
<template #fallback>
<NoPermissionTip />
</template>
</PermissionWrapper>
</template>效果数据
实现效果:
- 支持 100+ 个权限点的精细控制
- 权限变更实时生效,无需刷新页面
- 前后端权限一致性 100%
开发效率:
- 新增权限点只需配置,无需改代码
- 权限逻辑统一管理,维护成本低亮点3:微前端架构落地
背景价值
业务背景:
- 公司有多个独立系统,技术栈不统一
- 需要整合到统一平台,用户一次登录访问所有系统
- 各子系统需要独立开发、独立部署
技术挑战:
- 不同技术栈(Vue2、Vue3、React)共存
- 子应用样式和 JS 隔离
- 应用间通信
- 公共依赖共享技术方案
javascript
// 基于 qiankun 的微前端架构
// 主应用配置
import { registerMicroApps, start, initGlobalState } from 'qiankun';
// 子应用配置
const apps = [
{
name: 'vue-app',
entry: '//localhost:8001',
container: '#micro-container',
activeRule: '/vue-app',
props: {
token: getToken(),
user: getUserInfo(),
},
},
{
name: 'react-app',
entry: '//localhost:8002',
container: '#micro-container',
activeRule: '/react-app',
},
];
// 生命周期钩子
const lifeCycles = {
beforeLoad: [
app => {
console.log('before load', app.name);
},
],
beforeMount: [
app => {
console.log('before mount', app.name);
},
],
afterUnmount: [
app => {
console.log('after unmount', app.name);
},
],
};
registerMicroApps(apps, lifeCycles);
// 全局状态管理
const { onGlobalStateChange, setGlobalState } = initGlobalState({
user: null,
token: '',
});
// 监听全局状态变化
onGlobalStateChange((state, prev) => {
console.log('Global state changed:', state, prev);
});
// 修改全局状态
setGlobalState({ token: 'new-token' });
start({
sandbox: {
strictStyleIsolation: true, // 严格样式隔离
experimentalStyleIsolation: true,
},
prefetch: 'all', // 预加载所有子应用
});
// 子应用配置(Vue3)
// vue.config.js
module.exports = {
devServer: {
port: 8001,
headers: {
'Access-Control-Allow-Origin': '*',
},
},
configureWebpack: {
output: {
library: 'vueApp',
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_vueApp`,
},
},
};
// main.js
let instance = null;
function render(props = {}) {
const { container } = props;
instance = createApp(App);
instance.mount(container ? container.querySelector('#app') : '#app');
}
// 独立运行
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
// 导出生命周期钩子
export async function bootstrap() {
console.log('vue app bootstrapped');
}
export async function mount(props) {
console.log('vue app mounted');
render(props);
}
export async function unmount() {
console.log('vue app unmounted');
instance.unmount();
instance = null;
}
// 应用间通信
// 主应用
import { EventBus } from './utils/event-bus';
window.eventBus = new EventBus();
// 子应用
window.eventBus.emit('user:login', { userId: 123 });
window.eventBus.on('message:new', (data) => {
// 处理消息
});效果数据
架构效果:
- 成功整合 5 个子系统
- 支持 Vue2、Vue3、React 混合部署
- 子应用独立开发部署,互不影响
性能指标:
- 子应用加载时间 < 2s
- 应用切换无感知(预加载)
- 内存泄漏检测:切换 100 次无明显增长
开发效率:
- 新增子应用接入时间 < 1 天
- 复用公共组件和服务亮点4:前端监控系统搭建
背景价值
业务需求:
- 线上问题难以复现和定位
- 缺乏性能数据支撑优化决策
- 用户行为分析需求
技术目标:
- 错误监控:JS 错误、资源加载错误、接口错误
- 性能监控:页面加载、接口耗时、渲染性能
- 用户行为:PV/UV、点击、停留时间技术方案
javascript
// 监控 SDK 设计
class Monitor {
constructor(options) {
this.appId = options.appId;
this.reportUrl = options.reportUrl;
this.queue = [];
this.timer = null;
this.init();
}
init() {
this.initErrorMonitor();
this.initPerformanceMonitor();
this.initBehaviorMonitor();
this.startReportLoop();
}
// 错误监控
initErrorMonitor() {
// JS 运行时错误
window.onerror = (msg, url, line, col, error) => {
this.report({
type: 'js_error',
data: {
message: msg,
url,
line,
col,
stack: error?.stack,
},
});
};
// Promise 未捕获错误
window.onunhandledrejection = (event) => {
this.report({
type: 'promise_error',
data: {
message: event.reason?.message || event.reason,
stack: event.reason?.stack,
},
});
};
// 资源加载错误
window.addEventListener('error', (event) => {
const target = event.target;
if (target.tagName) {
this.report({
type: 'resource_error',
data: {
tagName: target.tagName,
src: target.src || target.href,
},
});
}
}, true);
// 接口错误(拦截 fetch 和 xhr)
this.interceptFetch();
this.interceptXHR();
}
interceptFetch() {
const originalFetch = window.fetch;
window.fetch = async (url, options) => {
const startTime = Date.now();
try {
const response = await originalFetch(url, options);
this.reportApi(url, startTime, response.status, response.ok);
return response;
} catch (error) {
this.reportApi(url, startTime, 0, false, error.message);
throw error;
}
};
}
reportApi(url, startTime, status, success, errorMsg) {
this.report({
type: 'api',
data: {
url,
duration: Date.now() - startTime,
status,
success,
errorMsg,
},
});
}
// 性能监控
initPerformanceMonitor() {
// 页面加载性能
window.addEventListener('load', () => {
setTimeout(() => {
const timing = performance.timing;
const paintTiming = performance.getEntriesByType('paint');
this.report({
type: 'performance',
data: {
// DNS 解析
dns: timing.domainLookupEnd - timing.domainLookupStart,
// TCP 连接
tcp: timing.connectEnd - timing.connectStart,
// 首字节时间
ttfb: timing.responseStart - timing.requestStart,
// DOM 解析
domParse: timing.domInteractive - timing.responseEnd,
// DOM Ready
domReady: timing.domContentLoadedEventEnd - timing.navigationStart,
// 页面完全加载
load: timing.loadEventEnd - timing.navigationStart,
// FP
fp: paintTiming.find(e => e.name === 'first-paint')?.startTime,
// FCP
fcp: paintTiming.find(e => e.name === 'first-contentful-paint')?.startTime,
},
});
}, 100);
});
// LCP、FID、CLS(Web Vitals)
this.observeWebVitals();
}
observeWebVitals() {
// LCP
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
this.report({
type: 'web_vitals',
data: {
name: 'LCP',
value: lastEntry.startTime,
},
});
}).observe({ entryTypes: ['largest-contentful-paint'] });
// FID
new PerformanceObserver((list) => {
const entry = list.getEntries()[0];
this.report({
type: 'web_vitals',
data: {
name: 'FID',
value: entry.processingStart - entry.startTime,
},
});
}).observe({ entryTypes: ['first-input'] });
// CLS
let clsValue = 0;
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
clsValue += entry.value;
}
}
this.report({
type: 'web_vitals',
data: {
name: 'CLS',
value: clsValue,
},
});
}).observe({ entryTypes: ['layout-shift'] });
}
// 用户行为监控
initBehaviorMonitor() {
// PV
this.report({
type: 'pv',
data: {
url: location.href,
referrer: document.referrer,
},
});
// 点击事件
document.addEventListener('click', (e) => {
const target = e.target;
this.report({
type: 'click',
data: {
tagName: target.tagName,
className: target.className,
id: target.id,
text: target.innerText?.slice(0, 50),
path: this.getXPath(target),
},
});
});
// 路由变化(SPA)
this.listenRouteChange();
}
// 数据上报
report(data) {
this.queue.push({
...data,
appId: this.appId,
timestamp: Date.now(),
url: location.href,
userAgent: navigator.userAgent,
});
}
startReportLoop() {
this.timer = setInterval(() => {
if (this.queue.length > 0) {
const data = this.queue.splice(0, 10);
this.send(data);
}
}, 5000);
// 页面卸载时上报
window.addEventListener('beforeunload', () => {
if (this.queue.length > 0) {
this.send(this.queue, true);
}
});
}
send(data, useBeacon = false) {
if (useBeacon && navigator.sendBeacon) {
navigator.sendBeacon(this.reportUrl, JSON.stringify(data));
} else {
fetch(this.reportUrl, {
method: 'POST',
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' },
});
}
}
}
// 使用
const monitor = new Monitor({
appId: 'my-app',
reportUrl: '/api/monitor/report',
});效果数据
监控覆盖:
- JS 错误捕获率:99%+
- 性能数据采集:100%
- 接口监控覆盖:100%
业务价值:
- 问题发现时间从用户反馈的 1 天缩短到 5 分钟
- 性能优化有数据支撑,首屏时间优化 40%
- 错误率从 2% 降低到 0.5%亮点5:低代码表单引擎
背景价值
业务需求:
- 后台有大量表单页面,开发成本高
- 表单需求变化频繁,改动成本大
- 希望产品运营能自行配置表单
技术目标:
- 通过 JSON Schema 配置生成表单
- 支持复杂联动和校验规则
- 可视化拖拽配置技术方案
javascript
// 表单引擎核心设计
// Schema 定义
const formSchema = {
type: 'object',
properties: {
name: {
type: 'string',
title: '姓名',
required: true,
maxLength: 20,
ui: {
widget: 'input',
placeholder: '请输入姓名',
},
},
gender: {
type: 'string',
title: '性别',
enum: ['male', 'female'],
enumNames: ['男', '女'],
ui: {
widget: 'radio',
},
},
age: {
type: 'number',
title: '年龄',
minimum: 0,
maximum: 150,
ui: {
widget: 'number',
},
},
city: {
type: 'string',
title: '城市',
ui: {
widget: 'select',
options: [], // 动态加载
dependencies: ['province'], // 依赖省份
},
},
},
// 联动规则
rules: [
{
when: { gender: 'female' },
then: {
age: { maximum: 120 },
},
},
{
when: { province: { $exists: true } },
effect: 'loadCities', // 触发加载城市
},
],
};
// 表单渲染器
const FormRenderer = defineComponent({
props: {
schema: Object,
value: Object,
onChange: Function,
},
setup(props) {
const formData = ref(props.value || {});
const widgetRegistry = inject('widgetRegistry');
// 递归渲染字段
const renderField = (key, fieldSchema) => {
const Widget = widgetRegistry.get(fieldSchema.ui?.widget || 'input');
return (
<FormItem
key={key}
label={fieldSchema.title}
required={fieldSchema.required}
rules={generateRules(fieldSchema)}
>
<Widget
value={formData.value[key]}
onChange={(val) => {
formData.value[key] = val;
handleFieldChange(key, val);
}}
{...fieldSchema.ui}
/>
</FormItem>
);
};
// 处理字段变化
const handleFieldChange = async (key, value) => {
// 执行联动规则
await executeRules(key, value);
props.onChange?.(formData.value);
};
// 执行联动规则
const executeRules = async (changedKey, value) => {
const { rules } = props.schema;
if (!rules) return;
for (const rule of rules) {
if (matchCondition(rule.when, formData.value)) {
if (rule.then) {
// 修改其他字段
applyThen(rule.then);
}
if (rule.effect) {
// 执行副作用
await executeEffect(rule.effect, formData.value);
}
}
}
};
return () => (
<Form>
{Object.entries(props.schema.properties).map(([key, field]) =>
renderField(key, field)
)}
</Form>
);
},
});
// 组件注册
const widgetRegistry = new Map([
['input', InputWidget],
['select', SelectWidget],
['radio', RadioWidget],
['checkbox', CheckboxWidget],
['date', DatePickerWidget],
['upload', UploadWidget],
// 可扩展...
]);
// 校验规则生成
function generateRules(schema) {
const rules = [];
if (schema.required) {
rules.push({ required: true, message: `${schema.title}不能为空` });
}
if (schema.maxLength) {
rules.push({ max: schema.maxLength, message: `最多${schema.maxLength}个字符` });
}
if (schema.pattern) {
rules.push({ pattern: new RegExp(schema.pattern), message: schema.patternMessage });
}
// 更多规则...
return rules;
}效果数据
开发效率:
- 简单表单配置时间:5 分钟
- 复杂表单配置时间:30 分钟
- 相比传统开发效率提升 5 倍
业务价值:
- 产品可自行配置 80% 的表单需求
- 表单变更从开发测试上线 3 天缩短到配置即生效Part C: 亮点讲解技巧
1. 讲解层次
┌─────────────────────────────────────────────────────────────┐
│ 技术亮点讲解层次 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 第一层:概述(1分钟) │
│ - 一句话说明做了什么 │
│ - 解决了什么问题 │
│ - 主要成果 │
│ │
│ 第二层:方案(2-3分钟) │
│ - 技术选型和原因 │
│ - 核心设计思路 │
│ - 关键技术点 │
│ │
│ 第三层:细节(按需深入) │
│ - 具体实现代码 │
│ - 难点处理 │
│ - 边界情况 │
│ │
│ 第四层:反思(1分钟) │
│ - 有什么不足 │
│ - 如果重做会怎么改进 │
│ - 学到了什么 │
│ │
└─────────────────────────────────────────────────────────────┘2. 回答追问的技巧
当面试官追问时:
1. 不要慌
- 追问说明面试官感兴趣
- 这是展示深度的机会
2. 结构化回答
- 先总后分
- 先主后次
3. 诚实回答
- 不知道就说不知道
- 可以说"这个我还没深入研究,但我的理解是..."
4. 主动延伸
- 回答完可以补充相关的知识点
- 展示知识面的广度复习检查清单
- 准备 3-5 个技术亮点
- 每个亮点能讲 3-5 分钟
- 准备好深度追问的回答
- 有具体的量化数据
- 能画出核心架构图
明日预告:Day 10 - 模拟面试