金九银十,focus前端高频面试考点

124 阅读3分钟

前言

最近在准备前端面试的过程中,我整理了一系列高频面试题,发现很多问题都围绕着现代前端框架的底层原理展开。今天就来深度解析这些"既常见又致命"的面试题,帮你打通前端框架的任督二脉!

一、Vue2到Vue3的变革:不只是语法糖

1.1 响应式系统的重写

Vue2使用Object.defineProperty实现响应式,而Vue3改用Proxy

// Vue2 响应式原理
const data = { count: 0 }
Object.defineProperty(data, 'count', {
  get() {
    console.log('获取值')
    return value
  },
  set(newVal) {
    console.log('设置值')
    value = newVal
  }
})

// Vue3 响应式原理
const data = { count: 0 }
const proxy = new Proxy(data, {
  get(target, key) {
    console.log('获取值', key)
    return target[key]
  },
  set(target, key, value) {
    console.log('设置值', key, value)
    target[key] = value
    return true
  }
})

优势对比:

  • Proxy可以直接监听对象而非属性
  • 可以监听数组变化而不需要重写方法
  • 支持Map、Set等数据结构

1.2 Composition API 的引入

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double: {{ double }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
import { ref, computed, onMounted } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const double = computed(() => count.value * 2)
    
    function increment() {
      count.value++
    }
    
    onMounted(() => {
      console.log('组件挂载')
    })
    
    return {
      count,
      double,
      increment
    }
  }
}
</script>

二、JavaScript基础深度拷问

2.1 变量声明与提升

var a = 1
var a = () => {}
console.log(a) // 打印什么?

答案:  打印的是函数 () => {}

原理分析:

  1. 变量提升:var a 被提升到作用域顶部
  2. 重复声明:var 允许重复声明,后面的覆盖前面的
  3. 执行顺序:先赋值 a = 1,然后被重新赋值为函数

扩展思考:

// 如果用 let 会怎样?
let a = 1
let a = () => {} // SyntaxError: Identifier 'a' has already been declared

2.2 事件循环与异步编程

console.log('1')

setTimeout(() => {
  console.log('2')
  Promise.resolve().then(() => {
    console.log('3')
  })
}, 0)

Promise.resolve().then(() => {
  console.log('4')
  setTimeout(() => {
    console.log('5')
  }, 0)
})

console.log('6')

// 输出顺序:1 -> 6 -> 4 -> 2 -> 3 -> 5

三、构建工具的革命:Webpack vs Vite

3.1 Webpack的打包机制

// webpack 需要先打包再服务
module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  devServer: {
    contentBase: './dist'
  }
}

3.2 Vite的ESM原生加载

// vite.config.js
export default {
  server: {
    port: 3000
  },
  build: {
    rollupOptions: {
      // 构建配置
    }
  }
}

Vite快的根本原因:

  1. 基于ES Modules:浏览器直接解析import,无需打包
  2. 按需编译:只编译当前页面需要的文件
  3. Esbuild预构建:使用Go编写的Esbuild,比JS打包器快10-100倍

四、React核心机制解析

4.1 setState的同步与异步

class Example extends React.Component {
  state = { count: 0 }
  
  handleClick = () => {
    // 异步情况
    this.setState({ count: this.state.count + 1 })
    console.log(this.state.count) // 0
    
    // 同步情况
    setTimeout(() => {
      this.setState({ count: this.state.count + 1 })
      console.log(this.state.count) // 2
    }, 0)
  }
}

setState工作流程:

  1. 将setState调用的更新放入队列
  2. 合并多次setState调用
  3. 在合适的时机批量执行更新

4.2 Hooks的设计哲学

// 自定义Hook
function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue)
  
  const increment = useCallback(() => setCount(c => c + 1), [])
  const decrement = useCallback(() => setCount(c => c - 1), [])
  const reset = useCallback(() => setCount(initialValue), [initialValue])
  
  return { count, increment, decrement, reset }
}

// 使用
function Counter() {
  const { count, increment } = useCounter(0)
  return <button onClick={increment}>{count}</button>
}

4.3 虚拟DOM与Diff算法优化

// 虚拟DOM结构
const vnode = {
  type: 'div',
  props: {
    className: 'container',
    children: [
      {
        type: 'span',
        props: { children: 'Hello' }
      }
    ]
  }
}

// Diff算法优化策略
1. 同层比较:只比较同一层次的节点
2. Key优化:通过key复用节点
3. 组件类型判断:类型不同直接替换

4.4 Fiber架构与优先级调度

// Fiber节点结构
const fiberNode = {
  type: Function | Class | String,
  key: null | String,
  stateNode: HTMLElement | Component,
  child: Fiber | null,
  sibling: Fiber | null,
  return: Fiber | null,
  pendingProps: Object,
  memoizedProps: Object,
  updateQueue: UpdateQueue,
  // ... 其他属性
}

// 优先级定义
const PriorityLevels = {
  ImmediatePriority: 1,    // 最高优先级
  UserBlockingPriority: 2,
  NormalPriority: 3,
  LowPriority: 4,
  IdlePriority: 5,         // 最低优先级
}

Fiber调度流程:

  1. 将任务分解为多个工作单元
  2. 为每个工作单元分配优先级
  3. 使用requestIdleCallback在浏览器空闲时执行低优先级任务
  4. 高优先级任务可以打断低优先级任务

五、现代Web技术:流式输出与SSE

5.1 Server-Sent Events (SSE)

// 服务端
app.get('/stream', (req, res) => {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
  })
  
  let count = 0
  const timer = setInterval(() => {
    res.write(`data: ${JSON.stringify({ count: count++ })}\n\n`)
    
    if (count >= 10) {
      clearInterval(timer)
      res.write('data: [DONE]\n\n')
      res.end()
    }
  }, 1000)
  
  req.on('close', () => {
    clearInterval(timer)
  })
})

// 客户端
const eventSource = new EventSource('/stream')
eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data)
  if (data === '[DONE]') {
    eventSource.close()
  } else {
    console.log('Received:', data)
  }
}

六、原型与原型链的终极理解

function Person(name) {
  this.name = name
}

Person.prototype.sayHello = function() {
  console.log(`Hello, I'm ${this.name}`)
}

const john = new Person('John')

// 原型链
john.__proto__ === Person.prototype // true
Person.prototype.__proto__ === Object.prototype // true
Object.prototype.__proto__ === null // true

// 继承
function Student(name, grade) {
  Person.call(this, name)
  this.grade = grade
}

Student.prototype = Object.create(Person.prototype)
Student.prototype.constructor = Student