跳到主要内容MetaAPP 前端一面面试题与原理深度解析 | 极客日志JavaScript大前端
MetaAPP 前端一面面试题与原理深度解析
MetaAPP 前端面试真题整理,涵盖项目经验、CSS 响应式布局、JS 语言特性(原型链、包装对象)、Vue 原理(响应式、实例、生命周期、nextTick)及 Promise 用法。文章提供深度解析与代码示例,帮助求职者理解底层逻辑与工程实践,适用于中大厂前端岗位准备。
ServerBase27 浏览 面经原文内容
面试公司:MetaAPP
面试时间:近期(2026-03-03)
面试岗位:前端
面试状态:很有压力,面试官 1 请假叫 2 来面,但问题很有启发
面试问题:
项目相关
- 自我介绍
- 实习拷打 - 看你组件封装那么多,讲一个最牛逼的吧
- 怎么确保你封装组件之后的 api 兼容性?
CSS/动画
4. css 如何做响应式设计移动端 PC 段 iPad 端?
5. rem 的原理是什么
6. 用 css 实现一个抛物线
7. 复杂运动怎么办?
JS 进阶
8. let a=5 经过一系列操作之后,if(a.b=100 && a=5) 是 true,这一系列操作是什么?(面试官举例 number 强转对象然后原型链类似)
9. promise.all 跟 allseted 区别
Vue 原理
10. vue 组件都是一个实例吗?
11. a 组件定时器跳转到 b,a 的会销毁吗?
12. vue3 响应式
13. 副作用函数知道吗?数据改变原理是什么?在源码的数据格式是什么?
14. 为什么使用 weakmap
15. nexttick 是微任务吗?
16. 低版本浏览器还能用吗?
反问
- 业务:游戏业务,都是 h5 跟官网
- 规模:10 个人
MetaAPP 前端一面·深度解析
面试整体画像
| 维度 | 特征 |
|---|
| 公司定位 | MetaAPP - 游戏业务(H5/官网) |
| 面试风格 | 项目深潜型 + 原理追问型 + 边界拓展型 |
| 难度评级 | ⭐⭐⭐(三星,从项目到原理到边界问题) |
| 考察重心 | 组件封装能力、响应式布局、动画实现、JS 语言特性、Vue 源码原理 |
| 特殊之处 | 问题很有启发性,尤其是 number 强转对象的题目考察了对 JS 语言本质的理解 |
逐题深度解析
二、组件封装与 API 兼容性
问题:讲一个最牛逼的组件 + 怎么确保 API 兼容性?
发散性问题,需要平时多做积累,这里我们只复盘一下 API 兼容性设计的内容
<Modal v-model="visible"
title="提示"
width="500"
:before-close="handleClose"
@on-ok="handleOk"
@on-cancel="handleCancel">
<div>内容</>
</>
: {
: {
: ,
: ,
: [, , ].(value)
},
: {
: ,
:
}
}
: {
: {},
: {}
},
: [, ]
div
Modal
<Button type="primary" :loading="loading" @click="handleClick" custom-prop="value">
按钮
</Button>
<button v-bind="$attrs" class="buttonClass">
<slot />
</button>
props
size
type
String
default
'medium'
validator
(value) =>
'small'
'medium'
'large'
includes
type
type
String
default
'default'
props
modelValue
value
emits
'update:modelValue'
'input'
三、响应式设计
问题:css 如何做响应式设计移动端 PC 段 iPad 端?
@media(max-width: 767px){
.container{
width: 100%;
padding: 10px;
}
}
@media(min-width: 768px) and(max-width: 1024px){
.container{
width: 750px;
margin: 0 auto;
}
}
@media(min-width: 1025px){
.container{
width: 1200px;
margin: 0 auto;
}
}
.container{
display: flex;
flex-wrap: wrap;
}
.item{
flex: 1 1 300px;
}
.grid{
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 20px;
}
.col-4{
grid-column: span 4;
}
.hero{
height: 100vh;
width: 100vw;
font-size: clamp(16px, 4vw, 24px);
}
img{
max-width: 100%;
height: auto;
}
picture{
display: block;
}
source{
media: "(max-width: 767px)"
}
@container(max-width: 500px){
.card{
flex-direction: column;
}
}
四、rem 原理
问题:rem 的原理是什么
html {
font-size: 16px;
}
.box {
width: 10rem;
height: 5rem;
}
(function flexible(){
function setRem(){
const width = document.documentElement.clientWidth
const rem = width / 7.5
document.documentElement.style.fontSize = rem + 'px'
}
setRem()
window.addEventListener('resize', setRem)
})()
module.exports = {
plugins: {
'postcss-pxtorem': {
rootValue: 75,
propList: ['*']
}
}
}
五、CSS 实现抛物线
问题:用 css 实现一个抛物线
.ball{
width: 50px;
height: 50px;
background: red;
border-radius: 50%;
animation: parabola 2s ease-out forwards;
}
@keyframes parabola{
0%{
transform: translate(0, 0);
}
100%{
transform: translate(300px, 200px);
}
}
@keyframes parabola-bezier{
0%{
transform: translate(0, 0);
}
100%{
transform: translate(300px, 200px);
}
}
.ball-bezier{
animation: parabola-bezier 2s cubic-bezier(0.2, 0.8, 0.4, 1) forwards;
}
.ball-combo{
animation: horizontal 2s linear forwards, vertical 2s ease-in forwards;
}
@keyframes horizontal{
to{
transform: translateX(300px);
}
}
@keyframes vertical{
to{
transform: translateY(200px);
}
}
function parabola(element, start, end, duration){
const startTime = performance.now()
function animate(currentTime){
const elapsed = currentTime - startTime
const progress = Math.min(elapsed / duration, 1)
const x = start.x + (end.x - start.x) * progress
const y = start.y + (end.y - start.y) * Math.pow(progress, 2)
element.style.transform = `translate(${x}px, ${y}px)`
if (progress < 1){
requestAnimationFrame(animate)
}
}
requestAnimationFrame(animate)
}
六、复杂运动处理
问题:复杂运动怎么办?
const element = document.querySelector('.ball')
const animation = element.animate([
{transform:'translate(0, 0)'},
{transform:'translate(300px, 200px)'}
],{
duration:2000,
easing:'cubic-bezier(0.2, 0.8, 0.4, 1)',
iterations:1
})
animation.onfinish=()=>{
console.log('动画完成')
}
gsap.to('.ball',{
x:300,
y:200,
duration:2,
ease:'power2.out',
onUpdate:()=>{
},
onComplete:()=>{
console.log('完成')
}
})
class Ball{
constructor(x, y){
this.x = x
this.y = y
this.vx = 5
this.vy = -10
this.gravity = 0.5
}
update(){
this.x += this.vx
this.y += this.vy
this.vy += this.gravity
if(this.y > canvas.height - 50){
this.y = canvas.height - 50
this.vy *= -0.7
}
}
draw(ctx){
ctx.beginPath()
ctx.arc(this.x, this.y, 25, 0, Math.PI*2)
ctx.fillStyle = 'red'
ctx.fill()
}
}
<motion.div
animate={{x:300,y:200,rotate:360,scale:[1,2,1]}}
transition={{duration:2,ease:"easeInOut",times:[0,0.5,1],loop:Infinity}}
/>
七、JS 语言特性(第 8 题)
问题:let a=5,如何使 if(a.b=100 && a=5) 为 true?
let a = 5
console.log(a.toString())
Object.defineProperty(Number.prototype,'b',{
set(value){
console.log('设置 b 为', value)
},
get(){
return 100
}
})
let a = 5
Number.prototype.b = 100
let a = 5
console.log(a.b)
if(a.b =100&& a ===5){
console.log('条件成立')
}
八、Promise.all vs allSettled
问题:promise.all 跟 allseted 区别
const promises = [
fetch('/api/user'),
fetch('/api/posts'),
fetch('/api/comments')
]
try{
const [user, posts, comments] = await Promise.all(promises)
}catch(error){
}
const results = await Promise.allSettled(promises)
results.forEach((result, index)=>{
if(result.status ==='fulfilled'){
console.log(`请求${index}成功:`, result.value)
}else{
console.log(`请求${index}失败:`, result.reason)
}
})
九、Vue 组件实例
问题:vue 组件都是一个实例吗?
export default {
mounted(){
console.log(this._uid)
console.log(this===this.$root)
}
}
const ComponentClass = Vue.extend({
data(){
return{
count:0
}
}
})
const instance1 = new ComponentClass()
const instance2 = new ComponentClass()
十、组件销毁与定时器
问题:a 组件定时器跳转到 b,a 的会销毁吗?
export default {
data(){
return{
timer:null
}
},
mounted(){
this.timer = setInterval(()=>{
console.log('A 组件定时器执行')
},1000)
},
beforeDestroy(){
if(this.timer){
clearInterval(this.timer)
console.log('定时器已清理')
}
},
destroyed(){
console.log('A 组件已销毁')
}
}
export default {
mounted(){
this.timer = setInterval(this.tick,1000)
},
beforeDestroy(){
clearInterval(this.timer)
},
mounted(){
const timer = setInterval(this.tick,1000)
this.$once('hook:beforeDestroy',()=>{
clearInterval(timer)
})
},
deactivated(){
clearInterval(this.timer)
},
activated(){
this.timer = setInterval(this.tick,1000)
}
}
十一、Vue3 响应式
问题:vue3 响应式 + 副作用函数 + 数据格式 + WeakMap
let activeEffect
function effect(fn){
activeEffect = fn
fn()
activeEffect = null
}
const targetMap = new WeakMap()
function track(target, key){
if(!activeEffect) return
let depsMap = targetMap.get(target)
if(!depsMap){
targetMap.set(target,(depsMap = new Map()))
}
let dep = depsMap.get(key)
if(!dep){
depsMap.set(key,(dep = new Set()))
}
dep.add(activeEffect)
}
function trigger(target, key){
const depsMap = targetMap.get(target)
if(!depsMap) return
const effects = depsMap.get(key)
effects && effects.forEach(fn=>fn())
}
function reactive(target){
return new Proxy(target,{
get(target, key, receiver){
const value = Reflect.get(target, key, receiver)
track(target, key)
return value
},
set(target, key, value, receiver){
const oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)
if(oldValue !== value){
trigger(target, key)
}
return result
}
})
}
十二、nextTick
问题:nexttick 是微任务吗?
let callbacks = []
let pending = false
function flushCallbacks(){
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
copies.forEach(cb=>cb())
}
let timerFunc
if(typeof Promise !=='undefined'){
timerFunc=()=>{
Promise.resolve().then(flushCallbacks)
}
}else if(typeof MutationObserver !=='undefined'){
let counter = 1
const observer = new MutationObserver(flushCallbacks)
const textNode = document.createTextNode(String(counter))
observer.observe(textNode,{characterData:true})
timerFunc=()=>{
counter = (counter + 1)%2
textNode.data = String(counter)
}
}else if(typeof setImmediate !=='undefined'){
timerFunc=()=>{
setImmediate(flushCallbacks)
}
}else{
timerFunc=()=>{
setTimeout(flushCallbacks,0)
}
}
export function nextTick(cb, ctx){
callbacks.push(()=>{
if(cb){
cb.call(ctx)
}
})
if(!pending){
pending = true
timerFunc()
}
}
this.message = 'updated'
this.$nextTick(()=>{
console.log('DOM 已更新')
})
十三、低版本浏览器兼容
问题:低版本浏览器还能用吗?
module.exports = {
presets: [
['@babel/preset-env',{
targets:{ie:'11'},
useBuiltIns:'usage',
corejs:3
}]
]
}
if(isIEBrowser()){
import('./vue2-app.js')
}else{
import('./vue3-app.js')
}
面试复盘
面试特点总结
| 类型 | 问题 | 考察点 |
|---|
| 项目深挖 | 组件封装、API 兼容性 | 工程实践能力 |
| CSS 基础 | 响应式、rem、抛物线 | 布局和动画能力 |
| JS 语言 | 包装对象、原型链 | 语言本质理解 |
| Promise | all/allSettled | 异步编程能力 |
| Vue 原理 | 响应式、nextTick、WeakMap | 源码理解深度 |
业务方向
知识点速查表
| 知识点 | 核心要点 |
|---|
| 组件封装 | 属性透传、默认值、版本兼容 |
| 响应式设计 | 媒体查询、弹性布局、栅格、视口单位 |
| rem 原理 | 根元素字体、动态适配、px 转 rem |
| 抛物线 | 组合动画、贝塞尔曲线、JS 计算 |
| 包装对象 | 基本类型属性访问、原型链修改 |
| Promise | all(全成功)、allSettled(全结果) |
| Vue 实例 | 每个使用创建一个实例、_uid 标识 |
| 组件销毁 | beforeDestroy 清理定时器、hook 事件 |
| 响应式原理 | WeakMap、Map、Set 存储依赖 |
| nextTick | 微任务优先、降级策略 |
| 兼容性 | Vue3 不支持 IE、polyfill、动态降级 |
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online