Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

wangsanshao/document

Open more actions menu

Repository files navigation

document

1、浏览器强缓存和协商缓存

强缓存 状态码200(from disk cache/ from memory cache) 响应头cache-contol 可以设置max-age js和图片会存入memory(内存), css 会存入disk(磁盘)

选项 解释
max-age=100 缓存在100s后过期,资源缓存在本地
no-cache 不使用本地缓存,使用协商缓存
no-store 不使用任何缓存
public 可以被所有的用户缓存,包括客户端和代理服务器
private 只能被单个用户缓存,通常是用户浏览器,代理服务器则无法缓存

如果cache-control 与expries同时存在,cache-control优先级高。

协商缓存 状态304

响应头last-modified 请求的时候带if-modifien-since,最后一次文件修改的时间

响应头 Etag 请求的时候头部 if-none-match,比较资源内容是否改动

2、webpack和vite的区别

构建速度: webpack 需要将项目所有的资源,进行静态分析,生成分析依赖图,进行打包。冷启动时间长,热更新的时候需要重新构建依赖图,热更新也慢。 vite 是基于现代浏览器对于原生ES的支持,无需预先打包。冷启动时间短,只更新修改的模块,无需重新构建,且利用浏览对于es的缓存,使得热更新更快。

开发和生产模式: webpack 在开发和生产环境使用相同的打包机制 vite 开发环境使用原生es,按需编译。生产环境使用Rollup进行打包,生成优化的静态文件

生态: webpack 插件丰富,可以处理各种资源类型。社区活跃。 vite 生态相对较新,但是发展迅速,插件是基于Rollup。

3、commonJs和ES规范的区别?

用法: commonJs

  • 使用require()引入
  • 使用exports.module 或者 exports 导出 esm
  • 使用import引入
  • 使用export导出

执行时机: commonJs

  • 模块加载和执行是同步的。
  • require()会阻塞代码执行,直到代码加载完毕
  • 模块的执行顺序和代码出现的顺序一致

esm

  • 模块的加载和执行是异步的
  • import不会阻塞代码的执行,浏览器会在后台加载模块
  • 模块的执行顺序取决于模块之间的依赖关系和浏览器的加载策略

动态导入 commonJs

  • 支持动态导入,但是是同步的 ems
  • 支持动态导入,是异步的,返回一个promise

顶层作用域 commonJS

  • 每个模块都有自己的顶层作用域
  • 再一个模块中定义的变量,函数等不会污染全局作用域,也不会被其他模块直接访问,除非显式导出 ems
  • 顶层作用域是共享的

兼容性 commonJs

  • nodejs环境
  • 浏览器需要使用webpack等工作进行转换 ems
  • js标准模块
  • 现代浏览器都支持原生esm

4、为什么要使用打包工具

模块化开发,性能优化,多种文件类型处理, 兼容性处理,开发效率提升

5、CDN(Content Delivery Network,内容分发网络)是指通过在不同地理位置部署服务器,来更快、更可靠地向用户分发静态和动态网页内容的技术。CDN的作用是:加速网站访问速度,提高用户体验,降低服务器负载,提高内容的可用性和安全性。

6、get请求和post请求有什么区别

从字面看get是请求数据,post是发送数据,其实两者都可以请求和发送。 get是通过url传参,参数数量有限,post是通过请求体携带参数,参数相对更多。 部分get请求不会跨域,script的src属性,img的href等

7、简单请求与复杂请求

简单请求:

  • 使用GET,POST,HEAD三种方法之一,
  • 设置的头部信息仅限于Accept,Accept-Language,Content-Language,Content-Type(仅限于application/x-www-form-unlencoded、multipart/form-data、text/plan),
  • 请求中任何XMLHttpRequestUpload对象没有注册任何事件监听器,XMLHttpRequestUpload对象可以使用XMLHttpRequest.upload属性访问,
  • 请求中没有使用ReadadleStream对象 复杂请求:
  • 使用了除GET,POST,HEAD之外的http方法
  • 人为设置的头信息超出了简单请求允许的范围
  • 像服务器发送了除允许的类型之外的Content-Type,如application/json

8、HTTPS的加密原理

首先客户端发送请求到服务器,服务器给客户端颁发证书,且把自己的公钥和证书一起发送给客户端。证书是有CA私钥加密,客户端使用CA的公钥验证,验证成功后,客户端自己生成一个随机数预主密钥,然后用服务端的公钥加密后,发送给服务端,然后客户端和服务端根据预主密钥等条件生产会话秘钥。后续会话都使用会话密钥加密传输

9、对称加密

11、前端持久化方案

  • localStorage
  • sessionStorage 当前会话中有效,关闭浏览器数据丢失
  • indexedDB 储存大数据,支持事务和异步操作
    const request = indexedDB.open('myDataBase', 1)
    request.onupgradeneeded = function(event) {
      const db = event.target.result
      db.createObjectStore('myStore', { keyPath: 'id '})
    }
    request.onsuccess = function(event) {
      const db = event.target.result;
      const transaction = db.transaction('myStore', 'readwrite')
      const store = transaction.objectStore('myStore');
      store.add({ id: 1, name: 'example'})
    }
    
  • cookies 储存少量数据,用于会话管理和用户跟踪

12、css实现图片高度自适应

13、flex

  • 容器6个属性
    • flex-direction属性决定主轴的方向
    • flex-wrap 是否换行
    • flex-flow 是上面两个的缩写
    • justify-content属性定义了项目在主轴上的对齐方式。
    • align-items属性定义项目在交叉轴上如何对齐。
    • align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。
  • 项目的6个属性
    • order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。
    • flex-grow 放大比例 默认为0,即如果存在剩余空间,也不放大。
      • 如果所有项目的flex-grow属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。
    • flex-shrink 缩小比例 默认为1,即如果空间不足,该项目将缩小。
      • 如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小
    • flex-basis 分配多余空间之前,项目占据的主轴空间.
      • 浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。
    • flex 上面三个的缩写 默认值为0 1 auto。后两个属性可选。
      • 该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。
    • align-self 允许单个项目有与其他项目不一样的对齐方式。可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。

14、flex: 1

flex-grow: 1;    /* 允许元素增长以填充可用空间 */
flex-shrink: 1;  /* 允许元素缩小以适应容器 */
flex-basis: 0%;  /* 元素的初始大小为 0 */

15、vue虚拟DOM

虚拟DOM实际上一个javaScript对象,用来表示真实DOM的数据结构。但是不会直接与浏览器交互,虚拟DOM的作用主要目的是减少直接操作真实DOM的次数,从而提高性能。

  • 工作原理
    • 创建虚拟DOM:当一开始vue组件渲染的时候,Vue会创建一个虚拟DOM树,表示组件的结构个状态
    • 更新虚拟DOM:当组件状态发生变化的时候,Vue会重新生成一个新的虚拟DOM树
    • 比较差异:Vue使用一种差异算法,来比较新旧虚拟DOM树之间的差异。
    • 更新真实DOM:找到差异,计算出最小的DOM操作,并将这些操作应用到真实DOM上

16、Vue双向绑定

两种方式:

  • v-model指令,作用表单元素如input, textarea, select
  • 元素通过$emit('input', value)暴露input,然后v-model指令作用与元素上 双向绑定是指数据和视图之间的同步更新。 内部是通过Object.defineProperty 或 Proxy(在 Vue 3 中)来劫持对象属性,并在属性变化的时候通知视图更新

17、浏览器渲染过程

  • URL解析
    • 确定协议,域名,端口
    • 特殊字符处理
  • DNS查询
    • 本地host,是否有对应的域名ip
    • 查找缓存
      • 浏览器缓存
      • 操作系统缓存
      • 路由器缓存
    • DNS查询,向DNS服务器发送请求,获取对应域名的IP地址
  • 建立TCP连接
    • 三次握手
      • 浏览器发送SYN包到服务器
      • 服务器回复SYN+ACK包
      • 浏览器发送ACK包,连接建立
  • 发送HTTP请求
  • 服务器处理请求
  • 接收响应
  • 渲染页面
    • 解析HTML 构建DOM树
    • 加载资源 link,script,img等标签资源
    • 解析css 构建CSSOM树
    • 构建渲染树,结合DOM和CSSOM,生成渲染树
    • 布局:计算每个节点的几何信息(位置,大小)
    • 绘制:将渲染树绘制到屏幕上
  • 执行javascript
    • 解析和执行javascript代码,可能会修改DOM或者CSSOM,引起重新布局和绘制
  • 页面交互
  • 关闭连接

18、手写防抖和节流

// 防抖 取最后一次
const debounce = () => {
  let timer = null
  return function() {
    if(timer) { clearTimeout(timer) }
    timer = setTimeout(() => {
      fn.apply(this, arguments)
    }, delay)
  }
}

// 节流 取第一次
const throttle = () => {
  let timer = null
  return function() {
    if(timer) { return }
    timer = setTimeout(() => {
      fn.apply(this, arguments)
      timer = null
    }, delay)
  }
}

19、vue-lazyloader原理

20、es5实现es6的class

21、浏览器的事件循环和 nodejs 事件循环的区别

22、express 的设计原理

23、vue-router原理

  • history 底层事件pushState popState
  • hash 底层事件hashchange

24、手写promise

25、手写promise.all

const PromiseAll = async(array) => {

}

26、手写Vue的mixin方法

27、怎么判断一个点是否在圆形内、正方形内

圆形

const isInCircle = (x, y, circle) => {
  // 解构circle对象,获取圆心的x坐标、y坐标和半径
  const { x: cx, y: cy, r } = circle
  // 计算点(x, y)到圆心的距离
  const distance = Math.sqrt((x - cx) ** 2 + (y - cy) ** 2)
  // 如果距离小于或等于半径,则点在圆内
  return distance <= r
}

正方形

const isInSquare = (x, y, square) => {
  const { x: sx, y: sy, w, h } = square
  return x >= sx && x <= sx + w && y >= sy && y <= sy + h
}

28、css 选择器的优先级

!important > 行内样式 > id选择器 > 类选择器 > 标签选择器 > 通配符选择器 > 继承 > 浏览器默认样式

29、实现一个发布订阅

class PubSub {
  constructor() {
    this.events = new Map()
    this.idCounts = 0
  }

  subscribe(eventName, callback) {
    if(typeof callback !== 'function') {
      throw new Error('callback must be a function')
    }

    const id = this.idCounts++
    if( !this.events.has(eventName) ) {
      this.events.set(eventName, new Map())
    }

    this.events.get(eventName).set(id, callback)
    return {
      unsubscribe: () => this._unsubscribe(eventName, id)
    }
  }
  
  publish(eventName, data) {
    const subscription = thus.events.get(eventName);
    if(subscription) {
      subscription.forEach((callback, id) => {
        try {
          callback(data)
        } catch(error) {
          console.error(`Error in callback ${id} for event ${eventName}:`, error)
        }
      })
    }
  }

  subscribeOnce(eventName, callback) {
    const subscription = this.subscribe(eventName, (data) => {
      callback(data)
      subscription.unsubscribe()
    })
    return subscription
  }

  _unsubscribe(eventName, id) {
    const subscription = this.events.get(eventName)
    if(subscription) {
      subscription.delete(id)
      if(subscription.size === 0) {
        this.events.delete(eventName)
      }
    }
  }

  clearAll() {
    this.events.clear()
  }

  getSubscriptionCount(eventName) {
    if(eventName) {
      return this.events.has(eventName) ? this.events.get(eventName).size : 0
    }
    return Array.from(this.events.values()).reduce((acc, curr) => acc + curr.size, 0)
  }
}

30、手写bind

Function.prototype.myBind = function(context, ...args) {
  const originalFunc = this

  return function(...newArgs) {
    const combinedArgs = args.concat(newArgs)
    const result = originalFunc.apply(context, combinedArgs)
    return result
  }
}

31、手写call

Function.prototype.myCall = function(context, ...args) {
  // 1. 处理 undefined 和 null 的上下文
  context = context || window; // 浏览器环境用 window,Node.js 需改为 globalThis
  // 2. 创建唯一键避免属性覆盖
  const fnKey = Symbol('__tempFn__');
  // 3. 将当前函数绑定到上下文
  context[fnKey] = this; // this 指向原函数
  // 4. 执行函数并保存结果
  const result = context[fnKey](...args);
  // 5. 清理临时属性
  delete context[fnKey];
  // 6. 返回执行结果
  return result;
};

32、手写apply

Function.prototype.myApply = function(context, argsArray) {
  // 类型安全检查
  if (typeof this !== 'function') {
    throw new TypeError('调用者必须是函数');
  }
  // 处理上下文
  context = context != null ? Object(context) : window;
  // 创建唯一键
  const fnKey = Symbol('fn');
  // 绑定函数
  context[fnKey] = this;
  // 参数处理
  let result;
  if (!argsArray) {
    result = context[fnKey]();
  } else {
    if (!Array.isArray(argsArray) && !isArrayLike(argsArray)) {
      throw new TypeError('第二个参数必须为数组或类数组');
    }
    result = context[fnKey](...Array.from(argsArray));
  }
  // 清理
  delete context[fnKey];
  return result;
};

33、手写new 关键字

const newInstance = (fn, ...args) => {
  const obj = Object.create(fn.prototype) // 创建一个新对象,继承构造函数的原型
  const res = fn.apply(obj, args) // 将构造函数的this指向新创建的对象,并传入参数
  return res instanceof Object ? res : obj // 如果构造函数返回的是一个对象,则返回该对象,否则返回新创建的对象
}

34、react-fiber

react15 渲染是同步不可中断的 react16 渲染是异步的可中断的 React Fiber 是 React 16 引入的核心架构革新,旨在解决旧版本(如 React 15)因同步渲染机制导致的性能瓶颈和交互卡顿问题。在 React 15 中,协调器(Reconciler)通过递归方式同步构建和对比虚拟 DOM 树,若遇到复杂组件或大规模 DOM 更新,JS 线程会长时间阻塞主线程,导致 GUI 渲染线程无法及时响应用户操作(如点击、滚动),造成界面卡顿甚至丢帧,严重影响用户体验。

Fiber 架构通过三大核心改进优化了这一过程:

任务切片与可中断调度:将虚拟 DOM 拆解为独立的 Fiber 节点链表结构,代替原有树形递归遍历。每个 Fiber 节点对应一个可中断的异步任务单元,利用浏览器空闲时段(通过调度器模拟 requestIdleCallback)分片执行,避免长时间占用主线程。

优先级调度:内置调度器(Scheduler)动态管理任务优先级(如用户交互触发的更新优先于数据请求),确保高优先级任务快速响应,提升交互流畅度。

增量渲染与双缓存:协调器(Reconciler)增量构建 Fiber 树并标记变更,完成后由渲染器(Renderer)按需批量更新真实 DOM,减少布局计算和重绘次数。同时采用双缓存机制,内存中维护新旧两棵 Fiber 树,实现无缝切换与回滚,支持并发模式下的状态一致性。 通过上述机制,Fiber 架构使 React 能够实现异步可中断的渲染流程,在复杂场景下保持界面高响应性,兼顾性能与用户体验,为后续并发模式(Concurrent Mode)和渐进式渲染奠定了基石。

35、深度优先遍历和广度优先遍历

36、setTimeout 模拟setInterval

const setInter = (fn, time) => {
  let running = true
  let timer = null
  const loop = () => {
    if(timer) { clearTimeout(timer) }
    if(!running) return
    fn()
    timer = setTimeout(loop, time)
  }
  loop()
  return {
    stop: () => {
      running = false
      clearTimeout(timer)
    }
  }
}

38、判断数据类型

typeof 可以判断number, string, boolean, undefined, symbol, function, object
instanceof 可以判断对象类型
Object.prototype.toString.call(value).slice(8, -1) 可以判断所有类型
const getType = (value) => {
  return Object.prototype.toString.call(value).slice(8, -1)
}

39、实现一个深拷贝

const deepClone = (obj) => {
  if(typeof obj !== 'object' || obj === null) {
    return obj
  }

  const result = Array.isArray(obj) ? [] : {}
  for(const key in obj) {
    if(obj.hasOwnProperty(key)) {
      result[key] = deepClone(obj[key])
    }
  }
  return result
}

40、http2和http1的区别

http1 是请求和响应都是文本格式传输,且是顺序处理,每个连接只能处理一个请求和响应,虽然可以使用keep-alive来复用连接,但是不能解决队头阻塞的问题。每次请求和响应都是携带完整的HTTP头部,重复传输相同信息,如User-Agent,cookie等,浪费带宽。 http2 数据是二进制传输,解析效率更高,单个TCP连接可以并发处理多个请求和响应,使用HPACK压缩头部,减少带宽占用。

41、xss攻击

xss攻击是跨站脚本攻击,攻击者通过在网页中注入恶意脚本,在用户浏览网页时执行恶意脚本,从而达到攻击目的。

42、csrf攻击

csrf攻击是跨站请求伪造,攻击者通过伪造用户请求,向目标网站发送恶意请求,从而达到攻击目的。

43、CSRF攻击的防御

  • 验证码
  • 使用CSRF Token
  • 检查Referer

44、vue的响应式原理

vue2 使用Object.defineProperty 劫持对象属性,当属性发生变化时,触发对应的回调函数。但是不能监听数组的变化,需要使用重写数组的方法来监听数组的变化。还有不能监听对象的添加和删除。嵌套数组和对象需要深度遍历。 vue3 使用Proxy 劫持对象,当对象的属性发生变化时,触发对应的回调函数。

45、vue3和vue2的区别

响应式原理:

  • vue2 使用Object.defineProperty劫持对象属性,无法检测新增/删除属性,需要$set/$delete
  • vue3 使用Proxy代理对象,支持更多数据类型的响应式追踪(如Map/Set等)

组合式API:

  • vue2 使用选项式API(data/methods等选项组织代码)
  • vue3 新增组合式API(setup + 响应式API),支持更好的逻辑复用和代码组织

性能优化:

  • vue3 虚拟DOM算法优化(编译器生成带更新标记的虚拟DOM)
  • vue3 打包体积更小(更好的tree-shaking支持)
  • vue3 渲染速度提升(静态节点提升、事件缓存等)

生命周期:

  • vue3 新增setup生命周期(组合式API入口)
  • 重命名beforeDestroy/beforeUnmount,destroyed/unmounted

Fragment特性:

  • vue2 模板必须单根节点
  • vue3 支持多根节点模板(Fragment特性)

TypeScript支持:

  • vue3 使用TypeScript重写,提供更好的类型推导
  • vue3 组件选项支持类型声明(defineComponent)

全局API:

  • vue2 全局API挂载Vue对象(Vue.use/Vue.component)
  • vue3 改为应用实例API(createApp().use())

46、2024年前端面试高频题:React Server Components 的核心优势

  1. 服务端渲染优化:在服务端直接生成静态内容,减少客户端JS包体积
  2. 自动代码分割:根据组件使用情况自动拆分代码,提升首屏加载速度
  3. 数据获取优化:服务端直接访问数据库/API,避免客户端多次请求
  4. SEO友好:服务端生成完整HTML内容,更利于搜索引擎爬取

47、2025年前端趋势预测题:WebAssembly 在前端的应用场景

  1. 高性能计算:图像/视频编辑、3D渲染等CPU密集型任务
  2. 跨语言开发:支持Rust/C++等语言编写前端核心逻辑
  3. 浏览器插件:实现更复杂的浏览器扩展功能
  4. 加密算法:安全执行密码学相关操作(如区块链应用)

48、2024年框架特性题:Vue 3.4 的响应式优化

  1. 更快的依赖追踪:使用位运算优化依赖收集速度
  2. 内存占用减少:改进的proxy handlers减少内存消耗
  3. 批量更新优化:自动合并多个状态变更的触发
  4. SSR hydration:改进服务端渲染的客户端激活过程

49、2025年工程化题:前端Monorepo的最佳实践

  1. 工作空间管理:使用pnpm workspace或npm 7+ workspaces
  2. 依赖共享:通过hoisting减少重复安装
  3. 构建优化:增量构建和缓存策略(如Turborepo)
  4. 版本管理:采用changesets进行多包版本控制

50、2024年性能优化题:首屏加载FCP优化方案

  1. 关键CSS内联:提取首屏关键样式直接嵌入HTML
  2. 字体加载策略:使用font-display: swap避免布局偏移
  3. 资源预加载:合理使用preload/prefetch指令
  4. 代码分割:基于路由的dynamic import拆分代码

51、voidzero框架,基于RUST语言开发

主要是一次构建AST语法树,统一语法树。

52、vue2和vue3的diff算法的区别

Vue2 双端Diff算法

  1. 遍历方式:采用双端比较(头头、尾尾、头尾、尾头)
  2. 节点复用:通过4个指针交叉比较旧新子节点数组
  3. 移动策略:优先处理相同节点,最后处理新增/删除节点
  4. 时间复杂度:O(n) 但存在较多冗余比较

Vue3 快速Diff算法

  1. 预处理阶段
    • 前序相同节点直接复用
    • 后序相同节点直接复用
  2. 核心Diff
    • 构建key-index映射表(keyed fragments)
    • 寻找最长递增子序列(LIS)作为稳定锚点
  3. 移动策略
    • 仅处理非稳定序列节点
    • 最小化DOM移动操作
  4. 时间复杂度:O(n) + O(k)(k为最长递增子序列长度)

关键差异对比

特性 Vue2 Vue3
算法类型 双端比较 快速Diff + LIS优化
key的作用 辅助节点匹配 关键索引依据
移动优化 简单位置交换 最小化DOM操作
静态节点处理 全量比较 静态提升跳过比较
动态列表性能 平均O(n) 接近O(1)稳定序列场景
内存占用 较高(维护4个指针) 较低(使用位运算标记)
vue2 使用双端比较算法,先比较头尾节点,然后比较剩余节点。

53、keep-alive常用的属性和实现原理

常用属性

属性名 类型 说明
include String/RegExp/Array 匹配名称的组件会被缓存(基于组件name选项)
exclude String/RegExp/Array 匹配名称的组件不会被缓存
max Number 最多可以缓存多少组件实例(LRU缓存策略)
<keep-alive :include="['Home','About']" :max="10">
  <router-view />
</keep-alive>

实现原理

  1. 缓存存储结构
const cache: Record<string, VNode> = {}
const keys: string[] = []
  1. 缓存策略
  • 使用LRU(最近最少使用)算法管理缓存
  • 当缓存实例数量超过max时,销毁最久未被访问的实例
  1. 组件标识
  • 使用组件name选项 + 特殊标识(如tag名)作为缓存key
  • 没有name时自动生成唯一ID
  1. 生命周期管理
  • 缓存组件时触发deactivated钩子
  • 激活缓存时触发activated钩子
  • 使用VNode的data.keepAlive属性标记缓存状态
  1. 渲染机制
  • 首次渲染时创建真实DOM并缓存VNode
  • 后续渲染时直接复用缓存的VNode
  • 使用虚拟DOM的diff算法避免重复渲染

54、nextTick的原理

Vue的nextTick实现主要基于JavaScript事件循环机制,核心原理如下:

关键实现机制

  1. 微任务优先
  • 优先使用Promise.then()(现代浏览器)
  • 回退方案:MutationObserver → setImmediate → setTimeout
  1. 异步更新队列
const callbacks = [] // 回调队列
let pending = false // 批量处理标志

function flushCallbacks() {
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}
  1. 执行优先级
  • 数据变化 → 触发Watcher → 将更新推入队列
  • 同一事件循环内的数据变化会被批量处理
  • DOM更新完成后执行nextTick回调

核心特点

  • 批处理优化:同一tick内的多次nextTick调用会合并执行
  • 环境适配:自动选择最优的微任务实现方式
  • 错误处理:在微任务中捕获异常并通过console.error输出

使用示例

// Promise风格
this.$nextTick().then(() => {
  // DOM更新后执行
})

// 传统回调风格
this.$nextTick(() => {
  // 可访问更新后的DOM
})

55、vue data为什么是函数

核心原因

组件复用时的数据隔离:当组件被复用时,每个实例需要维护独立的数据副本。如果使用对象形式,所有组件实例将共享同一个数据对象,导致状态污染。

源码实现解析

function mergeOptions() {
  // 组件初始化时会执行data函数获取独立数据对象
  const data = options.data
  options.data = typeof data === 'function' 
    ? getData(data, vm)
    : data || {}
}

关键对比

// ❌ 错误写法(对象形式)
data: { count: 0 } // 所有实例共享同一个count

// ✅ 正确写法(函数返回对象)
data() {
  return { 
    count: 0 // 每个实例有独立count
  }
}

特殊场景说明

  1. 根实例例外:new Vue() 根实例可以直接使用对象形式,因为不会被复用
  2. 函数调用时机:在组件初始化时被调用,返回新数据对象
  3. 响应式处理:返回的对象会被Vue进行深度响应式处理

设计原则体现

  • 组件独立性:每个组件实例应有独立状态空间
  • 可预测性:避免隐式共享状态带来的副作用
  • 生命周期管理:通过函数调用时机控制数据初始化

56、继承

原型链继承

function Parent() {
  this.name = 'parent';
  this.colors = ['red', 'blue'];
}

Parent.prototype.say = function() {
  console.log('hello');
}

function Child() {}
Child.prototype = Object.create(Parent.prototype); // 直接使用父类实例作为子类原型
Child.prototype.constructor = Child;

组合继承

function Parent(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

Parent.prototype.say = function() {
  console.log('hello');
}

function Child(name, age) {
  Parent.call(this, name); // 第一次调用父类构造函数
  this.age = age;
}

Child.prototype = new Parent(); // 第二次调用父类构造函数
Child.prototype.constructor = Child;

寄生组合继承

function Parent(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

Parent.prototype.say = function() {
  console.log('hello');
}

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

// 继承原型
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

// 子类可以添加自己的方法
Child.prototype.play = function() {
  console.log('playing');
};

类继承

class Parent {
  constructor(name) {
    this.name = name;
    this.colors = ['red', 'blue'];
  }
  
  say() {
    console.log('hello');
  }
}

class Child extends Parent {
  constructor(name, age) {
    super(name); // 必须先调用super
    this.age = age;
  }
  
  play() {
    console.log('playing');
  }
}

57、浏览器渲染的线程都有哪些

  • 主线程
    • 负责解析HTML、CSS、JavaScript
  • 合成线程
    • 负责将页面分成多个图层,进行图层合成
  • 光栅化线程
    • 负责将图层转换为位图,通常由GPU加速
  • JavaScript引擎线程
    • 负责解析和执行JavaScript代码
  • 事件处理线程
    • 负责处理事件循环,将事件分发给主线程或其他线程处理。
  • 定时器线程
    • 负责处理定时器,如setTimeout、setInterval
  • 网络请求线程
    • 负责处理网络请求,如XMLHttpRequest、fetch
  • WebWorker线程 -允许在后台运行JavaScript代码,不阻塞主线程。
  • serviceWorker线程
    • 用于实现离线缓存、推送通知等功能,独立于主线程运行。
  • GPU线程
    • 负责处理GPU相关任务,如3D渲染、视频解码等。

58、避免react重复渲染的手段

  • 使用React.memo() 包裹组件,缓存组件渲染结果
  • 使用useMemo() 缓存复杂计算结果
  • 使用useCallback() 缓存函数引用
  • 使用useRef() 创建不变的引用对象
  • 使用React.Fragment 包裹组件,避免额外DOM节点
  • 使用React.StrictMode 包裹组件,检查潜在问题

59、单例模式

//全局变量
let singleInstance
function Person() {
  if(!singleInstance) {
    singleInstance = this
  }
  return singleInstance
}
const p1 = new Person()
const p2 = new Person()
console.log(p1 === p2) // true
// 静态方法
function Person() {}
Person.getInstance = function() {
  if(!Person.instance) {
    Person.instance = new Person()
  }
  return Person.instance
}
const p1 = Person.getInstance()
const p2 = Person.getInstance()
console.log(p1 === p2) // true
// 闭包
const Singleton = (function() {
  let instance = null
  function createInstance() {
    this.data = '111'
  }
  return function() {
    if(!instance) {
      instance = createInstance()
    }
    return instance
  }
})()
const p1 = Singleton()
const p2 = Singleton()
console.log(p1 === p2) // true

60、Promise

Promise.all() // 并发执行多个Promise,返回一个Promise,所有Promise都成功时返回成功结果,有一个失败则返回失败结果 Promise.allSettled() // 并发执行多个Promise,返回一个Promise,所有Promise都成功或失败时返回成功或失败结果 Promise.any() // 并发执行多个Promise,返回一个Promise,只要有一个Promise成功则返回成功结果,所有Promise都失败则返回失败结果 Promise.race() // 并发执行多个Promise,返回一个Promise,只要有一个Promise成功或失败则返回成功或失败结果 Promise.resolve() // 返回一个成功的Promise Promise.reject() // 返回一个失败的Promise

61、常见的性能优化的手段有哪些

代码压缩,代码分割,图片压缩,图片懒加载,路由懒加载,SSR/SSG,Web Worker,防抖节流,Web Worker处理密集计算,避免内存泄漏,合理的组件拆分,响应式数据优化,使用SSR/SSG,ESBuild/SWC加速构建,并行构建,增量构建,性能指标监控,错误监控,用户行为分析,性能预算,CI/CD中的性能检测,A/B测试验证

1. 网络优化

  1. 资源压缩与合并

    • 代码压缩(JS/CSS/HTML)
    • 图片压缩和合适的格式选择(WebP/AVIF)
    • 合理的文件合并策略
  2. 缓存策略

    • 浏览器缓存(Cache-Control)
    • Service Worker缓存
    • CDN缓存
  3. 按需加载

    • 路由懒加载
    • 组件动态导入
    • 图片懒加载

2. 渲染优化

  1. 关键渲染路径优化

    • CSS放头部,JS放底部
    • 内联关键CSS
    • 异步加载非关键资源
  2. 减少重排重绘

    • 使用transform替代位置改变
    • 批量DOM操作
    • 合理使用will-change
  3. 虚拟列表

    • 长列表分页或虚拟滚动
    • 避免一次性渲染大量DOM

3. 代码层面优化

  1. JavaScript优化

    • 防抖节流
    • Web Worker处理密集计算
    • 避免内存泄漏
  2. 框架层优化

    • 合理的组件拆分
    • 响应式数据优化
    • 使用SSR/SSG

4. 构建优化

  1. 打包优化

    • Tree Shaking
    • 代码分割(Code Splitting)
    • 压缩混淆
  2. 现代化构建

    • ESBuild/SWC加速构建
    • 并行构建
    • 增量构建

5. 监控与分析

  1. 性能指标监控

    • FCP/LCP/TTI等指标 // 首次内容绘制/最大内容绘制/可交互时间
    • 错误监控
    • 用户行为分析
  2. 持续优化

    • 性能预算
    • CI/CD中的性能检测
    • A/B测试验证

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published
Morty Proxy This is a proxified and sanitized view of the page, visit original site.