顺丰面试题
1. 你擅长处理哪类问题
推荐回答:
"我比较擅长处理以下几类前端问题:
性能优化:包括加载优化(代码分割、懒加载)、运行时优化(减少重排重绘)等
复杂组件开发:如表单联动、可视化图表等交互复杂的组件
工程化问题:Webpack配置优化、自动化部署等
跨平台兼容:解决不同浏览器和设备下的兼容性问题
2. 数组 for in 和 for of 区别
const arr = ['a', 'b', 'c'];
arr.customProp = 'd';// for in (遍历键名)
for (let key in arr) {console.log(key); // 输出: 0, 1, 2, 'customProp'console.log(typeof key); // 'string'
}// for of (遍历值)
for (let value of arr) {console.log(value); // 输出: 'a', 'b', 'c'
}
区别总结:
特性 | for...in | for...of |
---|---|---|
遍历内容 | 可枚举属性(包括原型链) | 可迭代对象的值 |
适用对象 | 对象/数组 | 数组/Map/Set等可迭代对象 |
顺序保证 | 不保证顺序 | 保证迭代顺序 |
原型属性 | 会遍历原型链上的属性 | 只遍历对象自身值 |
3. 数组截取方法
常用方法:
const arr = [1, 2, 3, 4, 5];// 1. slice (不改变原数组)
arr.slice(1, 3); // [2, 3]// 2. splice (改变原数组)
arr.splice(1, 2); // 返回[2, 3], arr变为[1, 4, 5]// 3. 扩展运算符
const [first, ...rest] = arr; // first=1, rest=[2,3,4,5]// 4. filter (条件截取)
arr.filter(x => x > 2); // [3,4,5]
对比选择:
需要原数组不变 →
slice
需要修改原数组 →
splice
需要条件筛选 →
filter
4. 数组包含方法
检测方法:
const arr = [1, 2, 3];// 1. includes (ES7)
arr.includes(2); // true// 2. indexOf
arr.indexOf(2) !== -1; // true// 3. some (复杂条件)
arr.some(x => x > 2); // true// 4. find/findIndex (对象数组)
const objArr = [{id:1}, {id:2}];
objArr.find(o => o.id === 2); // {id:2}
性能建议:
简单值判断用
includes
最直观对象数组用
some
/find
需要索引值时用
indexOf
/findIndex
5. 类型检测的方法
全面方案:
// 1. typeof (基本类型)
typeof 'str'; // 'string'
typeof 123; // 'number'
typeof true; // 'boolean'
typeof undefined; // 'undefined'
typeof Symbol(); // 'symbol'
typeof 123n; // 'bigint'
typeof function(){}; // 'function'// 局限性
typeof null; // 'object'
typeof []; // 'object'// 2. instanceof (对象类型)
[] instanceof Array; // true
new Date() instanceof Date; // true// 3. Object.prototype.toString.call (最准确)
Object.prototype.toString.call(null); // '[object Null]'
Object.prototype.toString.call([]); // '[object Array]'// 4. Array.isArray (专用于数组)
Array.isArray([]); // true// 5. 自定义类型检查
class MyClass {}
const obj = new MyClass();
obj.constructor === MyClass; // true
最佳实践:
基本类型 →
typeof
数组 →
Array.isArray()
通用对象类型 →
Object.prototype.toString.call()
自定义类实例 →
instanceof
或constructor
检查
6. Vue 路由传参的方式
三种主要方式:
1. 动态路由
// 路由配置
{path: '/user/:id',component: User
}// 跳转
router.push('/user/123')// 获取
this.$route.params.id // '123'
2. query 传参
// 跳转
router.push({path: '/user',query: { id: '123' }
})// 获取
this.$route.query.id // '123'// URL表现: /user?id=123
3. props 解耦
// 路由配置
{path: '/user/:id',component: User,props: true
}// 组件接收
export default {props: ['id']
}
高级用法:
// 命名路由
router.push({ name: 'user', params: { id: '123' } })// 替换当前路由
router.replace({ path: '/user/123' })// 保持查询参数
router.push({ query: { ...this.$route.query, id: '123' } })
7. 插槽怎么传参
作用域插槽示例:
<!-- 子组件 -->
<template><div><slot name="header" :user="user"></slot><slot :data="listData"></slot></div>
</template><script>
export default {data() {return {user: { name: 'John' },listData: [1, 2, 3]}}
}
</script><!-- 父组件使用 -->
<ChildComponent><template #header="{ user }"><h1>{{ user.name }}的头像</h1></template><template v-slot:default="slotProps"><div v-for="item in slotProps.data" :key="item">{{ item }}</div></template>
</ChildComponent>
Vue3 组合式API写法:
<!-- 子组件 -->
<script setup>
const user = ref({ name: 'John' })
const listData = ref([1, 2, 3])
</script><template><slot name="header" :user="user"></slot><slot :data="listData"></slot>
</template>
8. 使用过哪些微前端
主流方案经验:
1. qiankun (基于single-spa)
// 主应用
import { registerMicroApps, start } from 'qiankun';registerMicroApps([{name: 'vueApp',entry: '//localhost:7100',container: '#subapp-container',activeRule: '/vue',}
]);start();// 子应用导出生命周期
export async function bootstrap() {}
export async function mount() {}
export async function unmount() {}
2. Module Federation (Webpack5)
// webpack.config.js (主应用)
new ModuleFederationPlugin({name: 'host',remotes: {app1: 'app1@http://localhost:3001/remoteEntry.js'}
});// 使用
import('app1/Button').then(ButtonModule => {const Button = ButtonModule.default;// 渲染Button
});
3. iframe (传统方案)
优缺点:
优点:隔离彻底,实现简单
缺点:通信复杂,体验不一致
选型建议:
需要快速接入 → qiankun
需要细粒度控制 → Module Federation
需要强隔离 → iframe
9. 服务端渲染
核心流程:
优势:
更好的SEO
更快的首屏渲染
更好的低端设备兼容性
实现方案:
Next.js (React):开箱即用SSR支持
Nuxt.js (Vue):约定式路由SSR
自定义SSR:使用Vue Server Renderer / ReactDOMServer
10. 页面渲染做了哪些操作
详细渲染过程:
解析HTML:
构建DOM树
遇到CSS/JS会并行下载
解析CSS:
构建CSSOM树
阻塞渲染(CSS是渲染阻塞资源)
合并渲染树:
结合DOM和CSSOM
排除
display:none
等不可见元素
布局计算:
计算每个节点的确切位置和大小
也称为"回流"(Reflow)
绘制:
将渲染树转换为屏幕像素
分层(Layer)绘制,使用GPU加速
合成:
将各层合并为最终页面
处理transform/opacity等属性
优化点:
减少重排重绘
使用will-change提示浏览器
关键渲染路径优化
11. 服务器端渲染和客户端渲染的区别
对比表格:
特性 | 服务端渲染 (SSR) | 客户端渲染 (CSR) |
---|---|---|
渲染位置 | 服务器端 | 浏览器端 |
首次响应内容 | 完整HTML | 空HTML骨架+JS |
SEO友好性 | 优秀 | 较差(需额外处理) |
首屏时间 | 快(立即显示内容) | 慢(需等待JS执行) |
服务器压力 | 高(每次请求需渲染) | 低(静态文件托管) |
交互响应速度 | 需等待JS加载完成 | 后续交互更快 |
技术复杂度 | 高(需处理同构等) | 低 |
选型建议:
需要SEO/首屏速度 → SSR
复杂交互/后台系统 → CSR
混合方案 → 关键页面SSR + 其他CSR
12. 开发中配置跨域和上线后配置跨域
开发环境配置
Vue CLI:
// vue.config.js
module.exports = {devServer: {proxy: {'/api': {target: 'http://backend:8080',changeOrigin: true,pathRewrite: { '^/api': '' }}}}
}
Vite:
// vite.config.js
export default defineConfig({server: {proxy: {'/api': {target: 'http://backend:8080',changeOrigin: true,rewrite: path => path.replace(/^\/api/, '')}}}
})
生产环境Nginx配置
server {listen 80;server_name yourdomain.com;location /api/ {proxy_pass http://backend-server/;# CORS配置add_header 'Access-Control-Allow-Origin' '$http_origin';add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';# 预检请求处理if ($request_method = 'OPTIONS') {add_header 'Access-Control-Max-Age' 1728000;return 204;}}# 前端静态资源location / {root /usr/share/nginx/html;try_files $uri $uri/ /index.html;}
}
高级配置:
# 多环境配置
map $env $backend {default "http://default-server";staging "http://staging-server";prod "http://prod-server";
}# HTTPS配置
server {listen 443 ssl;ssl_certificate /path/to/cert.pem;ssl_certificate_key /path/to/key.pem;location /api/ {proxy_pass $backend;# ...其他配置同上}
}