自我介绍
您好,面试官!我叫[您的姓名],非常荣幸能有机会参加这次面试。
在过去的 3 年里,我一直专注于前端开发领域,积累了丰富的实践经验。
在 Vue.js 项目中,我能够熟练运用组件化开发模式,实现高效的代码复用和维护。对于 React,我也相信我能迅速上手并开发出功能完善的组件。我对新技术始终保持着浓厚的兴趣和学习热情,不断探索和尝试将最新的前端技术应用到实际项目中。我相信,我的技能和经验能够为贵公司的前端项目带来价值。
前端性能优化
前端性能优化是提高网站或应用程序用户体验的重要方面,以下是一些常见的前端性能优化方法:
1.常见优化
-
压缩Code Minification)和合并文件
压缩 CSS(MiniCssExtractPlugin+cssnano)、JavaScript(TerserPlugin) 和 HTML 文件,减少文件大小,加快加载速度。
合并多个 CSS 和 JavaScript 文件,减少请求数量。 -
图片优化(+base 64 asset)
选择合适的图片格式(如 JPEG、PNG、WebP 等),根据图片内容和用途进行选择。
压缩图片大小,不影响视觉效果的前提下减少图片的像素和质量。
使用懒加载(Lazy Loading)技术,只在图片进入视口时加载。 -
缓存策略
设置适当的 HTTP 缓存头,让浏览器缓存静态资源,避免重复请求。
利用 cache (HardSourceWebpackPlugin、persistentCache)选项启用缓存,提高构建速度。为输出的文件添加 contenthash ,以便浏览器有效地利用缓存。
使用 Service Workers 实现离线缓存和更灵活的缓存控制。 -
代码优化
优化 JavaScript 代码,避免不必要的计算和循环。
去除未使用的 CSS 代码,减少样式表的大小。 -
减少 HTTP 请求
尽量减少页面中的图片、脚本、样式表等资源的请求数量。 -
异步加载
对于非关键的 JavaScript 脚本,使用异步加载(如 defer 或 async 属性),不阻塞页面渲染。
-
优化 CSS 和 JavaScript 的加载顺序
将关键的 CSS 放在页面头部加载,以避免页面闪烁。
非关键的 JavaScript 放在页面底部加载,不影响页面内容的呈现。 -
使用 CDN(内容分发网络)
利用 CDN 加速静态资源的加载,减少网络延迟。 -
减少 DOM 操作
避免频繁的 DOM 读写操作,尽量一次性修改 DOM。
2.Webpack优化
- 代码分离(Code Splitting):
利用 splitChunks 插件将代码分割成多个块,例如将第三方库与业务代码分离,或者将不同路由的模块分离。 - 动态导入(Dynamic Imports):使用 import() 函数实现按需加载模块。
- css/js代码压缩
- cache缓存
- 优化模块解析(Module Resolution Optimization):
合理配置 resolve 选项,如设置 extensions 减少文件扩展名的尝试,指定 modules 减少模块搜索路径。
网络篇
1. 三次握手,四次挥手。为什么要三次握手,四次挥手,两次不行吗?为什么四次挥手比三次握手多一次?
三次握手
三次握手是 TCP 协议建立连接时使用的一种机制。
具体过程如下: 第一次握手:客户端向服务端发送一个带有 SYN标志位的数据包,表示客户端想要建立连接,同时初始化序列号(seq)。
第二次握手:服务端收到客户端的 SYN 数据包后,向客户端发送一个带有SYN 和 ACK 标志位的数据包,确认客户端的请求,并也初始化自己的序列号(seq),同时确认号(ack)为客户端的序列号加 1。
第三次握手:客户端收到服务端的 SYN + ACK 数据包后,向服务端发送一个带有 ACK 标志位的数据包,确认号为服务端的序列号加1,表示客户端确认收到了服务端的回应,连接建立成功。
四次挥手
四次挥手是 TCP 协议断开连接时使用的机制。 具体过程如下:
第一次挥手:客户端向服务端发送一个带有 FIN 标志位的数据包,表示客户端没有数据要发送了,想要关闭连接。
第二次挥手:服务端收到客户端的 FIN 数据包后,向客户端发送一个带有 ACK标志位的数据包,表示服务端已经收到客户端的关闭请求。
第三次挥手:服务端向客户端发送一个带有 FIN标志位的数据包,表示服务端也没有数据要发送了,准备关闭连接。
第四次挥手:客户端收到服务端的 FIN 数据包后,向服务端发送一个带有 ACK 标志位的数据包,表示客户端确认收到服务端的关闭请求,连接完全关闭。
为什么要三次握手,两次不行吗?
三次握手是必要的,两次握手不行,原因如下:
两次握手无法确认双方的接收和发送能力。第一次握手客户端表明能发送,但服务端不知道客户端能接收。第二次握手服务端表明能接收和发送,但客户端不知道服务端能接收。
两次握手可能导致错误的连接建立。如果网络延迟,旧的请求到达服务端,服务端以为是新的请求而建立连接,会造成资源浪费和错误。
为什么四次挥手比三次握手多一次?
四次挥手比三次握手多一次,主要是因为在关闭连接时,双方的数据传输状态不同。
在三次握手建立连接时,服务端收到客户端的连接请求并确认后,可以直接将 SYN 和 ACK一起发送,因为此时服务端不需要额外处理未完成的数据传输。 而在四次挥手关闭连接时,当客户端发送 FIN表示没有数据要发送后,服务端可能还有数据需要继续发送,所以不能立即发送 FIN 关闭连接,而是先回复 ACK 确认客户端的关闭请求。等服务端数据发送完毕,再发送 FIN 表示自己也可以关闭了。因此,关闭连接时需要多一次交互。
例如,在一个客户端与服务端进行实时通信的场景中,客户端发送完所有消息并想要关闭连接(第一次挥手),服务端收到请求后先回复 ACK
表示知道了(第二次挥手),但此时服务端可能还有未发送完的消息,等发送完后才发送 FIN 表示自己也准备关闭(第三次挥手),客户端最后回复
ACK 完成关闭(第四次挥手)。
2.http,协议,状态码。http1.0跟 http2.0 的区别,http跟https的区别
HTTP 协议
HTTP(HyperText Transfer Protocol,超文本传输协议)是用于在 Web
上传输数据的应用层协议。它定义了客户端和服务器之间如何进行请求和响应的交互。
HTTP状态码
200:成功 301:表示请求的资源已被永久移动到新的 URL,客户端应使用新的 URL 访问
302:表示请求的资源临时被移动到了其他位置,客户端应继续使用原有 URL 进行访问
400、401、403、404:(客户端)语法出错、需要认证、没有权限、找不到 500:(服务端)出错
301和302状态码区别
301 Moved Permanently(永久移动)
含义:表示请求的资源已被永久移动到新的 URL地址。搜索引擎等爬虫会将链接权重传递到新的 URL 。 缓存:浏览器和搜索引擎会记住这个重定向,下次访问相同的请求时会直接访问新的 URL ,不会再向原 URL 发送请求。
影响:对 SEO(搜索引擎优化)有较大影响,因为搜索引擎会认为原 URL 已失效,将权重转移到新的 URL 。
示例:如果一个网站的域名发生了永久性的变更,从 olddomain.com 变更为 newdomain.com ,服务器就会返回 301 状态码,并告知客户端新的域名地址。
302 Found(临时移动)
含义:表示请求的资源临时被移动到了另一个 URL 。
缓存:浏览器在接收到 302 响应时,不会自动将后续请求重定向到新的 URL,每次请求都会先访问原 URL ,由服务器再次返回 302 重定向到新的地址。
影响:对 SEO 的影响相对较小,搜索引擎不会将原 URL 的权重立即传递给新的 URL ,因为它认为这只是临时的重定向。
示例:在网站进行临时维护或活动期间,将某些页面临时重定向到其他页面时,可能会使用 302 状态码。
例如,假设一个电商网站进行系统升级,预计需要几个小时,在此期间,访问商品详情页可能会被 302 临时重定向到一个通知页面。而如果该电商网站决定永久关闭某个商品分类,并将相关页面永久重定向到新的分类页面,就会使用 301 状态码。
HTTP1和HTTP2的区别
- 多路复用:2支持多个请求和响应在一个连接上并发处理1每次都需要建立
- 请求头压缩:2头部信息压缩,减少传输的数据量二进制分帧:2将请求和响应分割为更小的帧,并进行二进制编码传输效率
- 服务器推送:服务器可以主动向客户端推送资源,而无需等待客户端请求。
HTTP和HTTPS的区别
- 安全:https通过ssl/tls协议对数据进行加密,保证了数据的机密性和完整性,防止数据盗取和篡改,http使用的是明文传输不安全
- 端口:http端口80,https端口443
- 证书:使用https时服务器需要配置数字证书来进行身份验证,而http则不需要
例如:在线支付场景可以使用https更为严谨,而一般的网站http就可以
3.当输入地址后浏览器做哪些操作
地址解析—建立tcp连接—发送http请求—接收服务器响应—处理响应—
显示页面—后续交互
-
地址解析
检查地址格式是否正确
如果使用的是域名,那就去DNC(域名系统)服务器发送求情,获取该域名对应的Ip地址,如果使用的Ip则直接拿IP简历TCP连接 -
建立TCP连接
浏览器通过获取IP地址和端口(http端口80,https端口443)去向服务段建立tcp连接(3次握手:客SYN-服‘发送建立请求’服SYN+ACK-客‘确认请求’客ACK-服‘收到服务端确认,建立连接’) -
发送HTTP请求
Tcp连接成功后浏览器会构建http请求报文,里面包括请求方式(post,get)、URL、请求头(包含各种信息、如接受的内容格式)和可能需要的请求体(例post请求需要传一些请求参数);将这个请求通过已建立的TCP发送给服务端 -
接受服务器响应
服务器接收到请求后,会处理请求并返回一个http响应
浏览器接收这个响应,并解析响应体和响应头 -
处理响应
如果响应状态为成功(如200),浏览器会根据响应头中的内容类型来处理响应体 -
显示页面
将从服务器获取的数据放在页面上并进行渲染,最后页面展示最新的内容 -
后续交互
如果页面上用其他资源(图片、css、js),浏览器会再次发送请求获取该些资源
例如,当您输入一个电商网站的地址,浏览器首先解析并获取服务器的 IP 地址,建立连接后发送请求获取首页的 HTML 文档。然后根据文档中的资源链接继续获取图片、样式表和脚本等,逐步构建并显示完整的页面供您浏览和交互。
4.TCP,UDP有什么区别
TCP(Transmission Control Protocol,传输控制协议)
UDP(User Datagram Protocol,用户数据报协议)是两种常见的传输层协议
它们有以下主要区别:
- 连接性:
TCP是面向连接的协议,在数据传输之前需要先建立连接,通过三次握手建立可靠的连接。
UDP是无连接的协议,不需要事先建立连接,直接发送数据 - 可靠性:
TCP提供可靠的数据传输,通过确认。重传、排序和流量控制机制,确保数据准确无误和按序到达
UDP不保证可靠的交付,可能出现丢包、乱序的情况 - 头部开销:
TCP头部开销比较大,通常20个字节
UDP头部开销比较小,通常8个字节 - 应用场景:
TCP适用于数据准确和可靠行要求较高的应用:文件传输、电子邮箱、远程登陆
UDP适用于对实时性要求比较高的如视频会议、直播、在线游戏、实施音频
5.Cors请求头
CORS(跨域资源共享)请求头是一组 HTTP 头字段
用于解决浏览器的同源策略限制,实现跨域请求。
当一个请求的 URL 的协议、域名、端口三者之间任意一个与当前页面地址不同时,就会产生跨域问题。为了允许跨域请求,服务器需要在响应中添加特定的 CORS请求头,告诉浏览器该请求可以跨域访问资源。
为了允许跨域请求,服务器需要在响应中添加特定的 CORS 请求头,告诉浏览器该请求可以跨域访问资源。
常见的 CORS 请求头包括:
Access-Control-Allow-Origin:指定允许访问该资源的源(Origin),可以是具体的域名(如http://example.com)或通配符*(表示允许任何源访问)。
Access-Control-Allow-Methods:指定允许的跨域请求方法,如GET、POST、PUT、DELETE等,多个方法之间用逗号分隔。
Access-Control-Allow-Headers:指定允许的跨域请求头字段,多个字段之间用逗号分隔。
Access-Control-Expose-Headers:指定客户端可以访问的额外响应头字段。
Access-Control-Max-Age:指定预检请求(Preflight request)的结果可以被缓存的时间(以秒为单位),在缓存有效期内,浏览器无需再次发送预检请求。
Access-Control-Allow-Credentials:当需要跨域发送 Cookie 或其他凭证信息时,将该字段设置为true。但需注意,如果设置了该字段为true,Access-Control-Allow-Origin不能设置为通配符*,而必须指定具体的域名。
6.Get,POST请求
- 数据传递
Get:挂在浏览器的地址栏上面
Post:将数据放在请求体中进行传递 - 数据长度受限
Get:受到url长度限制,不适合传大量的数据
Post:不受限制,可以传输大量数据 - 安全性
Get安全性差url上不适合传敏感信息post数据在请求体内相对更安全 - 用途
Get:常用于获取数据,如查询操作
Post:常用于提交数据,表单或上传文件等
7.CSRF,XSS攻击
CSRF(Cross-Site Request Forgery,跨站请求伪造)和
XSS(Cross-Site Scripting,跨站脚本攻击)是两种常见的 Web 安全攻击方式。
CSRF 攻击是指攻击者诱导受害者在已经登录的 Web 应用程序中执行他们事先精心构造的恶意请求。例如,攻击者可能构造一个恶意的链接或表单,当受害者在登录状态下点击或提交时,会在受害者不知情的情况下执行一些危险操作,如修改密码、转账等。
XSS 攻击则是指攻击者通过在目标网站注入恶意脚本代码,当其他用户访问该网站时,恶意脚本会在用户浏览器中执行,从而获取用户的敏感信息(如 Cookie、会话令牌等)、篡改页面内容或进行其他恶意操作。
为了防范这两种攻击,可以采取多种措施:
如验证请求来源、设置 CSRF 令牌、对用户输入进行严格的过滤和转义、设置 HttpOnly 属性防止 Cookie 被脚本获取等。
8.HTTP缓存
HTTP 缓存是一种用于提高 Web 应用性能和减少网络带宽使用的机制。
-
强缓存
通过 Expires 和 Cache-Control 响应头来控制。
Expires 指明资源的过期时间。
Cache-Control 可以设置更精确的缓存策略,如 max-age 表示资源在多少秒内有效。<meta http-equiv="Cache-Control" content="max-age=31536000" /> <meta http-equiv="Expires" content="Thu, 31 Dec 2030 23:59:59 GMT" />
当缓存未过期时,浏览器直接从本地缓存获取资源,不会向服务器发送请求。
-
协商缓存
服务器通过 Last-Modified 告知浏览器资源的最后修改时间,浏览器再次请求时通过 If-Modified-Since 带上这个时间,服务器对比判断资源是否有更新,如果没有则返回 304 状态码,告知浏览器使用本地缓存。ETag 是服务器为资源生成的唯一标识,浏览器下次请求通过 If-None-Match 带上这个标识,服务器对比判断,决定是否返回新的资源。
在前端页面中设置(通过 HTML 的 标签)例如,一个网站的图片设置了强缓存,有效期为 1 天。当用户在一天内再次访问该页面时,浏览器直接从缓存中读取图片,无需再次从服务器下载,大大提高了页面加载速度。
又比如,对于经常更新的网页内容,服务器可以使用协商缓存,根据资源的实际修改情况决定是否让浏览器使用缓存。
9.Options预检请求
在 HTTP 协议中,“OPTIONS”是一种请求方法。当客户端向服务器发送 OPTIONS 请求时,它通常用于获取服务器支持的 HTTP 方法和其他与请求/响应相关的选项信息。
例如,在进行跨域请求之前,浏览器可能会先发送 OPTIONS 请求来检查服务器是否允许特定的跨域请求方法和头信息。
JS篇
0-1. 数组方法
改变原数组:push、pop、shift、unshift、sort、splice、reverse
不改变原属组:concat、join、map、forEach、filter、slice
0-2. 高阶函数
高阶函数是至少满足下列一个条件的函数:接受一个或多个函数作为输入,或输出一个函数。
Array扩展:
forEach、map、reduce、filter、sort 、every 、some 、find 、findIndex
let arr = [68, 53, 12];
// forEach里面传的是函数(满足一个)
arr.forEach((item, index) => {
console.log(`第${index}个数是:${item}`);
})
// forEach里面传的是函数并且也有返回值(满足两个个)
const map = arr.map(function(i){
return i*i
});
0-3 script 中async和defer表示什么意思
async 和 defer 是两个属性,用于控制脚本的加载和执行方式
当 script标签设置了 async 属性时,脚本将异步加载,并且一旦下载完成,就会立即执行,不会阻塞页面的渲染。这意味着脚本的执行顺序是不确定的,可能会在页面的其他部分还在加载和渲染时就开始执行。
当 script 标签设置了 defer 属性时,脚本会异步加载,但会等到页面的解析(DOM 构建)完成后,按照它们在页面中出现的顺序依次执行。这意味着脚本不会阻塞页面的初始渲染,但执行顺序是有保证的。
0-4 Dom事件流的顺序?什么是事件委托?
当页面上的一个元素被点击时,先从document向下一层层捕获到该元素。然后再向上冒泡,一层层触发。
事件委托是将事件写在父级元素上,e.target是事件捕获时那个最小的元素,即选中的元素。所以可以根据e.target操作选中的元素。这样不需要给每个子元素绑定事件,代码更加简约。
0-5. break/continue/return的使用场景?
break:用于结束整个循环(for、while、do-while)
continue:用于结束循环中的当前层循环,进入下一层循环
return:用户结束整个函数
0-. 闭包是什么?利弊?如何解决弊端?使用场景?
场景:
事件处理、回调函数、递归、链式调用
闭包是什么:
JS中内层函数可以访问外层函数的变量,外层函数无法操作内存函数的变量的特性。我们把这个特性称作闭包。
闭包的好处:
- 隔离作用域,保护私有变量;有了闭包才有局部变量,要不然都是全局变量了。
- 让我们可以使用回调,操作其他函数内部;
- 变量长期驻扎在内存中,不会被内存回收机制回收,即延长变量的生命周期;
闭包的弊端:
内层函数引用外层函数变量,内层函数占用内存。如果不释放内存,过多时,易引起内存泄露。
解决办法:无法自动销户,就及时手动回收,使用后将函数的引用赋null。
1.事件循环机制(event loop)
事件循环(Event Loop)是 JavaScript 运行时环境用于处理异步操作的一种机制。
在 JavaScript 中,同步任务会在调用栈中依次执行,而异步任务会在合适的时机被放入任务队列中等待执行。
事件循环的主要工作流程如下:
- 首先,执行同步任务,这些任务会依次进入调用栈并执行,直到调用栈为空。
- 然后,事件循环会检查任务队列(通常包括宏任务队列和微任务队列)。
宏任务(MacroTask),例如 setTimeout 、 setInterval 、 IO 操作等,会被放入宏任务队列。
微任务(MicroTask),如 Promise.then 、 MutationObserver 等,会被放入微任务队列。
- 事件循环会先处理微任务队列中的所有任务,直到微任务队列为空。
- 接着,从宏任务队列中取出一个任务并执行。
- 执行完一个宏任务后,再次检查微任务队列,重复上述过程。
通过这种机制,JavaScript 能够在单线程环境中有效地处理异步操作,不会造成阻塞,从而保证了程序的响应性和性能。
2.Null跟underfine的区别
Null 和 Undefined 在 JavaScript 中有以下区别:
- 含义:
Null 表示一个被明确赋值为空的对象引用。
Undefined 表示一个变量未被初始化或者一个对象中不存在的属性。 - 类型:
typeof null 的结果是 “object” ,这是 JavaScript 中的一个历史遗留问题。
typeof undefined 的结果是 “undefined” 。
3.Promise
Promise 是 JavaScript 中处理异步操作的一种机制。
创建一个 Promise 对象通常需要传入一个执行器函数,该函数接受两个参数 resolve 和 reject ,用于改变 Promise 的状态
它具有以下特点和优势:
- 解决回调地狱:通过链式调用 then 方法,可以更清晰地组织异步操作的逻辑,避免多层嵌套的回调函数导致的代码可读性差和维护困难的问题。
- 明确的状态:Promise 有三种状态:pending(等待)、fulfilled(成功)和 rejected(失败)。一旦状态确定,就不可再改变。
- 错误处理:可以使用 catch 方法统一处理异步操作中的错误。
Promise.all([promise1,promise2]).then(res=>{}).catch(err=>{})
4.判断一个属性是对象还是数组
Array.isArray()+typeOf
5.New操作符做了哪些操作
- 创建一个新的空对象:这个对象将成为构造函数创建的实例。
- 将新创建的对象的 __ proto__ 属性指向构造函数的 prototype 对象,从而建立实例与构造函数原型之间的链接,实现了继承和方法共享。
- 将构造函数中的 this 绑定到新创建的对象上。
- 执行构造函数中的代码:这可能包括属性的初始化、方法的定义等操作。
如果构造函数有返回值,并且返回值是一个对象类型(不是原始类型),则返回这个对象,否则返回新创建的对象。
6.Node非阻塞i/o
非阻塞 I/O 意味着当 Node.js 发起一个 I/O操作(如文件读取、网络请求等)时,不会阻塞当前线程等待操作完成,而是继续执行后续的代码。当 I/O操作完成时,通过回调函数或事件通知来处理结果。提高高并发处理能力和充分利用系统资源(Web 服务器、实时聊天、文件上传下载)
缺点:编程复杂增加开销(多了回调函数)、错误内容处理棘手
7.commonJS跟requireJS和ES6的区别
- 加载方式:
CommonJS 是同步加载,代码执行到 require 语句时会阻塞等待模块加载完成。
RequireJS 是异步加载,不会阻塞后续代码的执行。
ES6 模块在编译时确定模块的依赖关系,在运行时异步加载。 - 模块导出:
CommonJS 主要通过 module.exports 或 exports 来导出。
RequireJS 中模块通过定义函数并返回一个对象来导出。
ES6 模块可以使用 export default 导出默认值,或者使用 export 关键字导出多个具名的变量、函数、类等。 - 模块导入:
CommonJS 使用 require 函数引入模块,返回的是模块导出的对象的副本。
RequireJS 使用 require 函数,通过配置模块的路径和依赖来加载。
ES6 模块使用 import 语句导入,导入的是只读的绑定,而非值的副本。 - 运行环境:
CommonJS 常用于服务器端,如 Node.js 。
RequireJS 主要用于浏览器端。
ES6 模块既可以用于服务器端,也可以用于现代浏览器端。 - 模块循环引用:
CommonJS 可以较好地处理模块的循环引用。
RequireJS 也能处理循环引用,但方式与 CommonJS 不同。
ES6 模块在编译阶段确定依赖,原则上不支持循环引用,如果存在会报错。
8.基本数据类型
Number/string/boolen/null/indefind/symbol
i基础数据类型修改对应的栈就重新生成
9.es6语法,箭头函数,新的操作方法等
模板字符串、结构、set、map、箭头函数、默认参数、for of、扩展运算、promise、es6model
10.dom0跟dom2结构
DOM0 和 DOM2 是文档对象模型的不同级别,它们在处理事件的方式上有所不同。
DOM0 点击事件
DOM2 可以监听点击事件并捕获各种阶段(捕获、目标、冒泡)
DOM2 级的事件处理方式相比 DOM0 级更加规范和灵活,能够更好地管理多个事件处理程序,并且支持事件的捕获和冒泡阶段。
11.bind跟apply,call的区别,实现方式
getting.call(obj,”vlaue1”)
getting.apply(obj,[”vlaue1”,”vlaue2”])
call、apply上面两个会立即执行函数
而const newFun = getting.bind(obj,”vlaue1”);
newFun ()是创建一个新的函数并改变其 this 指向和预设部分参数,新函数需要手动调用执行。
12.深拷贝,浅拷贝
浅拷贝(Object.assign()、扩展运算符)
浅拷贝创建一个新的对象或数组,但它只复制原始对象或数组的顶层属性或元素。如果原始对象或数组的属性值是引用类型(如对象或数组),则浅拷贝会复制引用而不是创建新的引用类型实例。
深拷贝(JSON.parse(JSON.stringify())、clonedeep)
深拷贝会递归地复制原始对象或数组的所有属性和子属性,创建完全独立的副本,原始对象的修改不会影响到深拷贝的结果。
14.跨域,同源策略
所谓“同源”,指的是两个 URL 的协议、域名和端口都相同。
同源策略主要限制了以下几种操作:
Cookie、LocalStorage 和 IndexedDB 等数据无法跨域读取。
DOM 无法跨域操作。
AJAX 请求无法跨域发送(除非服务器明确允许)。
跨域:当浏览器请求的资源与当前页面的源不同时,就产生了跨域请求。
常见的跨域解决方案有:
- JSONP:利用 script标签的跨域能力,通过动态创建 script 标签并指定回调函数来获取数据。
- CORS:服务器端设置响应头,允许特定源的请求访问。
- nignx代理:在同域的服务器端转发跨域请求,将获取到的数据返回给前端。
15.快速排序
快速排序(Quick Sort)是一种分治的排序算法。它的平均时间复杂度为O(nlogn) ,空间复杂度为O(logn),最坏时间复杂度为O(n方)
以下是快速排序的基本步骤:
- 选择一个基准元素(pivot),通常可以选择数组的第一个元素、最后一个元素或中间元素。
- 对数组进行分区(partition)操作:将小于基准的元素放在基准的左边,大于基准的元素放在基准的右边。分区操作完成后,基准元素处于最终排序后的正确位置。
- 对基准元素左边和右边的子数组分别递归地重复步骤 1 和 2,直到整个数组有序。
16.解释原型继承如何工作
每个函数都有一个 prototype 属性,当使用这个函数作为构造函数创建对象实例时,这些实例可以通过 __proto__指针访问构造函数的prototype 对象。当试图访问实例对象的某个属性或方法时,如果在本身找不到,JS引擎会沿着 __proto__链向上(原型对象)查找,如果在整个原型链中都没有找到该属性或方法,最终会返回undefined。这种机制可以让多个实例共享来自原型对象的方法和属性,可以在保持代码复用和灵活性的同时,实现对象之间的层次结构和功能共享。
17.解释 JavaScript 中“this”的工作原理
- 普通函数调用:在非严格模式下,this指向全局对象(在浏览器中为window,在 Node.js 中为global);在严格模式下,this会绑定到undefined。
- 对象方法调用:this指向调用该方法的对象。
- 构造函数调用:使用new关键字调用构造函数时,this指的是实例化的新对象。
- apply和call调用:通过这两个方法显式地指定this所指向的对象,即this指向参数中的对象。
- 箭头函数:箭头函数没有自己的this,而是继承外层函数的this。
- 定时器中调用:例如setInterval和setTimeout,this指向的是全局变量(在浏览器中为window)。
18、New Set()的用法
类似数组、成员唯一用于数组去重
add()
clear()
delete()
has()
forEach()
[…new set(arr)]
19、描述一下 async、await 的用法及使用场景
ayayn/await主要用于异步操作,将异步代码从视觉上看来比较同步化更加简洁易读。
用法
1. ayayn 将一个函数标示为一个异步函数,在异步函数中可以使用await等待异步操作
2. await 只能在async中使用,等待异步函数的操作完成,获取异步函数执行的结果
使用场景
1. 网络请求 (获取页面渲染的数据)
2. 数据库操作(等待查询或更新)
20、for in 与 for of
for-in 遍历对象 key 为对象的 key,遍历数组的话 key 为索引数;for-of 用来遍历数组,不能遍历普通对象
HTML,CSS篇
重绘和重排(回流/重构/重载)是什么?如何优化?
样式的调整会引起重绘,比如字体颜色、背景色调整等
Dom的变动会引起重排,比如定位改动、元素宽高调整
伪类和伪元素区别?
伪类本质上用于弥补常规css选择器的不足,因为如果没有我们可能需要多写一个class,所以叫伪类
.class:last-child{}
.class:first-child{}
a:link {color:green;}
a:visited {color:green;}
a:hover {color:red;}
a:active {color:yellow;}
伪元素本质上是创建了一个有内容的虚拟元素,如::before ::after。因为相当于多了一个元素/节点,所以叫为元素
// :before用于在某个元素之前插入某些内容。
// :after用于在某个元素之后插入某些内容。
css
p:before{
content:"Read this: ";
}
html:
<p>I live in Ducksburg</p>
页面展示:
Read this: I live in Ducksburg
F12看dom中:
before
Read this: I live in Ducksburg
1.h5新增属性
本地存储,比如localStorage、sessionStorage
语义化标签,如header、footer、nav等,使代码结构清晰,利于seo
canvas
svg
web worker,在主线程外再创建一个线程,可与主线程交互
拖放功能
2.Css3新增属性,常问flex,transform
transform: translate(50px, 50px) rotate(30deg) scale(1.5);
3.本地存储
- cookie:
常用于保存用户登录状态,每次请求都会携带(4k) - localStorage:
存储数据量比较大的数据,只要浏览器不清除就一直会在(5m) - sessionStorage:
用于同一会话区间保存临时数据,比如表单数据等,窗口关闭数据清空(5m)
4.Meta有哪些属性
标签位于文档的 部分;一个 标签只能用于一种用途,如果想要使用多种特性,需要添加多个 标签;
charset=“utf-8” 字符编译
viewport 用于设置视口
http-equiv 网络缓存
content-security-policy 防止跨站点脚本攻击
5.怎么实现移动端适配
- 使用 viewport 元标签
<meta
name="viewport"
content="width-device-width,
initial-scale=1.0, maximum-scale=1.0,
user-scalable=no">
width=device-width 视口宽度与设备宽度一致,不设置的话浏览器会默认给个较宽值可能在移动端就不友好
- 媒体查询(Media Queries)@Media screen
- 弹性布局(Flexbox)和网格布局(Grid)
- 相对单位(rem相对根元素、vh/vw相对视口)
- 图片适配 banckground-size:cover
6.水平垂直居中方式
框架篇
vue
0. v2/v3/v3.2 v-model实现方法
v2 通过 value和input(‘update:value’, $event")事件
v3 通过 modleValue和input(‘update:modelValue’, $event")
v3.2通过 @update:title=“value= $event”
1.Vue数据双向绑定原理
- vue2 原理是使用 es5 的 api:object.defineProperty()对数据进行劫持结合发布订阅模式的方式来实现。
- vue3 使用的是 es6 的 proxy 实现的,Proxy 用于创建一个数据代理,当属性被访问或修改时,触发 Proxy 拦截器,实现数据响应式。
在 Vue 2 中,双向数据绑定是利用 ES5 的一个
API,Object.defineProperty()对数据进行劫持结合发布订阅模式的方式来实现的。通过Object.defineProperty()来劫持数据对象的属性,对属性getter和setter进行拦截,当属性被访问或修改时,除非getter和setter,否则实现数据响应式。每个数据都有一个Watcher实例,当数据发生变化时,触发setter,并通知Watcher,然后Watcher会调用其函数,将值更新在DOM。
而 Vue 3 中的双向数据绑定则是基于 ES6 的Proxy实现的。Proxy用于创建一个数据代理,当属性被访问或修改时,触发Proxy拦截器,实现数据响应式。Proxy相较于Object.defineProperty()的优势在于它可以直接监听对象而非属性,直接监听数组的变化,并且拦截方式较多。Proxy返回一个新对象,可以只操作新对象达到目的,而Object.defineProperty()只能遍历对象属性直接修改。
2.路由实现原理
路由管理器,它的实现原理主要是通过改变浏览器的 URL 来实现不同页面(组件)的切换,同时在不重新请求页面的情况下更新视图。
在浏览器环境下,Vue Router 主要有两种实现模式:hash 模式和 history 模式。
- hash 模式:
URL 中带有#,#后面的内容作为路径地址使用。
hash 模式的实现是基于锚点和onhashchange事件。hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,它是用来指导浏览器动作的,对服务器端完全无用,改变 hash 不会重新加载页面,不会出现404。
每一次改变 hash(window.location.hash),都会在浏览器的访问历史中增加一个记录。 - history 模式:
通过 HTML5 的history.pushState()和history.replaceState()方法改变地址栏,只改变了地址栏,并不会真正跳转到真正的路径。
需监听popstate事件,当点击浏览器前进后退按钮,或者通过 JavaScript 触发back或者forward时才会触发。
需要服务器端进行相应配置以处理页面刷新时的 404 错误。
这种模式充分利用了 HTML5 History Interface 中新增的方法,使 URL 看起来更美观、更接近真实的路径,但需要服务器的支持。因为没有#号,当用户刷新页面之类的操作时,浏览器会给服务器发送请求,为避免出现这种情况,需要服务器将除了静态资源外所有路由都重定向到根页面。
传参
router 包含所有路由信息和方法
route 只包含当前路由信息<router-link :to="{ name:'router1',params: { id: status ,id2:status3},query: { queryId: status2 }}" > router-link跳转router1 </router-link> ```
区别:
URL 表现形式:
param:参数作为 URL 路径的一部分。 有**/**标志 this.$route.params
query:参数以 ?key=value 的形式附加在 URL 后面。有?标志 this.$route.query
刷新页面影响: param:页面刷新不会丢失参数。 query:页面刷新参数仍然存在。
3.Watch跟computed的区别
- computed(计算属性)
- 多对一 通过现有的数据计算出一个新的值,具有缓存性(当依赖变化会重新计算)
- watch(监听属性)
1. 一对多 监听某一个特定属性(对象的单个属性或整个对象)执行相应的回调函数,没有缓存,每次变化都会触发
- 区别就是computed侧重于数据派发;watch侧重于数据变化后对复杂逻辑的处理操作
4.Watch的配置项
Immdera 立即执行
Deep
5.为什么数组push,vue能跟踪到数据发生变化,用下标方式不行
Vue 使用 Object.defineProperty 把数据对象的属性转为 getter/setter,从而实现对数据变化的监听。然而,对于数组,Vue 不能检测通过索引直接设置数组项和修改数组长度这两种变动。
当使用 push 等方法时,这些方法是 Vue 经过特殊处理的,它们会触发 Vue 的更新机制,从而通知视图进行更新。例如 push 方法向数组末尾添加元素,会改变数组的内容,并且 Vue 能够感知到这种变化。
6.怎么实现一个组件的封装
创建 — 引入 — 注册 — 使用
v-model的原理 (v-model其实是个语法糖)
在 Vue 中,v-model 是一个用于实现表单元素和组件数据双向绑定的指令。 其原理主要包括以下几个方面: 对于原生的表单元素(如 input、textarea、select 等): v-model 实际上是 value 属性和 input 事件的语法糖。
当用户输入内容时,会触发元素的 input 事件,Vue 会通过这个事件来更新组件实例中的数据。
同时,组件实例中的数据变化也会同步更新到元素的 value 属性上,从而实现双向绑定。
对于自定义组件: 父组件使用 v-model时,子组件需要接收一个名为 value 的 prop,并触发一个名为 input 的自定义事件来更新父组件传递的值。
7.MVVM模式的核心
通过数据绑定和 ViewModel 实现视图和模型的高效交互和低耦合,从而提高应用程序的开发效率和可维护性。
8.Provider跟inject
Provider 和 inject 是 Vue 中用于实现依赖注入的两个概念。它们的主要作用是允许祖先组件向其所有子孙后代组件传递数据或方法,而不论组件之间的嵌套层次有多深。
在 Vue 2 和 Vue 3 中都可以使用 provide 和 inject,但具体的使用方式略有不同。
9.Keep-alive及其生命周期
activated:在组件被激活时调用
deactivated:在组件被停用时调用
需要注意的是,使用 keep-alive 后,原本的 beforeDestroy 和 destroyed 钩子函数将不会再被触发,因为组件不会被真正销毁,只是被缓存起来了。
另外,keep-alive 组件还支持通过 include、exclude 属性或动态判断来有条件地缓存组件。
keep-alive 常用于需要频繁切换但又希望保持状态的组件,比如列表页、详情页等,以减少服务器请求次数和提高用户体验
10.不想让vue中data里的一个对象里所以属性都具有setter,getter,怎么解决?(freeze)
Object.freeze()
11.Slot
- 匿名插槽 (默认插槽)
子组件中没有指定名称的slot标签
父组件传递的未具名内容会填充到默认插槽中 - 具名
子组件中使用 name 属性为插槽命名。
父组件使用 template 标签并通过 v-slot:插槽名 来传递对应名称的内容。
// 子组件
<slot name="header">默认的头部内容</slot>
// 父组件
<template v-slot:header>
<p>父组件传递的头部内容</p>
</template>
- 作用域
子组件向插槽传递数据。
父组件通过接收这些数据来决定如何渲染插槽内容
// 子组件
<slot :user="user">
<p>默认用户信息:未知</p>
</slot>
data() {
return {
user: { name: '张三', age: 25 }
};
}
// 父组件
<template v-slot="{ user }">
<p>用户信息:{{ user.name }},年龄:{{ user.age }}</p>
</template>
12、Vuex 有哪种属性及作用
vue2+vue3都可使用vuex
- state: 存放共享的数据属性
- mutations: 更改 state 中的状态()同步操作
- actions: 异步操作 获取数据提交 mutations 来间接更新 state 数据
- getters: 类似于计算属性,对 stated 的数据进行计算
- model: 模块设置,每个模块有自己对于的 vuex 属性
vue3 新增了一个pinia
- state 存放共享属性
- actions 可支持同步和异步
- getters(可选):用于计算派生状态。
- persist(可选):用于配置状态的持久化选项。
13.计算属性和监听的执行顺序
- 默认 beforeCreate -> props -> data -> computed -> created -> mounted -> methods
- 监听设置立即执行 beforeCreate -> props -> data -> watch -> computed -> created -> mounted -> methods
- 触发某一事件
Methods -> watch -> computed
14.以下是 Vue3 与 Vue2 在一些方面的比较:
v2的mixin与v3的hook
- 相同点
目的相同:Vue 2 的 Mixin 和 Vue 3 的 Hook 都旨在实现代码的复用和逻辑的共享。 - 不同点
语法和实现方式
Mixin:在 Vue 2 中,通过将多个对象的属性和方法合并到组件中来实现复用。
Hook:在 Vue 3 中,通过函数来封装和返回有状态的逻辑。
数据响应式处理
Mixin:对于响应式数据的处理可能相对复杂,容易出现一些意外情况。
Hook:基于 Vue 3 改进的响应式系统,处理响应式更加可靠和高效。
代码组织和可读性
Mixin:多个 Mixin 混合使用时,可能导致代码结构混乱,难以清晰地理解数据和方法的来源。
Hook:函数式的风格使得代码的组织更加清晰,逻辑更加内聚。
类型支持
Hook:在 TypeScript 中的类型支持更好,更容易进行类型推导和类型检查。
响应式原理:
- Vue2:通过 Object.defineProperty 对 data 中的属性进行劫持来实现响应式。但存在无法监听数组变化、需通过特殊方法实现,以及无法监听对象新增和删除属性等限制。
- Vue3:使用 Proxy 代替 Object.defineProperty,可以监听到对象的所有属性,包括新增和删除操作,还支持数组的监听,并且使用 WeakMap 存储依赖关系,避免了内存泄漏问题。
性能提升:
- Vue3 在响应式处理以及重写 diff 算法等内部实现上进行了大量优化,渲染速度更快,性能更好,内存占用更少。例如采用静态提升技术,对不参与更新的元素做静态提升,只会被创建一次,在渲染时直接复用,优化内存。在 diff 算法中增加了静态标记,采用同序列比对和最长递归子序列算法计算最小修改偏移量。
- Vue3 还支持更好的 Tree Shaking,通过模块拆分和函数式编程,实现按需引入,减少打包体积;同时对源码体积进行了优化,移除了一些非必要特性,新增模块也可按需引入。
编码方式:
- Vue3 引入了 Composition API(组合式 API),允许开发者更好地组织和复用逻辑代码,提高代码的可维护性。可以根据逻辑相关性组织代码,代码量少,更灵活,语法提示友好,且对 TypeScript 的支持更好。
- Vue2 使用 Options API,组件的逻辑分散在不同的选项中(如 data、methods、computed 等)。
设计理念:
- Vue3 更注重模块的拆分,模块之间耦合度低,可独立使用部分模块,如只引入 reactive 模块来使用响应式处理。对所有 API 进行重写,采用函数式编程,支持更好的 Tree Shaking,精确实现按需引入,减少打包体积。
- Vue2 模块的独立性相对较弱。
特性支持:
- Vue3 支持更多特性,如片段、Teleport、Suspense 等。
- Vue2 相对较少。
生命周期:
Vue3 中的生命周期函数有所变化,例如 beforeCreate 和 created 变为 setup,beforeDestroy 改名为 beforeUnmount,destroyed 改名为 unmounted 等。并且 Vue3 的生命周期函数可以多次调用而不会覆盖。
全局方法定义:
- Vue2 中直接在 Vue 实例上挂载全局方法。
- Vue3 中通过创建的应用实例来定义全局方法。
template 根组件:
- Vue2 有且只有一个根组件。
- Vue3 没有固定的根节点,可以有多个根。
启动方式:
1. Vue2:new Vue({ store, router, render: h => h(App) }).$mount("#app")
2. Vue3:import { createApp } from 'Vue'; createApp(App).use(store).use(router).mount("#App")
路由和状态管理:
- Vue3 的路由创建方式和 Vue2 有所不同。在 Vue2 中创建路由对象实例通过 new Router,而 Vue3 是通过 import createRouter 这个函数来构建。
- Vue3 中的状态管理库 Pinia 与 Vue2 中的 Vuex 也有一些区别,Pinia 去掉了 mutations 和 modules,modules 在 Pinia 中是自带的,不需要手动去写。
computed 计算属性和 watch 监听:
- Vue2 中的 computed 写法是在对象中定义方法,而 Vue3 中需引入 computed,写法是一个函数,需返回结果。
- Vue3 中的 watch 监听写法也有所不同,第三个参数可选。对于 ref 数据的监听不需要加 .value,且能监听多个值的变化。而 ref 生成的响应式对象默认不支持深度监听,需加上 deep: true,reactive 生成的响应式对象则默认为深度监听。
props 处理:
- Vue2 中接收 props 是对象时,可添加 default、required、type 等属性。而 Vue3 中 props 是只读的,不能在 setup 函数中修改,如需修改可使用响应式对象或 ref。
微信小程序
1. 登陆顺序
- 调用 wx.login() 接口获取临时登录凭证 code
- 拿code向后端获取token
- 后端通过code去向微信端获取sessionKey和openId后向前端返回登录态也就是token
react
1.Hooks,高阶组件
- useState:用于在函数组件中添加状态,可跟踪随时间变化的数据。它返回一个状态值和更新该状态值的函数。
- useEffect:用于执行副作用操作,如数据获取、订阅或手动更改 DOM 等。类似于类组件中的componentDidMount、componentDidUpdate和componentWillUnmount生命周期。(没有依赖项初始+更新、依赖为空初始、有依赖初始+值变)
- useRef:用于创建对 DOM 元素或值的引用,可以在渲染之间保持状态。
- useMemo:用于对计算结果进行记忆,避免在每次渲染时重复计算。只有当依赖项发生变化时,才会重新计算结果。
- useReducer:用于更复杂的状态逻辑,它接收一个 reducer 函数和初始状态,然后返回当前的状态和派发 action 的 dispatch 函数。
- uesContet用于访问 React Context 在组件树中传递的数据,而不必通过每个组件传递 props
- useLayoutEffect与 useEffect 类似,但它在所有的 DOM 变更之后同步执行。这在需要读取 DOM 布局并同步触发重渲染时非常有用。
- useDebugValue用于在 React 开发者工具中显示自定义 Hook 的标签
- useCallback:用于返回一个记忆化版本的回调函数,防止不必要的渲染。它会在依赖项没有变化时返回上次缓存的函数。
- useFetch(非 React 内置 Hook):例如react-fetch-hook,用于便捷地使用fetch API进行数据获取。
- useWindowScrollPosition(非 React 内置 Hook):用于获取窗口的滚动位置。
- useLocalStorage(非 React 内置 Hook)用于同步本地存储的数据
总的来说,useState 关注状态的管理和组件的重新渲染,useEffect 专注于副作用的处理,useRef 则主要用于引用和保存跨渲染的数据。
2.Redux中间件原理
在 Redux 中,数据的更新流程通常是:
action 被派发 -> reducer 根据 action 计算新的 state -> 新的state 更新到 store 中,从而触发视图的更新。
中间件位于 action 被派发和 reducer 处理之间。它本质上是一个函数,接收 Redux 里 store 的 dispatch 和 getState 方法作为参数,并返回一个新的函数。这个新的函数会接收下一个中间件或者最终的 reducer 作为参数。
3.Redux异步操作
Redux Thunk:
原理:Redux Thunk 是一个中间件,它允许 action 创建函数返回一个函数而不是一个对象。返回的函数接收 dispatch 和 getState 方法作为参数,从而可以在函数内部进行异步操作,并在操作完成后使用 dispatch 派发相应的同步 action 来更新状态。
vue、react和小程序之间的区别
1.Diff算法,react跟vue的diff区别
Diff 算法是一种用于比较两个数据结构差异的算法,主要用于高效地更新虚拟 DOM 以最小化对真实 DOM 的操作,从而提高性能.
React 的 Diff 算法主要基于两个假设:
两个不同类型的元素会产生完全不同的树。 对于同一层级的一组子节点,通过 key 来标识节点的唯一性。
当对比两棵树时,如果节点类型不同,直接删除旧的节点并创建新的节点。对于同一类型的节点,则比较属性的变化来更新 DOM。
Vue 的 Diff 算法
在更新节点时,会更加精细化。在处理同层级节点对比时,会遵循以下策略: 新老节点数组的开始和结束位置设置指针进行比较。 如果节点有 key,会根据 key 进行更准确的匹配和复用。
当新节点多或者老节点多时,会通过创建或删除节点来达到最终的一致性。
例如,如果在 Vue 中一个列表的顺序发生了变化,并且节点有 key ,那么 Vue 可以更准确地移动节点而不是删除和重新创建。
2.React、vue生命周期,要展开来说
react:
- Mounted挂载
Constract(初始化state)
stateFromProps
Render(计算返回虚拟dom|挂挂在和更新都会用)
ComponetDidMout(获取数据) - 更新updating:父组件重新渲染、 setState或forceUpdate触发
前两个是因为父组件重新渲染更新的
stateFromProps
Render()
shouldComponetUpdate
ComputedDidUpdata - 卸载Unmouting
cnmponetWillUnmount(清理资源,取消定时器)
vue3:
{ setup, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted }
vue2
父前三个-子前四个-父结尾
3.小程序原生父子传值
父传子正常传 子properties收
子传父taggerEvent(“fun”,value) +父bindFun+this.selectComponent(“类名”)
4.Vue,react,小程序组件间的通信
Vue:
- 导航+编程
- Query 参数
- params路由参数
- meta
React:
- 导航+编程
- props.params 方法
- query 方法:类似于表单中的 get 方法,传递参数为明文,但刷新页面参数会丢失。
小程序:
- 使用 navigator 的 url 带参传值,利用 getCurrentPages 进行页面传值,
- 小程序中还可以使用 wx.navigateTo、wx.redirectTo 等方法进行路由跳转并传值,它们与 navigator 的对应关系如下:
- navigator 的 open-type=“navigate”(不写默认值是 navigate)等于 wx.navigateTo:保留当前页面,跳转到应用内的某个页面,但不能跳到 tabbar 页面,小程序中页面栈最多十层,可带参。
- wx.redirectTo:关闭当前页面,跳转到应用内的某个页面,但不允许跳转到 tabbar 页面,可带参。
- wx.switchTab:跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面,不可带参。
- wx.reLaunch:关闭所有页面,打开到应用内的某个页面,可带参。
- wx.navigateBack:关闭当前页面,返回上一页面或多级页面
5.Vue跟react的优缺点
Vue 和 React 都是非常流行的前端框架,它们各自有一些优点和缺点:
Vue 的优点:
- 学习曲线相对较平缓容易理解和上手。
- 模板语法简洁:Vue 的模板语法直观,与 HTML 结构相似,使得开发者可以更自然地编写视图。
- 轻量级:Vue 的核心库相对较小,性能开销较低。
- 渐进式框架:可以逐步将 Vue 引入现有的项目,无需一次性进行大规模重构。
- 中文文档丰富:对国内开发者来说,查找资料和解决问题更加方便。
Vue 的缺点: - 生态系统相对较小:与 React 相比,一些特定领域的库和工具可能相对较少。
- 大型项目的可扩展性:在处理超大型复杂应用时,可能在架构和扩展方面面临一些挑战。
React 的优点:
- 强大的生态系统:拥有大量的高质量第三方库和工具,几乎可以满足任何需求。
- 虚拟 DOM 优化:高效的虚拟 DOM 算法,使得性能优化更具可控性。
- 社区活跃:拥有庞大且活跃的社区,技术更新和支持力度大。
- 适用于大型应用:架构灵活,在大型项目中更容易进行代码组织和管理。
React 的缺点:
- 学习曲线陡峭:需要掌握较多的概念和技术,如 JSX、函数式编程等。
- 代码复杂度较高:尤其是在处理复杂的状态管理时。
- 原生模板语法不够直观:JSX 对于一些开发者来说可能不太习惯。
需要注意的是,选择 Vue 还是 React 取决于具体的项目需求、团队技术栈和个人偏好。在实际开发中,两者都能构建出高质量的应用程序。
Git
rebase
1.在基于某个分支进行开发时,如果上游分支有新的提交,使用 rebase 可以将本地的提交重新应用到更新后的上游分支顶端,而不是直接合并。