磐松资产-前端实习-一面
时间:一面11.21.25 时长:30min
1 考试信息
使用ShowMeBug这平台来考,双机位的摄像头监考,考的东西不太难,但是我有点忘了。大题的最后一道编程题,那个系统不太会用,浪费了不少时间。
2 考试内容(一共17道)
有点忘了
选择
- reduce,累加的用法
- 对象的字面量创建
- Array.from的用法,它能做什么
- 将一个数组转化为使用逗号分隔的字符串
- 如何为对象添加一个方法
填空
- 初始化一个空对象的方法
- 连接字符串的符号
- 忘了
大题(三道)
- 分析事件循环机制
- 编程,点击背景将一个三角型的颜色由红转换为粉色
3 参考答案
以下是前端笔试题目对应的参考答案,兼顾基础知识点准确性和实际应用场景,符合面试笔试答题规范:
一、选择题(含解析)
1. reduce 累加的用法
答案:reduce 是数组方法,用于对数组元素逐个处理并累加得到最终结果,核心语法:arr.reduce((acc, curr, idx, arr) => {}, initialValue)
- 关键参数:
- 回调函数:接收4个参数(
acc累计值、curr当前元素、idx当前索引、arr原数组) initialValue(可选):累计值初始值,不传递则默认以数组第1个元素为初始acc
- 回调函数:接收4个参数(
- 示例(数组求和):javascript
const arr = [1, 2, 3]; const sum = arr.reduce((acc, curr) => acc + curr, 0); // 结果:6
2. 对象的字面量创建
答案:使用 {} 直接定义对象属性和方法(ES6+ 支持简洁语法),示例:
// 基础字面量创建
const person = {
name: "张三", // 属性键值对
age: 20,
sayHi: function() { // 方法
console.log(`Hi, ${this.name}`);
}
};
// ES6 简洁语法(方法简写、属性简写)
const name = "李四";
const student = {
name, // 属性简写(键名与变量名一致)
study() { // 方法简写(省略 function)
console.log("学习中");
}
};3. Array.from 的用法
答案:将「类数组对象」或「可迭代对象(Iterable)」转为真正的数组,支持映射和绑定this,语法:Array.from(arrayLike, mapFn?, thisArg?)
- 核心功能:
- 转换类数组(含
length属性的对象,如arguments、DOM 集合) - 转换可迭代对象(字符串、Set、Map 等)
- 第二个参数(可选):映射函数(类似
map)
- 转换类数组(含
- 示例:javascript
// 转换 DOM 集合 const divs = document.querySelectorAll("div"); const divArr = Array.from(divs); // 转为数组 // 转换字符串(每个字符为数组元素) Array.from("abc"); // ['a', 'b', 'c'] // 带映射函数(数组去重+平方) const set = new Set([1, 2, 2, 3]); Array.from(set, x => x * x); // [1, 4, 9]
4. 将数组转化为逗号分隔的字符串
答案:使用数组方法 join(',')(最简洁高效),示例:
const arr = ["苹果", "香蕉", "橙子"];
const str = arr.join(','); // 结果:"苹果,香蕉,橙子"
// 特殊情况:空数组返回空串
[].join(','); // ""5. 为对象添加一个方法
答案:3种常用方式,优先推荐ES6简洁语法:
const obj = {};
// 方式1:字面量创建时直接定义(初始化阶段)
const obj1 = {
eat() { console.log("吃饭"); } // ES6 方法简写
};
// 方式2:后期通过点语法/方括号添加(动态添加)
obj2.say = function() { console.log("说话"); };
obj2["run"] = () => console.log("跑步"); // 方括号支持动态键名
// 方式3:通过 Object.assign 批量添加
Object.assign(obj3, {
jump() { console.log("跳跃"); }
});二、填空题(含核心答案)
1. 初始化一个空对象的方法
答案(至少2种常用方式):
const obj = {};(最简洁,推荐)const obj = new Object();(构造函数方式)const obj = Object.create({});(创建原型为指定对象的空对象)const obj = Object.create(null);(创建无原型的空对象)
2. 连接字符串的符号
答案:
+运算符(基础用法):"a" + "b" + 123→"ab123"- 模板字符串
`(ES6 推荐,支持换行和变量):const name = "张三";你好,${name}
三、大题(详细解析+代码)
1. 事件循环机制(Event Loop)分析
核心结论:JS 是单线程语言,事件循环是解决「异步任务执行顺序」的核心机制,分为「浏览器环境」和「Node 环境」,前端重点关注浏览器环境。
浏览器环境事件循环流程:
- 执行栈(Call Stack):同步代码按顺序进入执行栈,执行完后出栈;
- 异步任务分类:
- 微任务(Microtasks):优先级高,执行栈空后立即执行所有微任务(队列清空才会执行宏任务)
- 常见:
Promise.then/catch/finally、queueMicrotask()、process.nextTick(Node 特有)
- 常见:
- 宏任务(Macrotasks):优先级低,微任务执行完后执行1个宏任务(然后回到微任务检查)
- 常见:
setTimeout/setInterval、DOM 事件(如 click)、AJAX 请求、requestAnimationFrame
- 常见:
- 微任务(Microtasks):优先级高,执行栈空后立即执行所有微任务(队列清空才会执行宏任务)
- 循环逻辑:
- 执行所有同步代码 → 执行栈空 → 执行所有微任务 → 取1个宏任务执行 → 执行栈空 → 重复上述步骤
示例代码分析(理解执行顺序):
console.log("1"); // 同步代码
setTimeout(() => console.log("2"), 0); // 宏任务
Promise.resolve().then(() => console.log("3")); // 微任务
console.log("4"); // 同步代码
// 执行顺序:1 → 4 → 3 → 2
// 解析:
// 1. 同步代码 "1"、"4" 先执行,输出 1、4;
// 2. 执行栈空,执行微任务 Promise.then,输出 3;
// 3. 微任务队列清空,执行1个宏任务 setTimeout,输出 2。2. 编程题:点击背景切换三角形颜色(红→粉)
需求:页面背景可点击,点击后将三角形由红色转为粉色(再次点击可切换回红色,可选优化)。
实现思路:
- HTML:创建背景容器和三角形元素;
- CSS:用
border实现三角形(宽高为0,单边设颜色,其他透明),初始红色; - JS:给背景容器绑定点击事件,切换三角形的颜色(用
classList.toggle或直接修改style)。
完整代码(可直接运行):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>三角形颜色切换</title>
<style>
/* 背景容器:占满屏幕,可点击 */
.bg-container {
width: 100vw;
height: 100vh;
background-color: #f5f5f5;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
/* 三角形:用 border 实现 */
.triangle {
width: 0;
height: 0;
/* 初始红色:下三角(border-bottom 设颜色,其他透明) */
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-bottom: 100px solid red; /* 红色初始值 */
transition: border-color 0.3s ease; /* 过渡动画,优化体验 */
}
/* 粉色状态类 */
.triangle.pink {
border-bottom-color: pink;
}
</style>
</head>
<body>
<div class="bg-container">
<div class="triangle"></div>
</div>
<script>
// 1. 获取 DOM 元素
const bgContainer = document.querySelector('.bg-container');
const triangle = document.querySelector('.triangle');
// 2. 绑定点击事件
bgContainer.addEventListener('click', () => {
// 切换 pink 类:有则移除(红),无则添加(粉)
triangle.classList.toggle('pink');
});
</script>
</body>
</html>代码说明:
- 三角形实现:通过
border透明边框叠加,仅底部边框设颜色,形成下三角; - 颜色切换:用
classList.toggle简化逻辑,点击一次添加pink类(粉色),再点击移除(红色); - 优化点:添加
transition过渡动画,颜色切换更流畅,提升用户体验。
4 复盘反思
考试我个人认为考的不太好,虽然选填的答案应该还可以,但是事件循环机制忘了,最后一道的三角形也没整出来,忘记边框还能这样用了。也没有进行充足的复习(Java快考试了),周一还有个字节的,周五考完试,周末我要好好复习一下,尽最大可能拿下这个(拿不下我还有下家😋😋😋)
另外笔试题全部都是js的题,尤其是es6的部分特别的多,以及常用的API考的都很多,没考css和html
>11.22.25 一面过了😋,周三下午二面(应该没有三面了吧)
磐松资产-前端实习-二面
时间:11/26/25 时长:46min左右
1 流程
- 自我介绍
- 提问
- 反问
2 提问内容
数组去重的方法
promise.allsettled和promise.all的区别❌️
如何设计一个简单的事件总线(发布订阅)❌️
webpack与vite的区别
如何进行Axios配置
对于一刻短剧的滑动5个就重新刷新的设计原因
抖音的视频是提前加载,预处理。点赞和收藏是写死的
一刻短剧的性能优化
视频的浏览记录历史保存(返回视频有历史播放进度)
在项目中有没有使用provide与inject
针对事件监听的节流处理
你是如何确定当前播放的视频是哪一个的
防抖的设置
移动端优先的设计理念❌️
你部署的CI/CD流程
4k大屏,但是只能输出1080p,该如何避免缩放失真❌️
Tailwindcss和普通的css有什么优缺点
介绍一下项目是如何进行Pinia状态管理的
localstorage
主题切换该如何去做
平时开发有使用什么好用的AI吗
有了解过我们公司吗
偏向的公司类型
直属上级和你在代码上有意见,你该如何解决
你喜欢什么样的工作环境
对实习的期待或者希望学习到什么
个人投递方向全是前端吗
什么时间入职
实习多久
3 参考答案
以下是按「技术类」「项目业务类」「个人求职类」分类的面试参考答案,兼顾实操性、逻辑性和面试表达场景,可根据自身实际经验调整细节:
一、技术类问题(突出实操性+核心逻辑)
1. 数组去重的方法(常用3种,按场景选择)
- 基础高效版(推荐):
[...new Set(arr)]或Array.from(new Set(arr)),利用Set不可重复特性,简洁高效,时间复杂度O(n),但无法去重对象数组(仅适用于基本类型)。 - 兼容低版本/精准控制版:
arr.filter((item, index) => arr.indexOf(item) === index),利用indexOf返回首次出现索引,筛选唯一元素,可自定义去重规则(如对象数组可加条件)。 - 性能优化版(大数据量):
arr.reduce((acc, cur) => acc.includes(cur) ? acc : [...acc, cur], []),减少遍历次数,适合需同时处理其他逻辑(如数据转换)的场景。
2. Promise.all 与 Promise.allSettled 的区别
| 特性 | Promise.all | Promise.allSettled |
|---|---|---|
| 状态触发 | 全部Promise resolved才resolved;任一rejected则立即rejected | 所有Promise完成(无论resolve/reject)后才resolved |
| 返回结果 | 成功:所有结果组成的数组;失败:第一个reject的原因 | 每个Promise的结果对象数组(含status和value/reason) |
| 适用场景 | 依赖所有异步结果(如多接口并行获取后合并数据) | 无需依赖所有结果,需知道每个异步的执行状态(如批量请求统计成功/失败数) |
3. 如何设计一个简单的事件总线(发布订阅模式)
核心是「事件中心+订阅/发布/取消订阅」三大功能,基于对象存储事件回调:
class EventBus {
constructor() {
this.events = {}; // 存储事件:{ eventName: [callback1, callback2] }
}
// 订阅事件
on(eventName, callback) {
if (!this.events[eventName]) this.events[eventName] = [];
this.events[eventName].push(callback);
}
// 发布事件(传参)
emit(eventName, ...args) {
const callbacks = this.events[eventName];
if (callbacks) callbacks.forEach(cb => cb(...args));
}
// 取消订阅(单个回调/全部回调)
off(eventName, callback) {
const callbacks = this.events[eventName];
if (!callbacks) return;
if (callback) {
this.events[eventName] = callbacks.filter(cb => cb !== callback);
} else {
delete this.events[eventName]; // 移除整个事件
}
}
}
// 使用:const bus = new EventBus(); bus.on('xxx', ()=>{}); bus.emit('xxx', data);适用场景:非父子组件通信(如Vue2无Pinia时),但需注意避免内存泄漏(组件卸载前off事件)。
4. webpack 与 Vite 的区别(核心差异)
| 维度 | webpack | Vite |
|---|---|---|
| 构建原理 | 基于「打包」:先递归解析所有模块,打包成bundle后再启动服务 | 基于「原生ES模块(ESM)」:开发时不打包,直接按需加载模块,生产环境用Rollup打包 |
| 热更新(HMR) | 需重新打包更新的模块,大型项目热更新较慢 | 只更新修改的模块,无需整体打包,热更新速度极快(毫秒级) |
| 适用场景 | 复杂项目(多入口、复杂依赖、需大量插件定制)、兼容旧浏览器 | 现代前端项目(Vue/React/VitePress等)、追求开发效率、无需兼容IE |
| 启动速度 | 随项目体积增大变慢 | 启动速度快(不依赖打包) |
| 插件生态 | 生态成熟(插件数量多) | 生态快速发展,兼容部分webpack插件(需适配器) |
5. 如何进行 Axios 配置(项目实战版)
核心是「基础配置+拦截器+封装复用」,示例:
// 1. 基础配置(创建实例)
import axios from 'axios';
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL, // 环境变量区分开发/生产
timeout: 5000, // 请求超时
headers: { 'Content-Type': 'application/json' }
});
// 2. 请求拦截器(添加Token、统一参数)
service.interceptors.request.use(
config => {
const token = localStorage.getItem('token');
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
},
error => Promise.reject(error)
);
// 3. 响应拦截器(统一错误处理、数据解构)
service.interceptors.response.use(
response => response.data, // 直接返回响应体,简化组件使用
error => {
// 统一错误提示(如401token过期、500服务器错误)
if (error.response?.status === 401) {
localStorage.removeItem('token');
window.location.href = '/login';
}
ElMessage.error(error.message || '请求失败');
return Promise.reject(error);
}
);
// 4. 封装请求方法(GET/POST)
export const get = (url, params) => service.get(url, { params });
export const post = (url, data) => service.post(url, data);扩展:可添加取消请求、请求重试、缓存请求结果等功能。
6. 节流处理(事件监听场景)
核心逻辑:规定时间内只执行一次函数,避免高频事件(滚动、 resize、输入框搜索)触发过多请求/计算。
// 通用节流函数(立即执行版)
function throttle(fn, delay = 500) {
let lastTime = 0; // 上一次执行时间
return function(...args) {
const now = Date.now();
if (now - lastTime >= delay) { // 间隔超过delay才执行
fn.apply(this, args);
lastTime = now;
}
};
}
// 项目使用(如滚动加载)
window.addEventListener('scroll', throttle(() => {
console.log('滚动触发(500ms一次)');
// 加载更多逻辑...
}, 500));适用场景:滚动加载、按钮防重复点击(短时间内)、resize事件处理。
7. 防抖的设置
核心逻辑:触发事件后延迟n秒执行,若n秒内再次触发则重新计时,避免高频事件重复执行。
// 通用防抖函数(延迟执行版)
function debounce(fn, delay = 500, immediate = false) {
let timer = null;
return function(...args) {
if (timer) clearTimeout(timer); // 重新触发时清除之前的定时器
if (immediate && !timer) { // 立即执行版(首次触发直接执行)
fn.apply(this, args);
}
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay);
};
}
// 项目使用(如搜索框输入联想)
const searchInput = document.querySelector('input');
searchInput.addEventListener('input', debounce((e) => {
console.log('搜索请求:', e.target.value);
// 发送搜索接口...
}, 300));参数说明:immediate: true 适用于按钮提交(避免重复点击且立即响应),false 适用于输入框、搜索联想。
8. Tailwindcss 和普通 CSS 的优缺点
| 维度 | Tailwindcss | 普通CSS(含SCSS/LESS) |
|---|---|---|
| 优点 | 1. 原子化类,开发快速(无需写自定义类);2. 样式统一,减少命名冲突;3. 支持响应式、主题配置(无需写媒体查询);4. 生产环境自动 purge 无用样式,体积小 | 1. 结构清晰(自定义类名语义化);2. 灵活度高(复杂样式易实现);3. 学习成本低(原生语法);4. 适合复杂组件样式复用(如BEM命名) |
| 缺点 | 1. 类名冗长(如 flex justify-center items-center);2. 学习成本(需记原子类);3. 复杂样式需自定义工具类,不如普通CSS直观 | 1. 开发效率低(重复写样式);2. 易产生冗余样式;3. 响应式需写大量媒体查询;4. 多人协作易出现命名冲突 |
| 适用场景 | 快速开发、响应式项目、组件库开发(如UI组件) | 复杂设计稿、需高度自定义样式的项目、团队熟悉原生CSS |
9. 项目中如何进行 Pinia 状态管理
核心是「模块化划分+状态操作规范化」,步骤如下:
- 安装配置:
npm install pinia,在入口文件(如main.js)创建Pinia实例并挂载:javascriptimport { createPinia } from 'pinia'; app.use(createPinia()); - 定义模块(Store):按业务划分(如user、cart、setting),示例:javascript
// src/stores/user.js import { defineStore } from 'pinia'; export const useUserStore = defineStore('user', { state: () => ({ // 状态 token: localStorage.getItem('token') || '', userInfo: {} }), getters: { // 计算属性(缓存) isLogin: (state) => !!state.token }, actions: { // 异步/同步操作(修改状态) setToken(newToken) { this.token = newToken; localStorage.setItem('token', newToken); // 持久化 }, async fetchUserInfo() { // 异步请求 const res = await get('/api/user/info'); this.userInfo = res.data; } } }); - 组件中使用:javascript
import { useUserStore } from '@/stores/user'; const userStore = useUserStore(); // 读取状态 console.log(userStore.token); // 修改状态(直接修改/使用$patch批量修改) userStore.setToken('xxx'); userStore.$patch({ userInfo: { name: 'xxx' } }); // 异步操作 userStore.fetchUserInfo(); - 持久化:简单场景用localStorage/sessionStorage(如上述示例),复杂场景用
pinia-plugin-persistedstate插件。
10. localStorage 相关(特性+使用场景+注意事项)
- 核心特性:
- 存储容量:约5MB;
- 持久化:关闭浏览器后数据不丢失(需手动清除);
- 同源限制:同一协议、域名、端口下可访问;
- 仅存字符串:需用
JSON.stringify()/JSON.parse()处理对象/数组。
- 使用场景:存储不敏感的持久化数据(如用户token、主题配置、浏览历史)。
- 注意事项:
- 不能存储敏感数据(如密码,易被XSS攻击窃取);
- 存储大量数据会影响页面加载速度;
- 跨域无法访问,需跨域共享数据可使用postMessage+localStorage或后端接口。
11. 主题切换该如何实现
核心是「CSS变量+状态管理+持久化」,步骤如下:
- 定义主题变量:在全局CSS中定义不同主题的变量(如light/dark):css
/* src/assets/css/theme.css */ :root { /* 默认light主题 */ --bg-color: #fff; --text-color: #333; --primary-color: #42b983; } .theme-dark { /* dark主题 */ --bg-color: #121212; --text-color: #eee; --primary-color: #66bb6a; } - 在Pinia中管理主题状态:javascript
// src/stores/setting.js export const useSettingStore = defineStore('setting', { state: () => ({ theme: localStorage.getItem('theme') || 'light' // 持久化 }), actions: { toggleTheme() { this.theme = this.theme === 'light' ? 'dark' : 'light'; localStorage.setItem('theme', this.theme); this.applyTheme(); // 应用主题 }, applyTheme() { // 初始化时应用主题 document.documentElement.className = this.theme === 'dark' ? 'theme-dark' : ''; } } }); - 组件中使用:
- 样式中使用CSS变量:
body { background: var(--bg-color); color: var(--text-color); }; - 切换主题:调用
settingStore.toggleTheme()。
- 样式中使用CSS变量:
- 扩展:复杂主题(如多配色)可通过动态加载CSS文件实现。
12. 如何确定当前播放的视频是哪一个
核心是「标识唯一性+状态跟踪」,常用方案:
- 给视频元素绑定唯一ID:
- 每个视频项分配唯一标识(如后端返回的videoId);
- 用Pinia/全局变量存储当前播放的videoId,示例:javascript
// 视频列表渲染 <video v-for="video in videoList" :key="video.id" :data-id="video.id" @play="handlePlay(video.id)" ></video> // 记录当前播放视频ID const currentVideoId = ref(''); const handlePlay = (videoId) => { currentVideoId.value = videoId; // 存储当前播放ID // 暂停其他视频(可选) document.querySelectorAll('video').forEach(v => { if (v.dataset.id !== videoId) v.pause(); }); };
- 基于DOM元素跟踪:通过
document.activeElement判断是否为视频元素,或用querySelector('video:playing')(需结合CSS伪类)。 - 框架场景(如Vue):用v-model或组件通信传递当前播放状态,避免直接操作DOM。
13. 视频浏览记录历史保存(含播放进度)
实现方案:「本地存储+接口同步」,步骤如下:
- 存储结构设计:每条记录包含视频ID、标题、封面、播放进度、播放时间,示例:javascript
// 单条记录格式 { videoId: '123', title: 'xxx', cover: 'xxx.jpg', progress: 30, // 播放进度(秒) lastPlayTime: '2024-05-20 10:30' } - 本地存储(优先):
- 用localStorage存储浏览记录(最多保留N条,超出则删除最早的);
- 视频播放时,每3秒或暂停时更新进度:javascript
// 视频播放事件 const video = document.querySelector('video'); video.ontimeupdate = throttle(() => { const currentProgress = video.currentTime; // 更新本地存储中的进度 updateHistoryProgress(videoId, currentProgress); }, 3000); // 节流,避免频繁存储 video.onpause = () => { updateHistoryProgress(videoId, video.currentTime); // 暂停时强制更新 };
- 接口同步(可选):用户登录后,将本地记录同步到后端,实现多设备同步;未登录时仅存储在本地。
- 返回视频时恢复进度:加载视频时,查询本地/后端的历史进度,设置
video.currentTime = progress。
14. CI/CD 部署流程(前端项目示例)
以「GitHub Actions + 阿里云服务器」为例,核心流程:
- 代码管理:代码托管在GitHub,按分支规范开发(dev开发、main生产)。
- CI(持续集成):
- 触发条件:提交代码到dev/main分支时自动触发;
- 执行步骤:
- 拉取代码;
- 安装依赖(
npm install); - 代码检查(ESLint);
- 构建项目(
npm run build); - 生成构建产物(dist目录)。
- CD(持续部署):
- 开发环境(dev分支):构建成功后,通过SSH将dist目录上传到测试服务器,自动部署;
- 生产环境(main分支):构建成功后,手动确认部署(或自动部署),上传到生产服务器,替换旧版本。
- 配置示例(GitHub Actions):yaml
# .github/workflows/deploy.yml name: Deploy on: push: branches: [dev, main] jobs: build-and-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: '18' - name: Install dependencies run: npm install - name: Build run: npm run build - name: Deploy to server uses: easingthemes/ssh-deploy@main env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} # 服务器私钥(GitHub Secrets存储) ARGS: "-rltgoDzvO --delete" SOURCE: "dist/" # 构建产物目录 REMOTE_HOST: ${{ secrets.REMOTE_HOST }} # 服务器IP REMOTE_USER: ${{ secrets.REMOTE_USER }} # 服务器用户名 TARGET: "/var/www/xxx" # 服务器部署目录 - 扩展:可集成自动化测试(Jest、Cypress),测试通过后再部署。
15. provide 与 inject 使用场景
- 核心作用:Vue中跨层级组件通信(父组件向子孙组件传递数据/方法,无需逐层props传递)。
- 使用方式:javascript
// 父组件(provide提供数据) import { provide } from 'vue'; provide('theme', 'dark'); // 第一个参数是key,第二个是值(可传对象、方法) provide('changeTheme', (newTheme) => { /* 方法 */ }); // 子孙组件(inject接收数据) import { inject } from 'vue'; const theme = inject('theme', 'light'); // 第二个参数是默认值 const changeTheme = inject('changeTheme'); - 项目应用场景:
- 全局配置(如主题、语言、权限);
- 组件库开发(如ElMessageBox、ElNotification,通过provide/inject传递全局配置);
- 注意事项:
- 数据是响应式的(若传递的是响应式对象/refs);
- 避免滥用,跨层级通信优先用Pinia,provide/inject适合简单场景。
16. 移动端优先的设计理念
- 核心定义:先设计移动端界面(小屏幕),再基于大屏设备(平板、PC)进行适配扩展,而非反过来。
- 实现方式:
- 响应式布局:用媒体查询(
@media (min-width: 768px))适配大屏; - 弹性布局:flex/grid布局,适应不同屏幕宽度;
- 相对单位:用rem/vw/vh(如1rem=100px,通过html字体大小适配),避免固定px;
- 触控友好:按钮尺寸≥44px×44px,间距合理,避免密集操作。
- 响应式布局:用媒体查询(
- 优势:
- 适配更精准,避免大屏适配后移动端体验差;
- 聚焦核心功能(移动端屏幕有限,优先保留核心功能);
- 符合移动设备使用场景增长趋势。
17. 4K大屏仅输出1080p,如何避免缩放失真
- 核心思路:让页面按「物理像素」渲染,避免浏览器自动缩放。
- 实现方案:
- 设置meta标签:确保viewport不缩放,适配设备宽度:html
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> - 使用相对单位:用vw/vh(1vw=屏幕宽度的1%)或rem,基于屏幕宽度动态计算元素尺寸,而非固定px。
- 图片/资源适配:使用高分辨率图片(如2x图),通过
image-set自动选择适配图片:css.img { background-image: image-set( url('img-1080p.jpg') 1x, url('img-4k.jpg') 2x ); } - CSS媒体查询适配:针对4K屏幕(如
min-width: 3840px)单独优化布局,避免元素被过度拉伸。 - 框架层面适配:如Vue项目可使用
postcss-px-to-viewport插件,自动将px转换为vw。
- 设置meta标签:确保viewport不缩放,适配设备宽度:
二、项目业务类问题(结合业务场景+技术选型原因)
1. 一刻短剧“滑动5个就重新刷新”的设计原因
核心是「性能优化+用户体验+业务需求」:
- 性能优化:避免一次性加载过多视频(每个视频含视频文件、封面、文案),导致内存占用过高、页面卡顿,滑动5个刷新可分批加载,降低首屏加载压力。
- 用户体验:用户连续滑动5个后,大概率需要新内容,提前刷新可避免后续滑动时出现“空白加载”,保证流畅性。
- 业务需求:短剧内容多为连载/批量更新,分批刷新可优先展示最新/热门内容,同时减少接口请求频率(批量请求5个 vs 每次滑动请求1个)。
- 扩展:可结合“预加载”(滑动到第3个时就请求下一批),进一步提升体验。
2. 抖音视频“提前加载、预处理”,点赞收藏“写死”的设计逻辑
- 提前加载+预处理:
- 原因:视频播放需低延迟,提前加载(如用户当前播放第1个时,预加载第2、3个)可避免播放时缓冲;
- 预处理:对视频进行转码(适配不同网络/设备)、压缩(减少文件大小)、缓存(本地存储近期播放的视频),提升加载速度。
- 点赞收藏“写死”(实际是本地缓存+异步同步):
- 设计逻辑:用户点击点赞/收藏时,先本地更新状态(如点赞按钮立即变红),再异步调用接口同步到后端;
- 优势:避免接口请求延迟导致的“点击后无反应”,提升交互流畅性;即使网络波动,本地状态也能保持,后续网络恢复后自动同步。
3. 一刻短剧的性能优化方案(前端层面)
- 资源优化:
- 视频优化:转码为HLS格式(支持分片加载)、根据网络状况切换清晰度(4G加载720p,WiFi加载1080p);
- 图片优化:封面图使用WebP格式(压缩比高)、懒加载(
loading="lazy")、缩略图预览。
- 渲染优化:
- 列表虚拟化:使用
vue-virtual-scroller等插件,只渲染可视区域内的视频项,避免大量DOM节点导致卡顿; - 减少重绘重排:视频容器尺寸固定,避免滑动时DOM结构变化;使用CSS3硬件加速(
transform: translateZ(0))。
- 列表虚拟化:使用
- 接口优化:
- 批量请求:滑动时批量请求视频列表,减少接口调用次数;
- 缓存策略:本地缓存已请求的视频列表、用户操作(点赞/收藏),避免重复请求;
- 接口防抖:搜索、筛选时添加防抖,避免高频请求。
- 其他优化:
- 懒加载非核心组件:如评论区、分享功能,滑动到对应区域再加载;
- 内存释放:用户离开视频页面时,销毁视频实例、清除定时器,避免内存泄漏。
三、个人求职类问题(真诚+贴合岗位+展现适配性)
1. 有了解过我们公司吗?
参考回答: “了解过。贵公司是[行业领域,如“国内领先的短剧平台”/“专注于前端工程化解决方案的科技公司”],核心业务包括[具体业务,如“一刻短剧APP的开发与运营”/“企业级前端框架定制”],之前关注到贵公司推出的[产品/技术,如“XX短剧系列”/“XX前端组件库”]在行业内口碑很好,技术栈上以[前端技术,如Vue、Vite、Pinia]为主,和我目前的技术方向非常契合。另外,贵公司在[团队文化/行业地位,如“重视技术创新”/“快速发展的创业氛围”]方面的特点,也是我非常认可的,所以很希望能加入团队。”
2. 偏向的公司类型?
参考回答: “我更偏向「技术驱动+重视成长」的公司。一方面,希望公司有清晰的技术栈和工程化规范,能让我在实际项目中积累更专业的开发经验;另一方面,希望团队有良好的技术分享氛围,能和优秀的同事一起交流学习,不断提升自己的技术能力。贵公司的[业务/技术方向]正好符合我的期待,无论是项目复杂度还是技术迭代速度,都能让我快速成长。”
3. 直属上级和你在代码上有意见分歧,你该如何解决?
参考回答: “首先,我会先认真倾听上级的想法,了解他的核心诉求和考虑角度(比如性能、可维护性、业务兼容性),避免因为信息偏差导致分歧。然后,我会客观陈述自己的思路,比如代码的设计逻辑、技术选型的原因、可能带来的优势(如开发效率、后续扩展性),并拿出具体的示例或数据支持(如性能测试结果、类似项目案例)。如果还是无法达成共识,我会建议先小范围验证两种方案的可行性(比如写demo测试),基于实际效果做决策,而不是固执己见。最终的目标是保证代码质量和业务目标的实现,所以我会以理性、开放的态度沟通,尊重团队的最终决定。”
4. 你喜欢什么样的工作环境?
参考回答: “我喜欢「开放协作+高效务实」的工作环境。第一,团队内部沟通顺畅,遇到问题可以随时和同事、上级交流,没有层级壁垒;第二,大家都以结果为导向,注重效率,不搞形式主义;第三,有学习和成长的空间,比如定期的技术分享、项目复盘,能让我不断积累经验。另外,我也很看重工作和生活的平衡,这样才能保持持续的工作热情。”
5. 对实习的期待或者希望学习到什么?
参考回答: “首先,希望能深入参与实际项目的开发,将学校学到的理论知识转化为实操能力,尤其是在[前端技术方向,如Vue框架深入、工程化实践、性能优化]方面,积累真实项目的经验;其次,希望能学习到公司的开发规范和流程,比如代码评审、CI/CD部署、团队协作模式,培养专业的开发习惯;最后,希望能在前辈的指导下,提升解决问题的能力,比如遇到技术难题时的排查思路、业务需求的理解和转化能力。同时,我也会积极贡献自己的力量,认真完成分配的任务,为团队创造价值。”
6. 个人投递方向全是前端吗?
参考回答: “是的,我一直专注于前端开发方向。从学习阶段开始,我就系统学习了HTML、CSS、JavaScript、Vue等核心技术,也做过[个人项目/课程项目,如“博客系统”/“短视频播放器”]等前端相关项目,积累了一定的实操经验。我对前端开发的兴趣很浓厚,尤其是在用户交互体验、工程化优化方面,希望能一直深耕这个领域,所以投递的全部是前端相关岗位。”
7. 什么时间入职?
参考回答: “我目前是[学生身份,如“大三学生”],课程安排比较灵活,只要拿到offer,我可以在[具体时间,如“1-2周内”/“暑假期间”]入职,并且能保证[实习时长,如“3个月以上”]的全职实习时间,期间会合理协调学业和工作,确保不影响实习任务的完成。”
8. 实习多久?
参考回答: “我计划实习[3-6个月],如果期间双方合作愉快,并且学校课程允许,我也非常愿意延长实习时间。我希望能有足够的时间深入参与项目,不仅完成基础任务,还能在后续的工作中承担更多责任,真正学到东西,同时也为团队带来持续的价值。”
9. 平时开发有使用什么好用的AI吗?
参考回答: “我常用的AI工具主要是辅助开发效率的,比如:
- GitHub Copilot:写代码时提供自动补全、语法提示,尤其是复杂逻辑或不熟悉的API,能节省查文档的时间;
- ChatGPT:遇到技术难题(如bug排查、语法疑问)时,会用来辅助分析思路,但不会直接复制代码,而是理解后结合自己的需求修改;
- CodeLlama:偶尔用来优化代码结构、生成单元测试;
- Figma AI:如果涉及简单的UI设计(如项目原型),会用它辅助生成设计稿,提升协作效率。 我认为AI是很好的辅助工具,但核心还是自己的技术能力,所以会合理利用AI提升效率,同时注重理解底层逻辑,避免过度依赖。”
分割线
如果需要针对某个问题的答案进行细化(如补充具体项目案例、调整表达风格),或者想模拟面试场景进行问答演练,欢迎告诉我,我可以帮你进一步优化!
4复盘反思
首先,不要眼睛到处看,不要乱动身体,不要摇头晃脑,看着很不礼貌,端坐看着屏幕。本次的面试说是技术面是最后一次,也就是我的全部面试都已经结束了,我个人感觉很有希望啊!😋😋😋
>11/27/25 不是,都一整天了,怎么还不给我发offer啊?我还不够格吗?