2025年前端高频面试题全集

687 阅读17分钟

2025年前端高频面试题全集

目录

  1. [JavaScript基础]
  2. [ES6+新特性]
  3. [Vue框架]
  4. [React框架]
  5. [前端工程化]
  6. [性能优化]
  7. [浏览器原理]
  8. [网络与安全]
  9. [算法与数据结构]
  10. [项目实战]

JavaScript基础

1. 说说JavaScript的数据类型及其判断方法

答案

基本数据类型(7种)

  • Number
  • String
  • Boolean
  • Undefined
  • Null
  • Symbol(ES6)
  • BigInt(ES2020)

引用数据类型

  • Object(包括Array、Function、Date、RegExp等)

判断方法

// 1. typeof - 适用于基本类型(除null)
typeof 123 // 'number'
typeof 'abc' // 'string'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof Symbol() // 'symbol'
typeof 123n // 'bigint'
typeof null // 'object' (历史遗留bug)
typeof {} // 'object'
typeof [] // 'object'
typeof function(){} // 'function'

// 2. instanceof - 判断引用类型
[] instanceof Array // true
{} instanceof Object // true
function(){} instanceof Function // true

// 3. Object.prototype.toString.call() - 最准确
Object.prototype.toString.call(123) // '[object Number]'
Object.prototype.toString.call([]) // '[object Array]'
Object.prototype.toString.call(null) // '[object Null]'
Object.prototype.toString.call(undefined) // '[object Undefined]'

// 4. Array.isArray() - 专门判断数组
Array.isArray([]) // true
Array.isArray({}) // false

2. 闭包是什么?有什么作用和应用场景?

答案

定义:闭包是指有权访问另一个函数作用域中变量的函数。

形成条件

  • 函数嵌套
  • 内部函数引用外部函数的变量
  • 内部函数被返回或以某种方式被外部引用

示例

function outer() {
  let count = 0
  return function inner() {
    count++
    console.log(count)
  }
}

const counter = outer()
counter() // 1
counter() // 2
counter() // 3

作用

  1. 数据私有化/封装
  2. 保持变量不被回收
  3. 实现模块化

应用场景

// 1. 防抖函数
function debounce(fn, delay) {
  let timer = null
  return function(...args) {
    clearTimeout(timer)
    timer = setTimeout(() => {
      fn.apply(this, args)
    }, delay)
  }
}

// 2. 节流函数
function throttle(fn, delay) {
  let lastTime = 0
  return function(...args) {
    const now = Date.now()
    if (now - lastTime >= delay) {
      fn.apply(this, args)
      lastTime = now
    }
  }
}

// 3. 单例模式
const Singleton = (function() {
  let instance = null
  return function(name) {
    if (instance) return instance
    this.name = name
    instance = this
  }
})()

// 4. 缓存函数
function memoize(fn) {
  const cache = {}
  return function(...args) {
    const key = JSON.stringify(args)
    if (cache[key]) return cache[key]
    const result = fn.apply(this, args)
    cache[key] = result
    return result
  }
}

注意事项

  • 可能导致内存泄漏
  • 及时清理不需要的闭包

3. 说说原型和原型链

答案

原型(prototype)

  • 每个函数都有一个prototype属性,指向原型对象
  • 原型对象包含所有实例共享的属性和方法

原型链

  • 每个对象都有__proto__属性,指向其构造函数的prototype
  • 通过__proto__形成的链式结构就是原型链
  • 原型链的顶端是Object.prototype,再往上是null
function Person(name) {
  this.name = name
}

Person.prototype.sayName = function() {
  console.log(this.name)
}

const person = new Person('张三')

// 原型关系
person.__proto__ === Person.prototype // true
Person.prototype.__proto__ === Object.prototype // true
Object.prototype.__proto__ === null // true

// 原型链查找
person.sayName() // 先找person自身,没有就找Person.prototype
person.toString() // 继续往上找Object.prototype

继承实现

// ES5 寄生组合式继承
function Parent(name) {
  this.name = name
  this.colors = ['red', 'blue']
}

Parent.prototype.sayName = function() {
  console.log(this.name)
}

function Child(name, age) {
  Parent.call(this, name) // 继承属性
  this.age = age
}

Child.prototype = Object.create(Parent.prototype) // 继承方法
Child.prototype.constructor = Child

// ES6 Class继承
class Parent {
  constructor(name) {
    this.name = name
  }
  
  sayName() {
    console.log(this.name)
  }
}

class Child extends Parent {
  constructor(name, age) {
    super(name)
    this.age = age
  }
}

4. this的指向问题

答案

this的四种绑定规则

// 1. 默认绑定 - 独立函数调用
function foo() {
  console.log(this) // 非严格模式: window,严格模式: undefined
}
foo()

// 2. 隐式绑定 - 对象方法调用
const obj = {
  name: '张三',
  sayName() {
    console.log(this.name) // this指向obj
  }
}
obj.sayName()

// 隐式绑定丢失
const fn = obj.sayName
fn() // this指向window

// 3. 显式绑定 - call/apply/bind
function greet() {
  console.log(this.name)
}
const person = { name: '李四' }
greet.call(person) // this指向person
greet.apply(person)
const boundGreet = greet.bind(person)
boundGreet()

// 4. new绑定 - 构造函数
function Person(name) {
  this.name = name
}
const p = new Person('王五') // this指向新创建的对象

// 5. 箭头函数 - 继承外层作用域的this
const obj2 = {
  name: '赵六',
  sayName: () => {
    console.log(this.name) // this指向外层作用域
  },
  sayName2() {
    setTimeout(() => {
      console.log(this.name) // this指向obj2
    }, 100)
  }
}

优先级:new绑定 > 显式绑定 > 隐式绑定 > 默认绑定

5. 事件循环(Event Loop)机制

答案

概念

  • JavaScript是单线程语言,通过事件循环实现异步
  • 事件循环包括:主线程、宏任务队列、微任务队列

执行顺序

  1. 执行同步代码(宏任务)
  2. 执行所有微任务
  3. 渲染页面(如需要)
  4. 取出下一个宏任务,重复步骤1-3
console.log('1') // 同步代码

setTimeout(() => {
  console.log('2') // 宏任务
}, 0)

Promise.resolve().then(() => {
  console.log('3') // 微任务
}).then(() => {
  console.log('4') // 微任务
})

console.log('5') // 同步代码

// 输出顺序: 1 5 3 4 2

宏任务(MacroTask)

  • setTimeout
  • setInterval
  • setImmediate(Node.js)
  • I/O操作
  • UI渲染

微任务(MicroTask)

  • Promise.then/catch/finally
  • MutationObserver
  • process.nextTick(Node.js)

复杂示例

async function async1() {
  console.log('async1 start')
  await async2()
  console.log('async1 end')
}

async function async2() {
  console.log('async2')
}

console.log('script start')

setTimeout(() => {
  console.log('setTimeout')
}, 0)

async1()

new Promise(resolve => {
  console.log('promise1')
  resolve()
}).then(() => {
  console.log('promise2')
})

console.log('script end')

// 输出顺序:
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout

ES6+新特性

1. let、const和var的区别

答案

特性varletconst
作用域函数作用域块级作用域块级作用域
变量提升有(undefined)有(暂时性死区)有(暂时性死区)
重复声明允许不允许不允许
重新赋值允许允许不允许
全局对象属性
// 1. 作用域
{
  var a = 1
  let b = 2
  const c = 3
}
console.log(a) // 1
console.log(b) // ReferenceError
console.log(c) // ReferenceError

// 2. 变量提升
console.log(x) // undefined
var x = 1

console.log(y) // ReferenceError(暂时性死区)
let y = 2

// 3. 重复声明
var m = 1
var m = 2 // 允许

let n = 1
let n = 2 // SyntaxError

// 4. const常量
const obj = { a: 1 }
obj.a = 2 // 允许(修改对象属性)
obj = {} // TypeError(重新赋值)

2. 箭头函数与普通函数的区别

答案

// 1. this指向
const obj = {
  name: '张三',
  // 普通函数:this指向调用者
  sayName1: function() {
    console.log(this.name)
  },
  // 箭头函数:this继承外层作用域
  sayName2: () => {
    console.log(this.name) // undefined
  }
}

// 2. 没有arguments
function fn1() {
  console.log(arguments) // [1, 2, 3]
}
fn1(1, 2, 3)

const fn2 = () => {
  console.log(arguments) // ReferenceError
}

// 可以用剩余参数替代
const fn3 = (...args) => {
  console.log(args) // [1, 2, 3]
}

// 3. 不能作为构造函数
function Person1(name) {
  this.name = name
}
const p1 = new Person1('张三') // 正常

const Person2 = (name) => {
  this.name = name
}
const p2 = new Person2('李四') // TypeError

// 4. 没有prototype
console.log(Person1.prototype) // {constructor: ƒ}
console.log(Person2.prototype) // undefined

// 5. 不能用作Generator函数
function* generator() {
  yield 1
}
// 箭头函数不能使用yield

3. Promise原理及手写实现

答案

基本用法

const promise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    resolve('成功')
    // reject('失败')
  }, 1000)
})

promise
  .then(result => console.log(result))
  .catch(error => console.log(error))
  .finally(() => console.log('完成'))

手写Promise

class MyPromise {
  constructor(executor) {
    this.state = 'pending' // pending, fulfilled, rejected
    this.value = undefined
    this.reason = undefined
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []
    
    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled'
        this.value = value
        this.onFulfilledCallbacks.forEach(fn => fn())
      }
    }
    
    const reject = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.reason = reason
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }
    
    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }
  
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
    
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.state === 'fulfilled') {
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value)
            resolve(x)
          } catch (error) {
            reject(error)
          }
        })
      }
      
      if (this.state === 'rejected') {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason)
            resolve(x)
          } catch (error) {
            reject(error)
          }
        })
      }
      
      if (this.state === 'pending') {
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value)
              resolve(x)
            } catch (error) {
              reject(error)
            }
          })
        })
        
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason)
              resolve(x)
            } catch (error) {
              reject(error)
            }
          })
        })
      }
    })
    
    return promise2
  }
  
  catch(onRejected) {
    return this.then(null, onRejected)
  }
  
  finally(callback) {
    return this.then(
      value => MyPromise.resolve(callback()).then(() => value),
      reason => MyPromise.resolve(callback()).then(() => { throw reason })
    )
  }
  
  static resolve(value) {
    if (value instanceof MyPromise) return value
    return new MyPromise(resolve => resolve(value))
  }
  
  static reject(reason) {
    return new MyPromise((resolve, reject) => reject(reason))
  }
  
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      const results = []
      let count = 0
      
      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          value => {
            results[index] = value
            count++
            if (count === promises.length) {
              resolve(results)
            }
          },
          reason => reject(reason)
        )
      })
    })
  }
  
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      promises.forEach(promise => {
        MyPromise.resolve(promise).then(resolve, reject)
      })
    })
  }
}

4. async/await原理

答案

基本用法

async function fetchData() {
  try {
    const data1 = await fetch('/api/data1')
    const data2 = await fetch('/api/data2')
    return { data1, data2 }
  } catch (error) {
    console.error(error)
  }
}

实现原理(基于Generator + 自动执行器):

// async/await是Generator的语法糖
function* generatorFunc() {
  const data1 = yield fetch('/api/data1')
  const data2 = yield fetch('/api/data2')
  return { data1, data2 }
}

// 自动执行器
function asyncToGenerator(generatorFunc) {
  return function() {
    const gen = generatorFunc.apply(this, arguments)
    
    return new Promise((resolve, reject) => {
      function step(key, arg) {
        let result
        try {
          result = gen[key](arg)
        } catch (error) {
          return reject(error)
        }
        
        const { value, done } = result
        
        if (done) {
          return resolve(value)
        } else {
          return Promise.resolve(value).then(
            val => step('next', val),
            err => step('throw', err)
          )
        }
      }
      
      step('next')
    })
  }
}

// 使用
const asyncFunc = asyncToGenerator(generatorFunc)
asyncFunc().then(result => console.log(result))

错误处理

// 1. try-catch
async function getData() {
  try {
    const data = await fetch('/api/data')
    return data
  } catch (error) {
    console.error(error)
  }
}

// 2. Promise.catch
async function getData2() {
  const data = await fetch('/api/data').catch(error => {
    console.error(error)
    return null
  })
  return data
}

// 3. 统一错误处理
function to(promise) {
  return promise
    .then(data => [null, data])
    .catch(err => [err, null])
}

async function getData3() {
  const [err, data] = await to(fetch('/api/data'))
  if (err) {
    console.error(err)
    return
  }
  return data
}

5. 解构赋值和扩展运算符

答案

// 1. 数组解构
const arr = [1, 2, 3, 4, 5]
const [a, b, ...rest] = arr
console.log(a, b, rest) // 1 2 [3, 4, 5]

// 默认值
const [x = 0, y = 0] = [1]
console.log(x, y) // 1 0

// 交换变量
let m = 1, n = 2;
[m, n] = [n, m]

// 2. 对象解构
const obj = { name: '张三', age: 20, city: '北京' }
const { name, age, ...others } = obj
console.log(name, age, others) // 张三 20 {city: '北京'}

// 重命名
const { name: username } = obj
console.log(username) // 张三

// 默认值
const { gender = '男' } = obj
console.log(gender) // 男

// 嵌套解构
const user = {
  info: {
    name: '李四',
    address: {
      city: '上海'
    }
  }
}
const { info: { name, address: { city } } } = user

// 3. 扩展运算符
// 数组
const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
const merged = [...arr1, ...arr2]

// 对象
const obj1 = { a: 1, b: 2 }
const obj2 = { b: 3, c: 4 }
const merged2 = { ...obj1, ...obj2 } // {a: 1, b: 3, c: 4}

// 函数参数
function sum(...numbers) {
  return numbers.reduce((acc, cur) => acc + cur, 0)
}
sum(1, 2, 3, 4) // 10

// 浅拷贝
const original = { a: 1, b: { c: 2 } }
const copy = { ...original }
copy.b.c = 3 // 会影响original

Vue框架

1. Vue2和Vue3的区别

答案

特性Vue2Vue3
响应式原理Object.definePropertyProxy
API风格Options APIComposition API
性能较慢更快(重写虚拟DOM)
TypeScript支持较弱完全支持
包大小较大更小(Tree-shaking)
生命周期beforeCreate/created等setup
多根节点不支持支持
Teleport不支持支持
Suspense不支持支持

核心区别示例

// Vue2 Options API
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  },
  mounted() {
    console.log('mounted')
  }
}

// Vue3 Composition API
import { ref, onMounted } from 'vue'

export default {
  setup() {
    const count = ref(0)
    
    const increment = () => {
      count.value++
    }
    
    onMounted(() => {
      console.log('mounted')
    })
    
    return {
      count,
      increment
    }
  }
}

// Vue3 script setup(更简洁)
<script setup>
import { ref, onMounted } from 'vue'

const count = ref(0)

const increment = () => {
  count.value++
}

onMounted(() => {
  console.log('mounted')
})
</script>

2. Vue响应式原理

答案

Vue2响应式原理

// 基于Object.defineProperty实现
function defineReactive(obj, key, val) {
  const dep = new Dep() // 依赖收集器
  
  Object.defineProperty(obj, key, {
    get() {
      // 依赖收集
      if (Dep.target) {
        dep.depend()
      }
      return val
    },
    set(newVal) {
      if (newVal === val) return
      val = newVal
      // 通知更新
      dep.notify()
    }
  })
}

class Dep {
  constructor() {
    this.subs = []
  }
  
  depend() {
    if (Dep.target) {
      this.subs.push(Dep.target)
    }
  }
  
  notify() {
    this.subs.forEach(watcher => watcher.update())
  }
}

class Watcher {
  constructor(vm, exp, cb) {
    this.vm = vm
    this.exp = exp
    this.cb = cb
    this.value = this.get()
  }
  
  get() {
    Dep.target = this
    const value = this.vm[this.exp]
    Dep.target = null
    return value
  }
  
  update() {
    const newValue = this.vm[this.exp]
    this.cb.call(this.vm, newValue, this.value)
    this.value = newValue
  }
}

Vue3响应式原理

// 基于Proxy实现
function reactive(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      const result = Reflect.get(target, key, receiver)
      // 依赖收集
      track(target, key)
      return result
    },
    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
    }
  })
}

const targetMap = new WeakMap()
let activeEffect = null

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 dep = depsMap.get(key)
  if (dep) {
    dep.forEach(effect => effect())
  }
}

function effect(fn) {
  activeEffect = fn
  fn()
  activeEffect = null
}

3. computed和watch的区别

答案

// computed - 计算属性
// 特点:缓存、依赖追踪、同步
export default {
  data() {
    return {
      firstName: '张',
      lastName: '三'
    }
  },
  computed: {
    // 有缓存,只有依赖变化才重新计算
    fullName() {
      return this.firstName + this.lastName
    },
    // 可设置getter和setter
    fullName2: {
      get() {
        return this.firstName + this.lastName
      },
      set(val) {
        const names = val.split(' ')
        this.firstName = names[0]
        this.lastName = names[1]
      }
    }
  }
}

// watch - 侦听器
// 特点:无缓存、可异步、可配置
export default {
  data() {
    return {
      question: '',
      answer: ''
    }
  },
  watch: {
    // 简单监听
    question(newVal, oldVal) {
      this.getAnswer()
    },
    // 深度监听
    obj: {
      handler(newVal, oldVal) {
        console.log('obj changed')
      },
      deep: true,
      immediate: true // 立即执行
    },
    // 监听对象属性
    'obj.name'(newVal) {
      console.log(newVal)
    }
  },
  methods: {
    async getAnswer() {
      this.answer = await fetch('/api/answer')
    }
  }
}

// Vue3 Composition API
import { ref, computed, watch, watchEffect } from 'vue'

const count = ref(0)

// computed
const double = computed(() => count.value * 2)

// watch
watch(count, (newVal, oldVal) => {
  console.log(newVal, oldVal)
})

// watchEffect - 自动追踪依赖
watchEffect(() => {
  console.log(count.value)
})

区别总结

  • computed:有缓存,依赖不变不重新计算,适合复杂计算
  • watch:无缓存,每次都执行,适合异步操作或开销较大的操作

4. Vue组件通信方式

答案

// 1. Props / Emit(父子组件)
// 父组件
<Child :msg="message" @update="handleUpdate" />

// 子组件
export default {
  props: ['msg'],
  methods: {
    handleClick() {
      this.$emit('update', 'new value')
    }
  }
}

// 2. v-model(双向绑定)
// 父组件
<Child v-model="value" />

// 子组件
export default {
  props: ['modelValue'],
  emits: ['update:modelValue'],
  methods: {
    handleInput(e) {
      this.$emit('update:modelValue', e.target.value)
    }
  }
}

// 3. $refs(父访问子)
<Child ref="childRef" />

this.$refs.childRef.childMethod()

// 4. $parent / $children(不推荐)
this.$parent.parentMethod()
this.$children[0].childMethod()

// 5. provide / inject(祖孙组件)
// 祖先组件
export default {
  provide() {
    return {
      theme: 'dark',
      user: this.user
    }
  }
}

// 后代组件
export default {
  inject: ['theme', 'user']
}

// Vue3 响应式provide/inject
import { provide, inject, ref } from 'vue'

// 祖先组件
const theme = ref('dark')
provide('theme', theme)

// 后代组件
const theme = inject('theme')

// 6. EventBus(兄弟组件)
// Vue2
const EventBus = new Vue()

// 组件A
EventBus.$emit('event-name', data)

// 组件B
EventBus.$on('event-name', data => {
  console.log(data)
})

// Vue3使用mitt
import mitt from 'mitt'
const emitter = mitt()

emitter.on('event-name', data => {})
emitter.emit('event-name', data)

// 7. Vuex / Pinia(全局状态)
// Vuex
this.$store.commit('increment')
this.$store.dispatch('asyncIncrement')

// Pinia
import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()
store.increment()

// 8. $attrs / $listeners(Vue2)
// 父组件
<Child :name="name" :age="age" @click="handleClick" />

// 子组件(传递给孙组件)
<GrandChild v-bind="$attrs" v-on="$listeners" />

5. Vue生命周期

答案

Vue2生命周期

export default {
  // 1. 创建阶段
  beforeCreate() {
    // 实例初始化后,data、methods未初始化
    console.log(this.data) // undefined
  },
  created() {
    // data、methods已初始化,DOM未挂载
    // 适合:数据请求、初始化数据
    this.fetchData()
  },
  
  // 2. 挂载阶段
  beforeMount() {
    // 模板编译完成,DOM未渲染
  },
  mounted() {
    // DOM已挂载
    // 适合:DOM操作、第三方库初始化
    this.$refs.chart.init()
  },
  
  // 3. 更新阶段
  beforeUpdate() {
    // 数据更新,DOM未重新渲染
  },
  updated() {
    // DOM已重新渲染
    // 注意:避免在此修改数据,可能导致无限循环
  },
  
  // 4. 销毁阶段
  beforeDestroy() {
    // 组件销毁前
    // 适合:清除定时器、解绑事件
    clearInterval(this.timer)
  },
  destroyed() {
    // 组件已销毁
  }
}

Vue3生命周期

import { 
  onBeforeMount, 
  onMounted, 
  onBeforeUpdate, 
  onUpdated, 
  onBeforeUnmount, 
  onUnmounted 
} from 'vue'

export default {
  setup() {
    // beforeCreate和created的逻辑放在setup中
    console.log('setup')
    
    onBeforeMount(() => {
      console.log('beforeMount')
    })
    
    onMounted(() => {
      console.log('mounted')
    })
    
    onBeforeUpdate(() => {
      console.log('beforeUpdate')
    })
    
    onUpdated(() => {
      console.log('updated')
    })
    
    onBeforeUnmount(() => {
      console.log('beforeUnmount')
    })
    
    onUnmounted(() => {
      console.log('unmounted')
    })
  }
}

父子组件生命周期执行顺序

加载渲染:
父beforeCreate → 父created → 父beforeMount 
→ 子beforeCreate → 子created → 子beforeMount 
→ 子mounted → 父mounted

更新:
父beforeUpdate → 子beforeUpdate 
→ 子updated → 父updated

销毁:
父beforeDestroy → 子beforeDestroy 
→ 子destroyed → 父destroyed

React框架

1. React Hooks原理及使用

答案

常用Hooks

import { 
  useState, 
  useEffect, 
  useContext, 
  useReducer, 
  useCallback, 
  useMemo, 
  useRef, 
  useLayoutEffect 
} from 'react'

function Component() {
  // 1. useState - 状态管理
  const [count, setCount] = useState(0)
  const [user, setUser] = useState({ name: '张三', age: 20 })
  
  // 函数式更新
  setCount(prev => prev + 1)
  
  // 2. useEffect - 副作用
  useEffect(() => {
    // 副作用逻辑
    document.title = `Count: ${count}`
    
    // 清理函数
    return () => {
      document.title = 'React App'
    }
  }, [count]) // 依赖数组
  
  // 3. useContext - 上下文
  const theme = useContext(ThemeContext)
  
  // 4. useReducer - 复杂状态管理
  const [state, dispatch] = useReducer(reducer, initialState)
  
  // 5. useCallback - 缓存函数
  const handleClick = useCallback(() => {
    console.log(count)
  }, [count])
  
  // 6. useMemo - 缓存值
  const expensiveValue = useMemo(() => {
    return computeExpensiveValue(count)
  }, [count])
  
  // 7. useRef - 引用DOM或持久化值
  const inputRef = useRef(null)
  const countRef = useRef(count) // 不会触发重渲染
  
  useEffect(() => {
    inputRef.current.focus()
  }, [])
  
  // 8. useLayoutEffect - 同步执行
  useLayoutEffect(() => {
    // DOM变更后同步执行
  }, [])
  
  return <div>...</div>
}

自定义Hook

// 自定义Hook - 必须以use开头
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, decrement, reset } = useCounter(0)
  
  return (
    <div>
      <p>{count}</p>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
      <button onClick={reset}>Reset</button>
    </div>
  )
}

Hooks实现原理

// 简化版实现
let currentFiber = null
let hookIndex = 0

function useState(initialState) {
  const fiber = currentFiber
  const hooks = fiber.hooks || (fiber.hooks = [])
  const hook = hooks[hookIndex] || (hooks[hookIndex] = {
    state: initialState,
    queue: []
  })
  
  // 执行更新队列
  hook.queue.forEach(action => {
    hook.state = typeof action === 'function' ? action(hook.state) : action
  })
  hook.queue = []
  
  const setState = (action) => {
    hook.queue.push(action)
    scheduleUpdate() // 调度更新
  }
  
  hookIndex++
  return [hook.state, setState]
}

function useEffect(callback, deps) {
  const fiber = currentFiber
  const hooks = fiber.hooks || (fiber.hooks = [])
  const hook = hooks[hookIndex] || (hooks[hookIndex] = {})
  
  const hasChanged = !hook.deps || deps.some((dep, i) => dep !== hook.deps[i])
  
  if (hasChanged) {
    hook.cleanup && hook.cleanup()
    hook.cleanup = callback()
    hook.deps = deps
  }
  
  hookIndex++
}

2. React组件性能优化

答案

// 1. React.memo - 浅比较props
const MemoComponent = React.memo(function Component({ name, age }) {
  console.log('render')
  return <div>{name} - {age}</div>
})

// 自定义比较函数
const MemoComponent2 = React.memo(Component, (prevProps, nextProps) => {
  return prevProps.id === nextProps.id
})

// 2. useMemo - 缓存计算结果
function Component({ items }) {
  const expensiveResult = useMemo(() => {
    return items.reduce((acc, item) => acc + item.value, 0)
  }, [items]) // 只在items变化时重新计算
  
  return <div>{expensiveResult}</div>
}

// 3. useCallback - 缓存函数
function Parent() {
  const [count, setCount] = useState(0)
  const [name, setName] = useState('')
  
  // 不使用useCallback,每次渲染都会创建新函数
  const handleClick1 = () => {
    console.log(count)
  }
  
  // 使用useCallback,只在count变化时创建新函数
  const handleClick2 = useCallback(() => {
    console.log(count)
  }, [count])
  
  return <Child onClick={handleClick2} />
}

// 4. PureComponent - 类组件优化
class PureComp extends React.PureComponent {
  render() {
    return <div>{this.props.name}</div>
  }
}

// 5. shouldComponentUpdate - 手动控制更新
class Component extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    return nextProps.id !== this.props.id
  }
  
  render() {
    return <div>{this.props.name}</div>
  }
}

// 6. 虚拟列表 - 只渲染可见区域
import { FixedSizeList } from 'react-window'

function VirtualList({ items }) {
  const Row = ({ index, style }) => (
    <div style={style}>{items[index]}</div>
  )
  
  return (
    <FixedSizeList
      height={600}
      itemCount={items.length}
      itemSize={50}
      width="100%"
    >
      {Row}
    </FixedSizeList>
  )
}

// 7. 懒加载 - 按需加载组件
const LazyComponent = React.lazy(() => import('./Component'))

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  )
}

// 8. 代码分割 - 路由懒加载
const Home = React.lazy(() => import('./Home'))
const About = React.lazy(() => import('./About'))

// 9. 避免内联对象和函数
// ❌ 不好的做法
<Component style={{ color: 'red' }} onClick={() => {}} />

// ✅ 好的做法
const style = { color: 'red' }
const handleClick = () => {}
<Component style={style} onClick={handleClick} />

// 10. 使用Key优化列表渲染
{items.map(item => (
  <Item key={item.id} {...item} />
))}

3. React状态管理(Redux、Zustand)

答案

Redux使用

// 1. 创建store
import { createStore } from 'redux'

const initialState = { count: 0 }

function reducer(state = initialState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 }
    case 'DECREMENT':
      return { count: state.count - 1 }
    default:
      return state
  }
}

const store = createStore(reducer)

// 2. Redux Toolkit(推荐)
import { createSlice, configureStore } from '@reduxjs/toolkit'

const counterSlice = createSlice({
  name: 'counter',
  initialState: { count: 0 },
  reducers: {
    increment: state => {
      state.count += 1 // 可以直接修改(内部使用Immer)
    },
    decrement: state => {
      state.count -= 1
    },
    incrementByAmount: (state, action) => {
      state.count += action.payload
    }
  }
})

export const { increment, decrement, incrementByAmount } = counterSlice.actions

const store = configureStore({
  reducer: {
    counter: counterSlice.reducer
  }
})

// 3. 在组件中使用
import { useSelector, useDispatch } from 'react-redux'

function Counter() {
  const count = useSelector(state => state.counter.count)
  const dispatch = useDispatch()
  
  return (
    <div>
      <p>{count}</p>
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(decrement())}>-</button>
    </div>
  )
}

Zustand使用(更轻量):

import create from 'zustand'

// 创建store
const useStore = create((set, get) => ({
  count: 0,
  increment: () => set(state => ({ count: state.count + 1 })),
  decrement: () => set(state => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
  // 异步action
  fetchUser: async () => {
    const user = await fetch('/api/user').then(res => res.json())
    set({ user })
  }
}))

// 在组件中使用
function Counter() {
  const { count, increment, decrement } = useStore()
  
  return (
    <div>
      <p>{count}</p>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
    </div>
  )
}

// 选择性订阅(性能优化)
const count = useStore(state => state.count)

前端工程化

1. Webpack核心概念和配置

答案

核心概念

  • Entry:入口文件
  • Output:输出配置
  • Loader:文件转换器
  • Plugin:插件,扩展功能
  • Mode:模式(development/production)

基础配置

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  // 入口
  entry: './src/index.js',
  
  // 输出
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
    clean: true
  },
  
  // 模式
  mode: 'production',
  
  // Loader
  module: {
    rules: [
      // JS/JSX
      {
        test: /.jsx?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react']
          }
        }
      },
      // CSS
      {
        test: /.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader'
        ]
      },
      // SCSS
      {
        test: /.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'sass-loader'
        ]
      },
      // 图片
      {
        test: /.(png|jpg|gif|svg)$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024 // 10kb以下转base64
          }
        }
      }
    ]
  },
  
  // 插件
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html'
    }),
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css'
    })
  ],
  
  // 优化
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\/]node_modules[\/]/,
          name: 'vendors',
          priority: 10
        },
        common: {
          minChunks: 2,
          priority: 5,
          reuseExistingChunk: true
        }
      }
    }
  },
  
  // 开发服务器
  devServer: {
    port: 3000,
    hot: true,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true
      }
    }
  }
}

2. Vite与Webpack的区别

答案

特性WebpackVite
构建方式BundleESM
开发服务器启动慢(需打包)快(按需编译)
热更新较慢极快
生产构建WebpackRollup
配置复杂度复杂简单
生态成熟快速发展

Vite配置示例

// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'

export default defineConfig({
  plugins: [vue()],
  
  // 路径别名
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src')
    }
  },
  
  // 开发服务器
  server: {
    port: 3000,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: path => path.replace(/^/api/, '')
      }
    }
  },
  
  // 构建配置
  build: {
    outDir: 'dist',
    sourcemap: false,
    rollupOptions: {
      output: {
        manualChunks: {
          'vendor': ['vue', 'vue-router', 'pinia']
        }
      }
    }
  }
})

性能优化

1. 首屏加载优化方案

答案

// 1. 路由懒加载
const routes = [
  {
    path: '/',
    component: () => import('./views/Home.vue')
  },
  {
    path: '/about',
    component: () => import('./views/About.vue')
  }
]

// 2. 组件懒加载
const AsyncComponent = defineAsyncComponent(() => import('./Component.vue'))

// 3. 图片懒加载
<img v-lazy="imageUrl" />

// 或使用Intersection Observer
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target
      img.src = img.dataset.src
      observer.unobserve(img)
    }
  })
})

// 4. 资源预加载
<link rel="preload" href="critical.css" as="style">
<link rel="prefetch" href="next-page.js">

// 5. CDN加速
<script src="https://cdn.jsdelivr.net/npm/vue@3"></script>

// 6. Gzip压缩
// webpack配置
const CompressionPlugin = require('compression-webpack-plugin')

plugins: [
  new CompressionPlugin({
    algorithm: 'gzip',
    test: /.(js|css)$/,
    threshold: 10240,
    minRatio: 0.8
  })
]

// 7. 首屏内联关键CSS
<style>
  /* 关键CSS */
</style>

// 8. SSR服务端渲染
// Nuxt.js / Next.js

// 9. 骨架屏
<div class="skeleton">
  <div class="skeleton-avatar"></div>
  <div class="skeleton-content"></div>
</div>

// 10. Tree Shaking
// package.json
{
  "sideEffects": false
}

2. 长列表优化

答案

// 1. 虚拟滚动
// Vue3 + vue-virtual-scroller
<template>
  <RecycleScroller
    :items="items"
    :item-size="50"
    key-field="id"
    v-slot="{ item }"
  >
    <div class="item">{{ item.name }}</div>
  </RecycleScroller>
</template>

// React + react-window
import { FixedSizeList } from 'react-window'

function VirtualList({ items }) {
  const Row = ({ index, style }) => (
    <div style={style}>
      {items[index].name}
    </div>
  )
  
  return (
    <FixedSizeList
      height={600}
      itemCount={items.length}
      itemSize={50}
      width="100%"
    >
      {Row}
    </FixedSizeList>
  )
}

// 2. 分页加载
async function loadMore() {
  const { data } = await fetch(`/api/list?page=${page}`)
  items.value = [...items.value, ...data]
  page++
}

// 3. 时间切片
function timeSlice(tasks, duration = 50) {
  let index = 0
  
  function run() {
    const start = Date.now()
    
    while (index < tasks.length && Date.now() - start < duration) {
      tasks[index]()
      index++
    }
    
    if (index < tasks.length) {
      requestIdleCallback(run)
    }
  }
  
  requestIdleCallback(run)
}

// 使用
const tasks = items.map(item => () => renderItem(item))
timeSlice(tasks)

// 4. 防抖滚动
const handleScroll = debounce(() => {
  // 滚动处理逻辑
}, 200)

浏览器原理

1. 浏览器渲染流程

答案

1. 解析HTML → 构建DOM树
2. 解析CSS → 构建CSSOM树
3. 合并DOM树和CSSOM树 → 构建渲染树(Render Tree)
4. 布局(Layout)→ 计算元素位置和大小
5. 绘制(Paint)→ 将渲染树绘制到屏幕
6. 合成(Composite)→ 合成图层

优化点

// 1. 减少重排(Reflow)
// ❌ 触发多次重排
element.style.width = '100px'
element.style.height = '100px'
element.style.margin = '10px'

// ✅ 批量修改
element.style.cssText = 'width: 100px; height: 100px; margin: 10px;'
// 或
element.classList.add('new-style')

// 2. 使用transform代替left/top
// ❌ 触发重排
element.style.left = '100px'

// ✅ 只触发合成
element.style.transform = 'translateX(100px)'

// 3. 读写分离
// ❌ 强制同步布局
const width = element.offsetWidth
element.style.width = width + 10 + 'px'
const height = element.offsetHeight
element.style.height = height + 10 + 'px'

// ✅ 批量读取,批量写入
const width = element.offsetWidth
const height = element.offsetHeight
element.style.width = width + 10 + 'px'
element.style.height = height + 10 + 'px'

// 4. 使用DocumentFragment
const fragment = document.createDocumentFragment()
for (let i = 0; i < 1000; i++) {
  const div = document.createElement('div')
  fragment.appendChild(div)
}
document.body.appendChild(fragment)

// 5. 使用will-change提示浏览器
.element {
  will-change: transform;
}

2. 浏览器缓存机制

答案

// 1. 强缓存
// Cache-Control
Cache-Control: max-age=3600 // 1小时
Cache-Control: no-cache // 每次需验证
Cache-Control: no-store // 不缓存

// Expires(HTTP/1.0,已过时)
Expires: Wed, 21 Oct 2025 07:28:00 GMT

// 2. 协商缓存
// Last-Modified / If-Modified-Since
Last-Modified: Wed, 21 Oct 2024 07:28:00 GMT
If-Modified-Since: Wed, 21 Oct 2024 07:28:00 GMT

// ETag / If-None-Match(更精确)
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

// 3. 缓存策略
// HTML
Cache-Control: no-cache

// CSS/JS(带hash)
Cache-Control: max-age=31536000 // 1年

// 图片
Cache-Control: max-age=2592000 // 30天

// 4. Service Worker缓存
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open('v1').then(cache => {
      return cache.addAll([
        '/',
        '/styles/main.css',
        '/scripts/main.js'
      ])
    })
  )
})

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(response => {
      return response || fetch(event.request)
    })
  )
})

网络与安全

1. 跨域解决方案

答案

// 1. CORS(最常用)
// 服务端设置
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
  res.header('Access-Control-Allow-Credentials', 'true')
  next()
})

// 2. JSONP(仅支持GET)
function jsonp(url, callback) {
  const script = document.createElement('script')
  script.src = `${url}?callback=${callback}`
  document.body.appendChild(script)
}

window.handleData = function(data) {
  console.log(data)
}

jsonp('http://api.example.com/data', 'handleData')

// 3. 代理服务器
// webpack devServer
devServer: {
  proxy: {
    '/api': {
      target: 'http://localhost:8080',
      changeOrigin: true,
      pathRewrite: {
        '^/api': ''
      }
    }
  }
}

// Vite
server: {
  proxy: {
    '/api': {
      target: 'http://localhost:8080',
      changeOrigin: true,
      rewrite: path => path.replace(/^/api/, '')
    }
  }
}

// 4. postMessage
// 页面A
window.addEventListener('message', event => {
  if (event.origin === 'http://example.com') {
    console.log(event.data)
  }
})

// 页面B
window.parent.postMessage('数据', 'http://example.com')

// 5. nginx反向代理
location /api {
    proxy_pass http://backend:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

2. 常见Web安全问题

答案

// 1. XSS(跨站脚本攻击)
// 防御:转义输出
function escapeHTML(str) {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#x27;')
}

// Vue自动转义
<div>{{ userInput }}</div>

// 使用v-html需谨慎
<div v-html="sanitizedHTML"></div>

// 2. CSRF(跨站请求伪造)
// 防御:Token验证
// 后端生成token
const csrfToken = generateToken()
res.cookie('csrf-token', csrfToken)

// 前端发送请求时携带
axios.defaults.headers.common['X-CSRF-Token'] = getCookie('csrf-token')

// 3. SQL注入
// ❌ 危险的拼接
const query = `SELECT * FROM users WHERE username = '${username}'`

// ✅ 使用参数化查询
const query = 'SELECT * FROM users WHERE username = ?'
db.query(query, [username])

// 4. 点击劫持
// 防御:X-Frame-Options
X-Frame-Options: DENY
// 或
X-Frame-Options: SAMEORIGIN

// CSP
Content-Security-Policy: frame-ancestors 'self'

// 5. HTTPS
// 使用HTTPS传输敏感数据
// 设置Secure Cookie
Set-Cookie: sessionId=xxx; Secure; HttpOnly; SameSite=Strict

算法与数据结构

1. 常见算法题

答案

// 1. 防抖
function debounce(fn, delay) {
  let timer = null
  return function(...args) {
    clearTimeout(timer)
    timer = setTimeout(() => {
      fn.apply(this, args)
    }, delay)
  }
}

// 2. 节流
function throttle(fn, delay) {
  let lastTime = 0
  return function(...args) {
    const now = Date.now()
    if (now - lastTime >= delay) {
      fn.apply(this, args)
      lastTime = now
    }
  }
}

// 3. 深拷贝
function deepClone(obj, map = new WeakMap()) {
  // 处理null和非对象
  if (obj === null || typeof obj !== 'object') {
    return obj
  }
  
  // 处理循环引用
  if (map.has(obj)) {
    return map.get(obj)
  }
  
  // 处理日期
  if (obj instanceof Date) {
    return new Date(obj)
  }
  
  // 处理正则
  if (obj instanceof RegExp) {
    return new RegExp(obj)
  }
  
  // 处理数组和对象
  const clone = Array.isArray(obj) ? [] : {}
  map.set(obj, clone)
  
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key], map)
    }
  }
  
  return clone
}

// 4. 数组扁平化
function flatten(arr, depth = 1) {
  if (depth === 0) return arr
  
  return arr.reduce((acc, cur) => {
    return acc.concat(
      Array.isArray(cur) ? flatten(cur, depth - 1) : cur
    )
  }, [])
}

// 或使用flat
const arr = [1, [2, [3, [4]]]].flat(Infinity)

// 5. 数组去重
// 方法1: Set
const unique = arr => [...new Set(arr)]

// 方法2: filter
const unique2 = arr => arr.filter((item, index) => arr.indexOf(item) === index)

// 方法3: reduce
const unique3 = arr => arr.reduce((acc, cur) => {
  return acc.includes(cur) ? acc : [...acc, cur]
}, [])

// 6. 实现Promise.all
Promise.myAll = function(promises) {
  return new Promise((resolve, reject) => {
    const results = []
    let count = 0
    
    promises.forEach((promise, index) => {
      Promise.resolve(promise).then(
        value => {
          results[index] = value
          count++
          if (count === promises.length) {
            resolve(results)
          }
        },
        reason => reject(reason)
      )
    })
  })
}

// 7. 实现发布订阅
class EventEmitter {
  constructor() {
    this.events = {}
  }
  
  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = []
    }
    this.events[event].push(callback)
  }
  
  emit(event, ...args) {
    if (this.events[event]) {
      this.events[event].forEach(callback => {
        callback(...args)
      })
    }
  }
  
  off(event, callback) {
    if (this.events[event]) {
      this.events[event] = this.events[event].filter(cb => cb !== callback)
    }
  }
  
  once(event, callback) {
    const wrapper = (...args) => {
      callback(...args)
      this.off(event, wrapper)
    }
    this.on(event, wrapper)
  }
}

// 8. 柯里化
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args)
    } else {
      return function(...args2) {
        return curried.apply(this, args.concat(args2))
      }
    }
  }
}

// 使用
const add = (a, b, c) => a + b + c
const curriedAdd = curry(add)
curriedAdd(1)(2)(3) // 6
curriedAdd(1, 2)(3) // 6

项目实战

1. 如何设计一个组件库

答案

// 1. 目录结构
components/
├── Button/
│   ├── index.vue
│   ├── index.ts
│   └── style.css
├── Input/
├── ...
├── index.ts // 导出所有组件

// 2. 组件设计原则
// - 单一职责
// - 可组合
// - 可配置
// - 可扩展

// 3. Button组件示例
<template>
  <button 
    :class="['btn', `btn-${type}`, `btn-${size}`, { 'btn-disabled': disabled }]"
    :disabled="disabled"
    @click="handleClick"
  >
    <slot></slot>
  </button>
</template>

<script setup>
defineProps({
  type: {
    type: String,
    default: 'default',
    validator: value => ['default', 'primary', 'danger'].includes(value)
  },
  size: {
    type: String,
    default: 'medium'
  },
  disabled: Boolean
})

const emit = defineEmits(['click'])

const handleClick = (e) => {
  if (!props.disabled) {
    emit('click', e)
  }
}
</script>

// 4. 导出组件
// index.ts
import Button from './Button'
import Input from './Input'

const components = [Button, Input]

const install = (app) => {
  components.forEach(component => {
    app.component(component.name, component)
  })
}

export { Button, Input }
export default { install }

// 5. 使用
import MyUI from 'my-ui'
app.use(MyUI)

// 或按需引入
import { Button } from 'my-ui'

2. 微前端架构设计

答案

// 使用qiankun框架
// 主应用
import { registerMicroApps, start } from 'qiankun'

registerMicroApps([
  {
    name: 'app1',
    entry: '//localhost:8081',
    container: '#container',
    activeRule: '/app1',
    props: {
      data: { user: 'admin' }
    }
  },
  {
    name: 'app2',
    entry: '//localhost:8082',
    container: '#container',
    activeRule: '/app2'
  }
])

start()

// 子应用
// main.js
let instance = null

function render(props = {}) {
  const { container } = props
  instance = createApp(App)
  instance.mount(container ? container.querySelector('#app') : '#app')
}

// 独立运行
if (!window.__POWERED_BY_QIANKUN__) {
  render()
}

// 作为微应用
export async function bootstrap() {
  console.log('app bootstraped')
}

export async function mount(props) {
  console.log('app mounted')
  render(props)
}

export async function unmount() {
  console.log('app unmounted')
  instance.unmount()
  instance = null
}

// 应用间通信
import { initGlobalState } from 'qiankun'

const actions = initGlobalState({
  user: null,
  theme: 'light'
})

// 监听变化
actions.onGlobalStateChange((state, prev) => {
  console.log(state, prev)
})

// 修改状态
actions.setGlobalState({ user: { name: 'admin' } })

这份面试题集涵盖了2025年前端开发的核心知识点,建议重点掌握Vue3、React18、性能优化和工程化相关内容。