如何实现路由扁平化与权限控制
路由扁平化实现:
- **扁平化结构:将嵌套路由转化为一级结构,使用meta信息记录层级关系
- **动态路由:后端返回扁平路由配置,前端动态注册
- **菜单生成:根据扁平路由递归生成菜单树
- **实现方案:
javascript
// 原始嵌套路由
const nestedRoutes = [
{ path: '/admin', children: [
{ path: '/admin/user', children: [...] }
]}
];
// 扁平化路由
const flatRoutes = [
{ path: '/admin', parentPath: null },
{ path: '/admin/user', parentPath: '/admin' }
];权限控制实现:
- 路由守卫:前置守卫 beforeEach 拦截路由
- 权限校验:对比用户权限与路由meta.permission
- 动态路由:根据权限动态加载可访问路由
- 按钮级别权限:使用指令/函数控制按钮显示
- 实现方案:
javascript
router.beforeEach((to, from, next) => {
const hasPermission = checkPermission(to.meta.permission, userPermissions);
if (!hasPermission) next('/403');
else next();
});权限请求未返回怎么处理?
- 加载状态:显示loading骨架屏
- 缓存策略:使用上次权限缓存
- 超时处理:设置超时时间,超时跳转到登录页
- 重试机制:指数退避重试
- 降级方案:默认最低权限
- 实现方案:
javascript
async function getPermissions() {
try {
const cached = getCache('permissions');
if (cached) return cached;
const res = await fetchWithTimeout('/api/permissions', { timeout: 5000 });
setCache('permissions', res);
return res;
} catch (e) {
if (e.name === 'TimeoutError') {
return retryWithBackoff(() => getPermissions(), 3);
}
return defaultPermissions;
}
}点击超权页面?
- 路由拦截:beforeEach 拦截
- 403页面:跳转无权限提示页
- 菜单隐藏:超权菜单不显示
- 日志记录:记录超权操作日志
- 前端防御:前端防御,超链接跳转登录页
- 实现方案:
javascript
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !hasPermission(to)) {
logUnauthorizedAccess(to.path);
next({ path: '/403', query: { from: to.path } });
} else {
next();
}
};你如何在前端项目中推行代码规范与团队协作工具?
代码规范:
- ESLint + Prettier:代码格式化和检查
- Husky + lint-staged:提交前检查
- Commitlint:commit message规范
- 代码审查:PR代码审查
- 文档规范:组件文档、API文档
团队协作工具:
- Git Flow/GitHub/GitLab:版本控制
- Jira/Teambition:项目管理
- Figma:设计协作
- Confluence:知识库
- 钉钉/飞书:即时通讯
- 实施步骤:
- 配置工具→培训→试点→推广→持续改进
多人开发一个项目,如何进行团队协作,可以减少不必要沟通
清晰的架构设计:
- 模块化设计
- 明确的接口定义
- 文档化API
规范的开发流程:
- Git分支策略(Git Flow)
- 代码规范
- 代码审查
自动化工具:
- CI/CD流水线
- 自动测试
- 自动部署
文档完善:
- 架构文档
- API文档
- 开发指南
定期同步:
- 站会
- 技术分享
- 代码回顾
数据量大场景怎么做瀑布流加载,说一下实现逻辑
实现逻辑:
- 列布局:多列等高排列
- 计算高度:记录每列高度
- 最小高度插入:新元素插入最小高度列
- 懒加载:滚动到底部加载更多
- 虚拟滚动:大数据量使用虚拟列表
- 实现方案:
javascript
class Waterfall {
constructor(container, columns = 3) {
this.columns = new Array(columns).fill(0);
this.container = container;
}
append(item) {
const minIndex = this.columns.indexOf(Math.min(...this.columns));
const column = this.container.children[minIndex];
column.appendChild(item);
this.columns[minIndex] += item.offsetHeight;
}
}大文件上传的交互流程是怎么样的,上传过程中失败了怎么办,重试机制是怎么样的?
前端组件二次封装需要注意什么?
主要在哪些场景下会封装组件?
业务组件:
- 表单组件
- 表格组件
- 弹窗组件
通用组件:
- 按钮组件
- 图标组件
- 布局组件
功能组件:
- 上传组件
- 分页组件
- 搜索组件
封装原则:
- 复用性
- 可维护性
- 可扩展性
如何设计一个系统,要求可对外提供UI和页面包?
架构设计:
- 组件库
- 页面模板
- 主题系统
打包方案:
- UMD/ESM/CJS格式
- Tree Shaking支持
- 按需加载
文档系统:
- Storybook
- API文档
- 示例展示
版本管理:
- Semantic Versioning
- CHANGELOG
- 升级指南
技术栈:
- Vue/React
- Rollup/Webpack
- TypeScript
怎么优化主题切换导致的闪屏?
- CSS变量:使用CSS自定义属性
- 预加载:提前加载主题样式
- 服务端渲染:SSR时确定主题
- localStorage:持久化主题
- 过渡动画:平滑过渡
- 实现方案:
css
:root { --primary-color: #1890ff; }
[data-theme="dark"] { --primary-color: #177ddc; }javascript
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}微前端有哪些加载方式,区别是什么,如何做的环境隔离?
在日常项目中,大数据懒加载和diff优化是如何做到?除此之外还有其他优化吗?
懒加载:
- 图片懒加载:Intersection Observer
- 路由懒加载:动态import
- 组件懒加载:异步组件
- 虚拟列表:只渲染可视区域
Diff优化:
key优化:
- 唯一key
- 避免index作为key
Vue优化:
- v-once
- v-memo
- 计算属性缓存
React优化:
- memo
- useMemo
- useCallback
其他优化:
- 防抖节流
- Web Worker
- RequestAnimationFrame
- WebAssembly
首屏优化有哪些手段?
资源优化:
- 代码分割
- 懒加载
- 预加载
- 压缩
网络优化:
- CDN
- HTTP/2
- 缓存策略
- 预连接
渲染优化:
- SSR/SSG
- 骨架屏
- 关键路径CSS
- 避免JS延迟
服务端优化:
- Gzip压缩
- 服务端渲染
- 接口缓存
重排重绘
重排(Reflow):
- 定义:元素几何属性改变导致布局重新计算
- 触发条件:
- 尺寸改变
- 位置改变
- display:none
- 内容改变
重绘(Repaint):
- 定义:元素外观改变不影响布局
- 触发条件:
- color
- background
- visibility
优化策略:
- 批量修改:DocumentFragment/offscreen
- 避免频繁读取:缓存布局属性
- transform:使用GPU加速
- will-change:提示浏览器优化
- 实现方案:
javascript
// 优化前
for (let i = 0; i < 100; i++) {
el.style.top = i + 'px';
}
// 优化后
el.style.willChange = 'transform';
el.style.transform = `translateY(${100}px)`;