抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

面试八股

HTTP状态码及常见状态码

HTTP状态码

• 1xx:指示信息类,表示请求已接受,继续处理
• 2xx:指示成功类,表示请求已成功接受
• 3xx:指示重定向,表示要完成请求必须进行更近一步的操作
• 4xx:指示客户端错误,请求有语法错误或请求无法实现
• 5xx:指示服务器错误,服务器未能实现合法的请求

常见状态码

• 200 OK:客户端请求成功
• 301 Moved Permanently:所请求的页面已经永久重定向至新的URL
• 302 Found:所请求的页面已经临时重定向至新的URL
• 304 Not Modified 未修改
• 403 Forbidden:对请求页面的访问被禁止
• 404 Not Found:请求资源不存在
• 500 Internal Server Error:服务器发生不可预期的错误原来缓冲的文档还可以继续使用
• 503 Server Unavailable:请求未完成,服务器临时过载或宕机,一段时间后可恢复正常

HTTP报文的组成成分

请求报文{ 请求行、请求头、空行、请求体 }

响应报文{ 状态行、响应头、空行、响应体 }

Request Header:
GET /sample.Jsp HTTP/1.1 //请求行:{http方法、页面地址、http协议、http版本}
Host://请求的目标域名和端口号
Origin: //请求的来源域名和端口号 (跨域请求时,浏览器会自动带上这个头信息)
Referer: //请求资源的完整URI
User-Agent: //浏览器信息
Cookie: 当前域名下的Cookie
Accept: //代表客户端希望接受的数据类型
Accept-Encoding: //代表客户端能支持的压缩格式
Accept-Language://代表客户端可以支持的语言
Connection: keep-alive //告诉服务器,客户端需要的tcp连接是一个长连接

Response Header:
HTTP/1.1 200 OK // 响应状态行:{http协议、http版本 响应码 状态}
Date: //服务端发送资源时的服务器时间
Expires: //比较过时的一种验证缓存的方式,与浏览器(客户端)的时间比较,超过这个时间就不用缓存(不和服务器进行验证),适合版本比较稳定的网页
Cache-Control: // 控制缓存的方式
etag: // 一般是Nginx静态服务器发来的静态文件签名,浏览在没有“Disabled cache”情况下,接收到etag后,同一个url第二次请求就会自动带上“If-None-Match”
Last-Modified://是服务器发来的当前资源最后一次修改的时间,下次请求时,如果服务器上当前资源的修改时间大于这个时间,就返回新的资源内容
Content-Type: text/html; charset=utf-8 //如果返回是流式的数据,我们就必须告诉浏览器这个头,不然浏览器会下载这个页面,同时告诉浏览器是utf8编码,否则可能出现乱码
Content-Encoding: gzip //告诉客户端,应该采用gzip对资源进行解码
Connection: keep-alive //告诉客户端服务器的tcp连接也是一个长连接

HTTP请求/响应的步骤

  1. 用户在浏览器地址栏输入URL,URL由协议、域名、端口号、文件路径组成,没有文件路径的时候,就会访问根目录下的默认文件,例如index.html
  2. 浏览器检查本地缓存是否有该域名对应的IP地址。如果缓存中没有,浏览器会通过递归方式请求本地DNS服务器、本地DNS服务器通过迭代方式请求根域名服务器、顶级域名服务器、权威域名服务器等,最终将IP地址返回给客户端,这个过程中浏览器、操作系统、各级DNS服务器都会先查看本地缓存,没有记录再去查询
  3. 浏览器通过IP地址和端口号与服务器建立TCP连接。也就是”三次握手“过程,如果是HTTPS协议,还会进行TLS/SSL握手,确保通信安全。
  4. 浏览器向服务器发送HTTP请求,请求头中包含浏览器信息、支持的编码格式、Cookie等。服务器接收到请求后,根据路径和参数处理请求,生成响应内容。
  5. 服务器将响应数据和HTTP状态码返回给浏览器。响应头中包含内容类型、缓存策略等信息。
  6. 览器接收到响应后,开始解析HTML文件。解析过程中,浏览器会加载HTML中的CSS、JavaScript、图片等资源。浏览器构建DOM树、CSSOM树,合并生成渲染树,最后进行布局和绘制,将页面显示在屏幕上。
  7. 当所有资源加载完毕,页面完全渲染后,浏览器触发load事件,页面加载完成。
  8. TCP连接关闭(四次挥手)

HTTP 的 5 种方法

GET—获取资源
POST—传输资源
PUT—更新资源
DELETE—删除资源
HEAD—获取报文首部

POST请求和GET请求

  • GET传递的参数在url后拼接,不安全。POST传递的参数在request body中,更安全。
  • GET对数据进行查询,POST主要对数据进行增删改!
  • GET在浏览器回退时是无害的,而POST会再次提交请求
  • GET请求会被浏览器主动缓存,POST不会,要手动设置
  • GET请求长度有限制,POST没有

cookie、localStorage、sessionStorage区别

  • 相同:在本地(浏览器端)存储数据
  • 不同:
    • cookie同域名共享;localStorage要在相同的协议、域名、端口号下;sessionStorage比localStorage更严苛一点,除了协议、主机名、端口外,还要求在同一窗口(也就是浏览器的标签页)下。
    • localStorage是永久存储,除非手动删除;sessionStorage当会话结束(当前页面关闭的时候,自动销毁);cookie可以设置过期的时间,默认是会话结束的时候,当时间到期自动销毁
    • cookie的数据会在每一次发送http请求的时候,同时发送给服务器而localStorage、sessionStorage不会。
    • sessionStorage和localStorage的存储比cookie大得多

当Token被窃取并被用于伪造请求

  • 快速撤销被盗Token。

  • 监控异常请求:检查日志,识别异常IP、高频请求或非常规操作,及时拦截。

  • 设置较短的过期时间。

  • Token与首次请求的IP或设备指纹绑定,异常时拒绝请求。

  • 强制使用HTTPS,避免明文传输。避免将Token存储在localStorage中,优先使用HttpOnly+Secure的Cookie。

  • 避免在URL、日志或前端代码中暴露Token。对敏感操作(如修改密码)要求二次认证(如短信/OTP)。

  • Token使用限制

    • 单次Token(如微信支付的nonce_str)或限制Token使用次数。
    • 动态Token(如每次请求生成新Token,旧Token立即失效)。

OAuth2.0防护

XSS攻击

Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。

根据攻击的来源,XSS 攻击可分为存储型、反射型和 DOM 型三种。

对比:

类型 存储区 插入点
存储型 XSS 后端数据库 HTML
反射型 XSS URL HTML
DOM 型 XSS 后端数据库/前端存储/URL 前端 JavaScript

常用防范方法

  • httpOnly: 在 cookie 中设置 HttpOnly 属性后,js脚本将无法读取到 cookie 信息。
  • 输入过滤: 一般是用于对于输入格式的检查,例如:邮箱,电话号码,用户名,密码……等,按照规定的格式输入。
  • 转义 HTML: 如果拼接 HTML 是必要的,就需要对于引号,尖括号,斜杠进行转义。

预防 DOM 型 XSS 攻击

DOM 型 XSS 攻击,实际上就是网站前端 JavaScript代码本身不够严谨,把不可信的数据当作代码执行了。

在使用 .innerHTML.outerHTMLdocument.write() 时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用 .textContent.setAttribute() 等。

如果用 Vue/React 技术栈,并且不使用 v-html/dangerouslySetInnerHTML 功能,就在前端 render 阶段避免 innerHTMLouterHTML 的 XSS 隐患。

DOM 中的内联事件监听器,如 locationonclickonerroronloadonmouseover 等,<a> 标签的 href 属性,JavaScript 的 eval()setTimeout()setInterval() 等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患。

CSRF 跨站点请求伪造

跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。如:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。

XSS 是代码注入问题,CSRF 是 HTTP 问题。 XSS 是内容没有过滤导致浏览器将攻击者的输入当代码执行。CSRF 则是因为浏览器在发送 HTTP 请求时候自动带上 cookie,而一般网站的 session 都存在 cookie里面。

防御

  • 验证码;强制用户必须与应用进行交互,才能完成最终请求。此种方式能很好的遏制 csrf,但是用户体验比较差。
  • Referer check;请求来源限制,此种方法成本最低,但是并不能保证 100% 有效,因为服务器并不是什么时候都能取到 Referer,而且低版本的浏览器存在伪造 Referer 的风险。
  • token;token 验证的 CSRF 防御机制是公认最合适的方案。

进程和线程

进程是是系统进行资源分配的基本单位,线程是CPU调度的基本单位。一个进程可以包含多个线程,这些线程共享进程的资源,每个线程拥有独立的栈和寄存器。进程之间相互独立,每个进程拥有自己的地址空间和系统资源,多个线程共享所属进程的资源。线程不拥有自己独立的地址空间,所以切换较快。进程之间有进程间通信机制,如共享存储、消息队列、管道等。线程可以直接访问同一进程的全局变量,通信成本较低。创建和切换进程的开销更大。一个进程崩溃不会影响其他进程,而一个线程崩溃,可能会导致整个进程崩溃。

面向对象和面向过程有什么区别,JavaScript是哪一种

面向对象以对象为基本单元,数据和方法绑定在对象中。强调封装继承多态。

面向过程以函数为基本单元,数据和方法分离,通过函数和流程控制组织代码。

JavaScript属于面向对象的语言,两种范式都支持。

什么是模块化设计

将代码拆分为独立的、功能单一的模块,每个模块负责特定功能,通过明确的接口与其他模块通信。减少代码耦合,提高可维护性和复用性。

两种规范

  • CommonJS Node.js默认
  • ES Modules 原生浏览器支持

什么是组件化开发

将UI界面拆分为独立的、可复用的组件,每个组件包含自身的结构(HTML)、样式(CSS)和逻辑(JS)。

提高代码的复用性,隔离性,组合性。

Vue和React有什么区别,有什么相同

不同:

  • 模板语法:React采用JSX,Vue采用基于HTML的模板语法
  • 数据绑定:Vue实现了双向的数据绑定,React需要手动控制组件的属性和状态
  • 状态管理:Vue采用Vuex/Pinia,React使用Redux
  • 组件通信不同:Vue采用props和事件来实现父子组件通信,React采用props和回调函数实现
  • 生命周期不同:Vue有8个生命周期钩子,React有10个
  • 响应式原理不同:Vue使用双向数据绑定来实现数据更新,React使用单向数据流来实现

相同

  • VueReact 都采用了组件化开发的方式。
  • VueReact 都使用虚拟 DOM 技术。
  • VueReact 都支持响应式更新。
  • VueReact 都具有良好的集成能力。

生命周期函数

组件从创建到销毁的一些钩子函数

vue3

  • setup()

  • onBeforemount()

  • onMounted()

  • onBeforeUpdate()

  • onUpdated()

  • onBeforeUnMount()

  • onUnMounted()

React

  • constructor() useState()

  • render() 函数本身

  • componentDidMount() useEffect()

  • getDerivedStateFromProps() useState()返回的setState()函数

  • shouldComponentUpdate() useMemo()

  • getSnapshotBeforeUpdate()

  • componentDidUpdate() useEffect()

  • componentWillUnmount() useEffect()的返回函数

闭包

  • 函数可以捕获外部作用域的变量,即使在定义的外部作用域之外被调用。
  • 在函数外部无法读取函数内的局部变量。出于种种原因,我们有时候需要得到函数内的局部变量。那就是在函数的内部,再定义一个函数,利用内层函数来获取外层函数的局部变量。常用于模块化编程和数据封装。
  • 闭包可能会引起内存泄漏和性能问题

防抖节流

防抖和节流都是前端限制函数触发频率的技术。

防抖:函数触发后的一段时间内,不再触发事件时,才执行一次。应用:搜索栏搜索历史、浏览器窗口大小调整引起的页面变化

节流:函数触发后的一段时间内,保证只触发这一次。应用:无限滚动列表加载数据、表单提交。

箭头函数和普通函数的区别

  • 普通函数有自己的this,由调用方式决定。对象调用指向调用,new调用指向实例;箭头函数没有自己的this,继承外部作用域的this
  • 箭头函数不能作为构造函数,普通函数可以
  • 箭头函数有简洁语法,函数体只有一个表达式的时候,可以省略大括号和return
  • 普通函数可以访问隐式的arguments对象,它包含传递给函数的所有参数,而箭头函数没有

webpack构建过程

  1. 初始化配置项 config.js
  2. 解析依赖:模块之间的依赖关系
  3. 模块转化 :vue文件,jsx文件等解析为对应和html、css、js文件
  4. 优化:消除未使用的代码块、代码分割、代码压缩。
  5. 输出:将优化后的代码输出到指定目录中

Vite 和 Webpack的区别

vite的优势:基于原生ESModules,启动和热更新快。

webpack的优势:功能更全面更加灵活,生态更好,适合复杂的项目。

Vite 之所以在启动和热更新快的原因

  1. Vite 直接基于浏览器的原生 ESM 功能,无需打包启动,在浏览器请求时按需编译。
  2. 启动时使用 esbuild将第三方依赖预编译为 ESM 格式并缓存,后续启动直接使用缓存。
  3. 更新时仅替换修改的模块及其依赖链,无需重建整个 bundle。

Vite生产环境使用rollup

rollup是js模块打包工具,将分散的ESM代码高效打包为少数几个优化后的文件。它的核心优势就是使用Tree-shaking进行优化:

  1. 从入口文件出发,静态分析所有 import/export 语句,构建依赖关系图。
  2. 遍历依赖图,标记哪些 export 被其他模块 import 并使用。
  3. 将标记为未使用的代码从最终 Bundle 中移除。生成扁平化的 Bundle(合并作用域、变量名压缩等)。

跨域

跨域是指在浏览器中,当一个网页的脚本试图获取另一个源的资源的时候,由于同源策略限制协议域名端口号必须相同,可能会被浏览器阻止。解决方法CORS,JSONP,开发时正向代理(dev-server)、部署时反向代理(nginx)、WebSocket

正向代理和反向代理

正向代理代理客户端,目标服务器只能看到代理的IP,无法知道真实客户端的身份,可以用来做科学上网、爬虫、缓存加速。

反向代理代理服务器端,客户端以为反向代理就是真实的服务器,不知道后端服务器的存在。可以用来做CDN、负载均衡、隐藏服务器架构

强缓存和协商缓存

强缓存:不会向服务器发送请求,直接从缓存中读取资源。

协商缓存:向服务器发送请求,服务器会根据携带的request header来判断是否命中协商缓存。如果命中,浏览器返回304并带上新的response header通知浏览器从缓存中读取资源

两者的共同点是,都是从客户端缓存中读取资源;区别是强缓存不会发请求,协商缓存会发请求

浏览器的缓存过程

  1. 浏览器第一次加载资源,服务器返回200,浏览器将资源从服务器上下载下来,并把response header以及该请求的返回时间一并缓存
  2. 下一次加载资源时,比较当前请求时间和上次请求时间有没有超过cache-control设置的max-age,如果没有超过则命中强缓存,直接从本地读取文件。如果时间过期,向服务器发送header带有If-None-MatchIf-Modified-Since的请求。
  3. 服务器接到请求后,又出现根据Etag的值判断文件有没有做修改,一致则命中协商缓存,返回304。如果值不一样,直接返回新的资源。
  4. 如果收到的请求没有Etag值,则将If-Modified-Since和被修改的文件时间作对比

浏览器渲染流程

  1. 解析HTML和CSS生成DOM树和CSSOM树
  2. 将两棵树合并成渲染树
  3. 布局计算和绘制生成像素
  4. 合成显示

重排和重绘

重排:DOM结构发生改变以及元素的几何属性发生变化时,重新计算元素的位置和尺寸

重绘:元素的外观发生改变但不影响布局的时候,重新绘制元素

重排的渲染消耗更大,频繁的重排和重绘都会影响页面性能。

面对瞬时的并发请求,如何减少后端请求压力

  • 请求合并成一个请求
  • 数据缓存
  • 懒加载
  • WebSocket实时数据推送
  • 防抖和节流

UDP和TCP的区别、使用场景、如何让UDP变得可靠

UDP和TCP都是计算机网络传输层的协议,UDP是无连接、不可靠的,TCP是有链接、可靠的。UDP适合数据量小、需要实时传输、丢失数据可容忍的场景,如音视频传输、在线游戏。TCP适合于需要数据完整性和顺序性的场景,如网页浏览、文件传输。可以在应用层实现重传机制、数据校验来保证UDP的可靠性。

前端路由的几种方式

hash路由:利用URL中#后的哈希值变化不会引起路由跳转的特性。通过监听hashChange事件,读取hash值来实现。兼容性更好。 (最初是用来做锚点定位,滚动到页面的指定位置的)

history路由:利用history API修改URL路径并通过监听popState事件来监听浏览器前进后退。URL更简洁,使得服务器端可以解析完整路径

Vue的响应式原理

vue2中是通过Object.defineProperty来实现的,在页面加载时,vue会使用Object.defineProperty来重新定义data中的所有属性,劫持各个属性的setter和getter,读取属性时触发getter方法,修改属性时触发setter方法,通知组件实例对应的watch方法实现视图的更新。

defineProperty也是有缺点的:

  1. 对于复杂对象需要深度监听、性能不好
  2. 对于对象的新增、删除属性无法监听这些操作。所以需要Vue.$set和Vue.$delete
  3. 需要重写数组的原生方法来实现数组监听

在vue3中使用proxy来替代defineProperty,它的优势:

  1. 可以直接监听整个对象、不需要遍历监听属性,性能有所提升。
  2. 可以直接监听数组的变化
  3. 拦截方法更多(13种)

Vue2和Vue3有哪些区别

  • Vue2使用的是选项式API ,Vue3使用组合式API,提高代码可维护性和复用性。
  • Vue3使用Proxy代理替代Object.defineProperty实现了新的响应式系统,提高了性能。
  • Vue3引入了Teleport组件,可以将DOM元素渲染到DOM树的其他位置,用于创建模态框、弹出框等。
  • Vue3全局API名称发生了变化,同时新增了watchEffectHooks等功能
  • Vue3TypeScript的支持更加友好
  • Vue3核心库的依赖更少,减少打包体积

reactive 对比 ref

  1. 都是用来生成响应式数据
  2. 不同点
    1. reactive不能处理简单类型的数据
    2. ref参数类型支持更好,但是必须通过.value做访问和修改
    3. ref函数内部的实现依赖于reactive函数

SPA和多页面有什么区别

区别

  • 页面加载方式:在多页面应用中,每个页面都是独立的 HTML 文件,每次导航时需要重新加载整个页面。而在 SPA 中,初始加载时只加载一个 HTML 页面,后续的导航通过 JavaScript 动态地更新页面内容,无需重新加载整个页面。
  • 用户体验SPA 提供了流畅、快速的用户体验,因为页面切换时无需等待整个页面的重新加载,只有需要的数据和资源会被加载,减少了页面刷新的延迟。多页面应用则可能会有页面刷新的延迟,给用户带来较长的等待时间。
  • 代码复用SPA 通常采用组件化开发的方式,可以在不同的页面中复用组件,提高代码的可维护性和可扩展性。多页面应用的每个页面都是独立的,组件复用的机会较少。
  • 路由管理:在多页面应用中,页面之间的导航和路由由服务器处理,每个页面对应一个不同的 URL。而在 SPA 中,前端负责管理页面的导航和路由,通过前端路由库(如 React RouterVue Router)来管理不同路径对应的组件。
  • SEO(搜索引擎优化):由于多页面应用的每个页面都是独立的 HTML 文件,搜索引擎可以直接索引和抓取每个页面的内容,有利于搜索引擎优化。相比之下,SPA 的内容是通过 JavaScript 动态生成的,搜索引擎的爬虫可能无法正确地获取和索引页面的内容,需要采取额外的优化措施。
  • 服务器负载SPA 只需初始加载时获取 HTMLCSSJavaScript 文件,后续的页面更新和数据获取通常通过 API 请求完成,减轻了服务器的负载。而多页面应用每次导航都需要从服务器获取整个页面的内容。

优点

  • 可以实现实时更新和动态加载内容,使用户可以快速地与应用程序交互。
  • SPA 通常采用组件化开发的方式,提高了代码的可维护性和可扩展性。
  • 只有初始页面加载时需要从服务器获取 HTMLCSSJavaScript 文件,减轻了服务器的负载。

缺点

  • 首次加载时需要下载较大的js文件,这可能导致初始加载时间较长。

  • 由于 SPA 的内容是通过js动态生成的,搜索引擎的爬虫可能难以优化。

  • 内存占用高。

Vue的性能优化有哪些

编码阶段

  • v-ifv-for不一起使用
  • v-for保证key的唯一性
  • 使用keep-alive缓存组件
  • v-ifv-show酌情使用
  • 路由懒加载、异步组件
  • 图片懒加载
  • 节流防抖
  • 第三方模块按需引入

打包优化

  • 压缩代码
  • 使用CDN加载第三方模块
  • 抽离公共文件

用户体验

  • 骨架屏
  • 客户端缓存

SEO优化

  • 预渲染
  • 服务端渲染
  • 合理使用 meta 标签

Computed 和 Watch 的区别

computed计算属性,通过对已有的属性值进行计算得到一个新值。它需要依赖于其他的数据,当数据发生变化时,computed会自动计算更新。computed属性值会被缓存,只有当依赖数据发生变化时才会重新计算,这样可以避免重复计算提高性能。

watch用于监听数据的变化,并在变化时执行一些操作。它可以监听单个数据或者数组,当数据发生变化时会执行对应的回调函数,和computed不同的是watch不会有缓存。

常见的事件修饰符及其作用

  • .stop阻止冒泡
  • .prevent阻止默认事件
  • .capture :与事件冒泡的方向相反,事件捕获由外到内;
  • .self :只会触发自己范围内的事件,不包含子元素;
  • .once:只会触发一次。

v-if和v-show的区别

v-if条件渲染,v-show条件显示。v-if切换条件时会通过响应式系统销毁或渲染整个条件块,有更高的切换消耗,适用于初始不存在或不频繁切换可见性的组件,比如需要一定权限才显示的模块; v-show通过设置元素display属性控制显示隐藏,更高的初始渲染消耗,适用于频繁切换可见性的组件,比如弹窗。

data为什么是一个函数而不是对象

因为对象是一个引用类型,如果data是一个对象的情况下会造成多个组件共用一个datadata为一个函数,每个组件都会有自己的私有数据空间,不会干扰其他组件的运行。

如何设置动态路由

  • params传参
    • 路由配置: /index/:id
    • 路由跳转:this.$router.push({name: 'index', params: {id: "zs"}});
    • 路由参数获取:$route.params.id
    • 最后形成的路由:/index/zs
  • query传参
    • 路由配置:/index正常的路由配置
    • 路由跳转:this.$rouetr.push({path: 'index', query:{id: "zs"}});
    • 路由参数获取:$route.query.id
    • 最后形成的路由:/index?id=zs

区别

  • 获取参数方式不一样,一个通过$route.params,一个通过 $route.query

  • 参数的生命周期不一样,query参数在URL地址栏中显示不容易丢失,params参数不会在地址栏显示,刷新后会消失

路由守卫

  • 全局前置钩子:beforeEachbeforeResolveafterEach
  • 路由独享守卫:beforeEnter
  • 组件内钩子:beforeRouterEnterbeforeRouterUpdatebeforeRouterLeave

slot是什么?有什么作用?

slot插槽,一般在封装组件的时候使用,在组件内不知道以那种形式来展示内容时,可以用slot来占据位置,最终展示形式由父组件以内容形式传递过来,主要分为三种:

  • 默认插槽:又名匿名插槽,当slot没有指定name属性值的时候一个默认显示插槽,一个组件内只有有一个匿名插槽。
  • 具名插槽:带有具体名字的插槽,也就是带有name属性的slot,一个组件可以出现多个具名插槽。
  • 作用域插槽:默认插槽、具名插槽的一个变体,可以是匿名插槽,也可以是具名插槽,该插槽的不同点是在子组件渲染作用域插槽时,可以将子组件内部的数据传递给父组件,让父组件根据子组件的传递过来的数据决定如何渲染该插槽。

$nextTick 原理及作用

Vue 的 nextTick 其本质是对 JavaScript 执行原理 EventLoop 的一种应用。 nextTick是将回调函数放到一个异步队列中,保证在异步更新DOM的watcher后面,从而获取到更新后的DOM。

因为在created()钩子函数中,页面的DOM还未渲染,这时候也没办法操作DOM,所以,此时如果想要操作DOM,必须将操作的代码放在nextTick()的回调函数中。

虚拟DOM就一定比真实DOM更快吗

虚拟DOM不一定比真实DOM更快,而是在特定情况下可以提供更好的性能。

在复杂情况下,虚拟DOM可以比真实DOM操作更快,因为它是在内存中维护一个虚拟的DOM树,将真实DOM操作转换为对虚拟DOM的操作,然后通过diff算法找出需要更新的部分,最后只变更这部分到真实DOM就可以。在频繁变更下,它可以批量处理这些变化从而减少对真实DOM的访问和操作,减少浏览器的回流重绘,提高页面渲染性能。

而在一下简单场景下,直接操作真实DOM可能会更快,当更新操作很少或者只是局部改变时,直接操作真实DOM比操作虚拟DOM更高效,省去了虚拟DOM的计算、对比开销。

Diff 算法

Diff 算法(差异算法)是前端框架(如 React、Vue)用来 高效更新 DOM 的核心机制。它的核心思想是:对比新旧虚拟 DOM(或 VNode)的差异,只更新真正变化的部分,而不是全部重新渲染,从而提升性能。

为什么需要 ?

  • 直接操作 DOM 很慢:浏览器渲染 DOM 的开销很大,频繁更新会导致页面卡顿。
  • 全量更新浪费资源:如果数据变化时直接重新渲染整个页面,性能会很差。

基本流程

(1)虚拟 DOM(Virtual DOM)

  • 框架(如 React/Vue)会先用 JavaScript 对象(虚拟 DOM)描述页面结构。
  • 数据变化时,生成新的虚拟 DOM,然后和旧的虚拟 DOM 进行 对比(Diff)

(2)Diff 对比规则

① 同级比较(Tree Diff)
  • 只比较同一层级的节点,不会跨层级对比(减少计算量)。
  • 如果节点类型不同(如 divspan),直接销毁重建
② 列表对比(List Diff / Key 优化)
  • **给列表元素加 key**(如 key={item.id}),帮助框架识别哪些元素是新增、移动或删除的。
  • 如果没有 key,框架会按顺序对比,可能导致不必要的 DOM 操作

③ 组件对比(Component Diff)

  • 如果组件类型相同(如 <Button /><Button />),复用组件实例,只更新 props
  • 如果组件类型不同(如 <Button /><Input />),直接销毁重建。

JavaScript 的 Event Loop(事件循环)

JavaScript 是单线程的,意味着它一次只能执行一个任务。

Event Loop(事件循环) 就是 JS 处理异步任务的机制,它的核心逻辑是JS 按顺序执行同步代码(函数调用等)。异步任务完成后,它们的回调函数会被放入队列。队列又分成宏任务队列和微任务队列。宏任务队列包括 setTimeout、setInterval、IO、ui渲染事件的回调函数。微任务队列包括Promise.thenMutationObserver 等回调,优先级更高。

  • 调用栈为空时,先检查微任务队列,依次执行所有微任务。
  • 然后从宏任务队列取一个任务(如 setTimeout 回调)执行。
  • 重复这个过程。

垃圾回收机制

标记清除算法 JavaScript 中最常用的垃圾收集方式

这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。

该算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象)。垃圾回收器将定期从根开始(在JS中就是全局对象)扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的。那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。

此算法可以分为两个阶段,一个是标记阶段(mark),一个是清除阶段(sweep)。

  1. 标记阶段,垃圾回收器会从根对象开始遍历。每一个可以从根对象访问到的对象都会被添加一个标识,于是这个对象就被标识为可到达对象。
  2. 清除阶段,垃圾回收器会对堆内存从头到尾进行线性遍历,如果发现有对象没有被标识为可到达对象,那么就将此对象占用的内存回收,并且将原来标记为可到达对象的标识清除,以便进行下一次垃圾回收操作。

当内存耗尽时,程序将会被挂起,垃圾回收开始执行。

标记清除算法缺陷

  • 那些无法从根对象查询到的对象都将被清除
  • 垃圾收集后有可能会造成大量的内存碎片

引用计数算法

这是最初级的垃圾收集算法.现在已经没有浏览器会用这种算法.

此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。

引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。

引用计数缺陷

该算法有个限制:无法处理循环引用。如果两个对象被创建,并互相引用,形成了一个循环。它们被调用之后会离开函数作用域,所以它们已经没有用了,可以被回收了。然而,引用计数算法考虑到它们互相都有至少一次引用,所以它们不会被回收。

确定this的值

在非严格模式下,总是指向同一个对象,在严格模式下可以是任意值

非严格模式 严格模式
全局环境 window window
直接调用 window undefined
对象方法调用 调用者 调用者

开启严格模式

1
'use strict'

评论