前端面试题汇总(持续更新...)

文章目录

1.bind、call、apply 区别?如何实现一个bind?

Apply,call,bind三者都可以改变函数的this指向,三者的第一个参数都是this要指向的对象,如果没有这个参数或者参数为undefined或者null,则默认指向全局window,三者都可以传参,但是apply是数组,而call是参数列表,而apply和call是一次性传入参数,而bind可以分为多次传入,bind是返回绑定this之后的函数,apply,call是立即执行。
实现一个bind可以分成三个步骤:
1.修改this指向
2.动态传递参数
3.兼容new关键字

2.什么是防抖和节流?有什么区别?如何实现?

本质上是优化高频率执行代码的一种手段,
节流:n秒内只运行一次,如果在n秒内重复触发,只有一次生效
防抖:n秒后再执行该事件,如果在n秒内被重复触发,则重新计时
节流:完成节流可以使用时间戳和定时器的写法,使用时间戳写法,事件会立即执行,停止触发之后没有办法再次执行,使用定时器写法,在定时之后第一次执行,第二次事件停止触发之后依然会再一次执行,也可以将时间戳写法的特性和定时器写法的特性相结合,实现一个更加精确的节流
防抖:防抖也可以通过定时器实现,在一段连续操作结束后,处理回调,利用clearTimeout和setTimeout实现

3.怎么理解回流跟重绘?什么场景下会触发?

在html中,每个元素都可以理解成一个盒子,在浏览器解析过程中,会涉及到回流与重绘,
回流:布局引擎会根据各种样式计算每个盒子在页面上的大小与位置
重绘:当计算好盒模型的位置,大小以及其他属性之后,浏览器根据每个盒子的特性进行绘制
有回流一定有重绘,有重绘不一定有回流
回流这一阶段主要是计算节点的位置和几何信息,那么当页面布局和几何信息发生变化的时候,就需要回流,如下面情况:
1.添加或者删除可见的DOM元素
2.元素的位置发生变化
3.元素的尺寸发生变化(包括外边距,内边框,边框大小,高度和宽度等)
4.内容发生变化,比如文本变化或者图片被另一个不同尺寸的图片所替代
5.页面一开始渲染的时候
6.浏览器的窗口尺寸发生变化
7.需要通过计算得到的属性,浏览器为了获取这些值会进行回流
触发回流一定会触发重绘,除此之外还有一些引起重绘的行为:
1.颜色修改
2.文本方向的修改
3.阴影的修改

4.VUE路由的原理?

vue路由是通过vue router来实现的,它是vue官方提供的一种路由管理方式,vue router的原理是通过监听url的变化来实现页面的切换,当url发生变化时,vue router会根据url匹配到对应的路由,然后渲染相应的组件,具体来说,vue router的实现原理包括以下几个方面:
1.路由配置:vue router需要在应用程序中配置路由,包括路由的路径和对应的组件
2.路由匹配:当url发生变化时,vue router会根据路由配置进行匹配,找到与当前url匹配的路由
3.组件渲染:当找到匹配的路由时,vue router会根据路由配置中指定的组件来渲染页面
4.导航守卫:vue router支持通过导航守卫来控制路由的跳转,可以在路由跳转之前,路由跳转之后,路由更新前等不同阶段进行拦截和处理

在实际开发中,可以使用vue router来实现单页面应用程序SPA,通过路由的切换来实现页面之间的跳转和刷新,vue router还提供了丰富的API和扩展功能,可以满足不同的开发需求

5.你了解vue的diff算法吗?说说看?

diff算法是一种通过同层的树节点进行比较的高效算法,有两个特点:比较只会在同层级中进行,不会跨层级进行比较,在diff比较的过程中,循环从两边向中间比较,diff算法在很多场景下都有应用,在vue中,作用于虚拟dom渲染成真实dom的新旧节点比较

6.说说你对keep-alive的理解?

Keep-alive是vue内置的一个抽象组件,用于缓存路由组件或者其他组件的状态,通过Keep-alive组件,可以将组件状态缓存到内存中,避免组件的反复创建和销毁,从而提高应用程序的性能,具体来说,Keep-alive组件可以缓存有状态的组件,例如包含表单数据,滚动位置等状态的组件,当这些组件被缓存后,下次再使用时,就可以直接从内存中读取组件的状态,而不需要重新创建和初始化组件,
使用Keep-alive组件时,需要注意一下几点:
Keep-alive组件是一个抽象组件,不能直接使用,需要使用其内置的include和exclude属性来指定需要缓存的组件,缓存的组件会被包裹在一个标签中,并且需要设置一个唯一的key属性来标识缓存的组件。当缓存的组件被激活时,会触发activated生命周期钩子函数,当缓存的组件被停用时,会触发deactivated生命周期钩子函数。在使用时需要注意缓存的组件状态可能会影响应用程序的性能和内存占用,需要根据实际情况进行优化和控制,在实际开发中,可以使用Keep-alive组件来提高应用程序的性能和用户体验,避免因组件的反复创建和销毁而导致应用程序的卡顿和加载时间的延长

7.什么是响应式设计?响应式设计的基本原理是什么?如何做?

响应式设计是一种设计理念,通过灵活的网页设计,使网页在各种设备上呈现出最佳的用户体验,响应式设计的基本原理是使用css3媒体查询技术,根据设备的视口大小和方向等特性,自适应的调整网页的布局,字体,图片和其他元素,使其在不同的设备上呈现出最佳的效果。响应式设计的优点如下:
1.提高用户体验:响应式设计可以让网页在不同的设备上呈现出最佳的效果,从而提高用户的满意度和体验
2.节省开发成本:响应式设计可以减少开发成本,因为只需要开发一个网页即可兼容不同的设备。
3.提高SEO:响应式设计可以提高网站的搜索引擎排名,因为搜索引擎更倾向于优先展示响应式网站的搜索结果。
响应式设计的实现方法如下:
1.使用css3媒体查询:根据设备的屏幕大小,分辨率,方向等特性,使用css3媒体查询技术来调整网页的布局和样式。
2.使用流式布局:使用百分比和em等相对单位来布局网页,从而使网页在不同设备上具有相同的比例。
3.图片优化:使用响应式图片来适应不同设备的分辨率,或者使用图片压缩等技术来减小网页的加载时间。
4.使用视口:使用视口来控制网页的缩放比例和布局,从而使网页在不同设备上呈现出最佳的效果
总之,响应式设计是一种非常重要的设计理念,可以提高网页的兼容性,性能和用户体验,在实际开发中,需要根据具体情况灵活运用响应式设计的方法和技术,为用户提供更好的体验

8.说说 React 生命周期有哪些不同阶段?每个阶段对应的方法是?

挂载阶段(Mounting):已插入真实的Dom阶段

更新阶段(Updating):正在被重新渲染的阶段

卸载阶段(Unmounting):已移出真是dom阶段

挂载阶段:
	constructor()类组件实例化后触发的方法,
	componentWillMount 组件挂载完成之前触发的方法,
	render()每次组件渲染的时候都会触发的方法
	componentDidMount()组件渲染完成触发的方法
更新阶段:
	componentWillReceivedProps()父组件传递的数据发生变化的时候触发的生命周期函数,
	如果父组件的数据改变频繁的话,性能消耗会比较大
	shouldComponentUpdate 组件是否应该更新,返回值是一个布尔值,true,触发render,false不会触发render方法,
	判断state的值和props的值是否发生改变,return true更新
	componentWillUpdate 组件更新前触发的方法
	render()渲染视图触发的方法
	componentDidUpdate 组件更新完成之后触发的方法
销毁阶段:
	componentWillUnmount 组件销毁阶段的内容

9.如何解决跨域问题?

通过jsonp跨域
document.domain + iframe跨域
location.hash + iframe
window.name + iframe跨域
postMessage跨域
跨域资源共享(CORS)
nginx代理跨域
nodejs中间件代理跨域
WebSocket协议跨域

jsonp

jsonp的原理就是利用了script标签不受浏览器同源策略的限制,然后和后端一起配合来解决跨域问题的。
具体的实现就是在客户端创建一个script标签,然后把请求后端的接口拼接一个回调函数名称作为参数传给后端,并且赋值给script标签的src属性,然后把script标签添加到body中,当后端接收到客户端的请求时,会解析得到回调函数名称,然后把数据和回调函数名称拼接成函数调用的形式返回,客户端解析后会调用定义好的回调函数,然后在回调函数中就可以获取到后端返回的数据了。

cors

cors是跨域资源共享,是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它 origin(域,协议和端口),使得浏览器允许这些 origin 访问加载自己的资源。服务端设置了Access-Control-Allow-Origin就开启了CORS,所以这种方式只要后端实现了CORS,就解决跨域问题,前端不需要配置。

搭建Node代理服务器解决跨域

因为同源策略是浏览器限制的,所以服务端请求服务器是不受浏览器同源策略的限制的,因此我们可以搭建一个自己的node服务器来代理访问服务器。
大概的流程就是:我们在客户端请求自己的node代理服务器,然后在node代理服务器中转发客户端的请求访问服务器,服务器处理请求后给代理服务器响应数据,然后在代理服务器中把服务器响应的数据再返回给客户端。客户端和自己搭建的代理服务器之间也存在跨域问题,所以需要在代理服务器中设置CORS。

Nginx反向代理解决跨域

nginx通过反向代理解决跨域也是利用了服务器请求服务器不受浏览器同源策略的限制实现的。

postMessage方式解决跨域

window.postMessage() 方法可以安全地实现跨源通信,此方法一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。

主要的用途是实现多窗口,多文档之间通信:

页面和其打开的新窗口的数据传递
多窗口之间消息传递
页面与嵌套的 iframe 消息传递

Websocket方式解决跨域

使用Websocket也可以解决跨域问题,因为WebSocket本身不存在跨域问题,所以我们可以利用webSocket来进行非同源之间的通信,
WebSocket 规范定义了一个在 Web 浏览器和服务器之间建立“套接字”连接的 API。 简单来说:客户端和服务器之间存在持久连接,双方可以随时开始发送数据。

10.如何优化webpack打包速度?

Webpack是一款强大的模块打包工具,但在处理大型项目时,由于需要处理大量的文件和依赖关系,可能会导致打包速度变慢。以下是几种优化Webpack打包速度的方法:
使用DllPlugin和DllReferencePlugin
DllPlugin和DllReferencePlugin是Webpack内置的插件,可以将第三方库和框架打包成单独的文件,并缓存起来。使用DllPlugin和DllReferencePlugin可以避免每次打包时都重新编译第三方库和框架,从而提高打包速度。
使用HappyPack
HappyPack是一个Webpack插件,可以将任务分解成多个子进程并行处理,从而提高打包速度。通过使用HappyPack,可以将Webpack的loader和插件的处理过程放在子进程中执行,从而避免Webpack的单线程限制。
使用Tree Shaking
Tree Shaking是Webpack 2.x版本及以上的一个新特性,它可以通过静态分析代码的引用关系来删除未引用的代码。通过使用Tree Shaking,可以避免将未使用的代码打包到最终的输出文件中,从而减小文件体积,提高打包速度。
使用Code Splitting
Code Splitting是Webpack的另一个优化打包速度的方法,它可以将代码分割成多个模块,按需加载。通过使用Code Splitting,可以将应用程序中的代码分割成多个小模块,从而提高文件的加载速度和执行效率。
合理使用缓存
缓存是提高Webpack打包速度的另一个重要因素。可以通过使用cache-loader和hard-source-webpack-plugin等插件来实现缓存。使用缓存可以避免每次重新编译和打包代码,从而提高打包速度。
需要注意的是,以上方法并非适用于所有的项目和场景,需要根据实际情况进行选择和使用。同时,还需要合理配置Webpack的loader和插件,避免不必要的文件解析和处理,从而提高打包速度。

11.SPA首屏加载速度慢的怎么解决?

SPA(Single Page Application)是一种基于 AJAX 和 HTML5 技术的应用程序,通常只有一个页面,通过动态加载数据和页面来实现用户交互。由于 SPA 只需要加载一次页面,因此可以提高用户的体验和性能。但是,SPA 的首屏加载速度慢是一个普遍存在的问题,下面是一些解决方案:
代码优化:可以通过压缩、合并、懒加载等优化技术来减小代码的体积,从而提高加载速度。
图片优化:优化图片大小、格式、质量等参数,可以减少图片加载时间,从而提高页面的加载速度。
缓存静态资源:使用缓存技术来缓存静态资源,例如使用 HTTP 缓存、浏览器缓存、CDN 缓存等技术,可以减少网络请求和加载时间。
服务器端渲染(SSR):使用服务器端渲染技术可以在服务器端生成 HTML 页面,从而提高首屏加载速度。
骨架屏(Skeleton Screen):在页面加载时,先显示一个简单的骨架屏,然后再加载真实内容,从而提高用户的体验。
使用第三方库或框架:使用一些优秀的第三方库或框架,例如 React、Vue、Angular 等,可以提高开发效率和性能。
总之,针对 SPA 首屏加载速度慢的问题,需要综合运用上述的优化技术和解决方案,才能够提高页面的加载速度和用户体验。

12.说说react router有几种模式?实现原理?

React Router 是一个基于 React 的路由库,用于实现单页应用程序(SPA)中的路由功能。React Router 有三种路由模式:HashRouter、BrowserRouter 和 MemoryRouter。
HashRouter
HashRouter 使用 URL 的 hash 值来作为路由信息,例如:http://example.com/#/path/to/page。HashRouter 的实现原理是监听 URL 的 hash 值变化,然后根据 hash 值来匹配对应的路由信息,从而加载相应的组件。
BrowserRouter
BrowserRouter 使用 HTML5 History API 来管理路由信息,例如:http://example.com/path/to/page。BrowserRouter 的实现原理是使用 History API 来监听 URL 的变化,然后根据 URL 来匹配对应的路由信息,从而加载相应的组件。BrowserRouter 需要后端服务器的支持,因为它需要在服务器上配置一些规则,以便在用户请求时正确地返回页面。
MemoryRouter
MemoryRouter 是一种不需要使用 URL 来管理路由信息的路由模式,它使用内存中的数据来管理路由信息,因此不会影响 URL 的变化。MemoryRouter 的实现原理是使用内存中的数据来匹配路由信息,然后根据路由信息来加载相应的组件。
不同的路由模式适用于不同的场景,例如:如果需要支持浏览器前进后退功能,可以使用 BrowserRouter;如果不需要改变 URL,可以使用 MemoryRouter;如果需要在旧浏览器中支持路由功能,可以使用 HashRouter。
React Router 的实现原理是通过监听 URL 变化,然后根据 URL 匹配对应的路由信息,从而加载相应的组件。React Router 使用一些核心组件,例如 Route、Switch、Link、Redirect 等,来实现路由功能。在使用 React Router 时,需要先定义路由信息,然后将路由信息与组件进行关联,最后通过 Link 组件来实现页面的跳转。

13.从浏览器地址栏输入url到显示页面的步骤?

从浏览器地址栏输入URL到显示页面的过程可以分为以下几个步骤:
DNS解析
当用户在浏览器中输入URL后,浏览器首先会检查本地缓存中是否存在该域名的DNS解析结果。如果本地缓存中不存在,则会向本地DNS服务器发起请求,获取域名的IP地址。
建立TCP连接
在获取到域名的IP地址后,浏览器会根据该IP地址和端口号向服务器发起TCP连接请求。在建立TCP连接时,会进行三次握手,确认双方可以进行通信。
发送HTTP请求
在建立TCP连接后,浏览器会向服务器发送HTTP请求。在发送HTTP请求时,会包含请求头、请求方法、请求参数等信息。
服务器处理请求并返回响应
服务器在接收到HTTP请求后,会根据请求的信息进行处理,并返回响应结果。在返回响应时,会包含响应头、响应状态码、响应体等信息。
浏览器解析和渲染页面
在接收到服务器返回的响应结果后,浏览器会进行页面解析和渲染。在页面解析和渲染时,会根据HTML、CSS、JavaScript等内容来构建页面并显示。
关闭TCP连接
在页面显示完成后,浏览器会关闭TCP连接,释放资源。
需要注意的是,以上步骤并非一成不变,可能会因不同的网络环境、浏览器类型和服务器配置等因素而略有不同。但总体来说,以上步骤反映了从浏览器地址栏输入URL到显示页面的大致过程。

14.说说JavaScript中的数据类型?存储上的差别?

JavaScript 中有七种基本数据类型和一种复杂数据类型,具体如下:
基本数据类型:
Number:表示数字,包括整数和浮点数。
String:表示字符串,使用单引号、双引号或反引号来表示。
Boolean:表示布尔值,只有 true 和 false 两个取值。
Null:表示空值,只有一个取值 null。
Undefined:表示未定义值,只有一个取值 undefined。
Symbol:表示唯一的标识符,用于对象的属性名。
BigInt:表示任意精度的整数,用于处理大整数。
复杂数据类型:
Object:表示对象,是一种无序的键值对集合。

在存储上,JavaScript 中的基本数据类型和复杂数据类型有所不同:
基本数据类型的值被存储在栈内存中,它们的值被直接存储在变量所在的内存空间中。
复杂数据类型的值被存储在堆内存中,变量存储的是一个指向堆内存地址的指针。当变量复制时,复制的是指针,而不是实际的值。
需要注意的是,在 JavaScript 中,基本数据类型是按值传递的,而复杂数据类型是按引用传递的。当将基本数据类型的值传递给函数时,传递的是实际的值;而当将复杂数据类型的值传递给函数时,传递的是指向堆内存地址的指针。

总之,JavaScript 中的数据类型包括七种基本数据类型和一种复杂数据类型,它们在存储上有所不同,需要根据实际情况选择适当的数据类型和存储方式。

15.说说对React Hooks的理解?解决了什么问题?

React Hook是React 16.8版本引入的新特性,它可以让我们在函数组件中使用state和其他React特性,而不需要使用类组件。使用Hook可以使代码更简洁、易于理解和维护。常用的Hook包括useState、useEffect、useContext等,它们可以帮助我们处理组件状态、副作用和全局数据等问题。

React Hooks的引入解决了以下几个问题:
解决了组件逻辑复用的问题
在React之前,组件逻辑的复用通常是通过高阶组件、render props等方式来实现。而使用Hooks后,可以将组件逻辑封装成自定义Hook,然后在多个组件中进行复用,从而避免了高阶组件和render props等方式带来的代码冗余和复杂性。
解决了类组件的问题
类组件在处理复杂逻辑时,往往需要使用生命周期方法和state等特性,而这些特性在使用时需要考虑作用域、绑定this等问题。而使用Hooks后,可以在函数组件中使用state、生命周期方法等特性,避免了类组件的一些问题。
解决了代码复杂度的问题
在React之前,组件的业务逻辑通常被拆分成多个生命周期方法和方法之间的调用,从而导致代码复杂度的增加。而使用Hooks后,可以将组件的逻辑拆分成多个自定义Hook,从而避免了组件之间的耦合和代码冗余。
总的来说,React Hooks的引入可以让我们更方便地编写React应用程序,提高代码的复用性和可维护性。同时,Hooks还可以提高代码的可测试性,从而让我们更容易地编写高质量的React代码。

16.说说你对promise的了解?

Promise 是一种用于异步编程的对象,它可以让异步操作更加方便和可读。Promise 通过链式调用的方式,将异步操作和回调函数分离开来,从而使代码更加清晰和易于维护。
Promise 对象有三种状态:Pending、Fulfilled 和 Rejected。当 Promise 对象处于 Pending 状态时,表示异步操作正在进行中,还没有得到结果;当 Promise 对象状态变为 Fulfilled 时,表示异步操作已经成功完成,可以获取到异步操作的结果;当 Promise 对象状态变为 Rejected 时,表示异步操作出现了错误或异常,无法得到异步操作的结果。
Promise 对象有两个重要的方法:then() 和 catch()。then() 方法用于处理异步操作成功后的结果,catch() 方法用于处理异步操作失败时的错误信息。then() 和 catch() 方法可以通过链式调用的方式进行调用,从而实现异步操作和回调函数的分离。
Promise 还有一些其他的方法,例如 all()、race() 和 finally() 等。其中 all() 方法用于处理多个异步操作的结果,race() 方法用于处理多个异步操作中最快完成的结果,finally() 方法用于在异步操作结束后执行一些清理工作。
总之,Promise 是一种非常重要的异步编程技术,可以使异步操作更加方便和可读。在实际开发中,需要根据实际情况灵活使用 Promise,从而提高代码的可维护性和性能。

17.说说webpack中常见的Loader?解决了什么问题?

常见的loader如下:

style-loader: 将css添加到DOM的内联样式标签style里

css-loader :允许将css文件通过require的方式引入,并返回css代码

less-loader: 处理less

sass-loader: 处理sass

postcss-loader: 用postcss来处理CSS

autoprefixer-loader: 处理CSS3属性前缀,已被弃用,建议直接使用postcss

file-loader: 分发文件到output目录并返回相对路径

url-loader: 和file-loader类似,但是当文件小于设定的limit时可以返回一个Data Url

html-minify-loader: 压缩HTML

babel-loader :用babel来转换ES6文件到ES

18.说说 React 性能优化的手段有哪些?

避免使用内联函数

使用 React Fragments 避免额外标记

使用 Immutable

懒加载组件

事件绑定方式

服务端渲染

使用PureComponent或shouldComponentUpdate方法来避免不必要的渲染。

使用React.memo来缓存组件的渲染结果。

使用key属性来优化列表渲染。

使用useCallback和useMemo来缓存函数和计算结果。

使用虚拟列表或分页加载来优化大数据量的渲染。

使用React.lazy和Suspense来优化组件的懒加载。

使用Webpack或Rollup等工具来优化代码的打包和压缩。

使用CDN或缓存来优化静态资源的加载。

使用SSR来优化首屏加载速度和SEO效果。

使用性能分析工具来定位性能瓶颈并进行优化

19.说说 React中的setState执行机制?

在组件生命周期或React合成事件中,setState是异步
在setTimeout或者原生dom事件中,setState是同步

React中的setState方法是用于更新组件状态的方法,它可以接收一个对象或一个函数作为参数,用于更新组件的状态。当调用setState方法时,React会将新的状态合并到原有的状态中,并触发组件的重新渲染。setState方法的执行机制可以分为以下几个步骤:

合并状态:当调用setState方法时,React会将新的状态合并到原有的状态中,从而生成一个新的状态对象。

批量更新:当调用setState方法时,React会将更新操作添加到一个更新队列中,而不是立即更新组件状态。这是因为React希望将多个更新操作合并成一个更新操作,从而减少组件的重新渲染次数,提高性能。

触发更新:当更新队列中的所有更新操作都执行完毕后,React会触发组件的重新渲染,并将新的状态传递给组件。

总的来说,React中的setState方法是用于更新组件状态的方法,它可以接收一个对象或一个函数作为参数,用于更新组件的状态。当调用setState方法时,React会将新的状态合并到原有的状态中,并将更新操作添加到一个更新队列中,而不是立即更新组件状态。当更新队列中的所有更新操作都执行完毕后,React会触发组件的重新渲染,并将新的状态传递给组件。这个过程是自动化的,开发者只需要关注状态的更新和组件的渲染,而不需要手动操作DOM节点。

20.Vue组件之间的通信方式都有哪些?

父子组件之间的通信
兄弟组件之间的通信
祖孙与后代组件之间的通信
非关系组件间之间的通信

父子关系的组件数据传递选择 props 与 e m i t 进行传递,也可选择 r e f 兄弟关系的组件数据传递可选择 emit进行传递,也可选择ref 兄弟关系的组件数据传递可选择 emit进行传递,也可选择ref兄弟关系的组件数据传递可选择bus,其次可以选择$parent进行传递
祖先与后代组件数据传递可选择attrs与listeners或者 Provide与 Inject
复杂关系的组件数据传递可以通过vuex存放共享的变量

父组件向子组件传递

由于React的数据流动为单向的,父组件向子组件传递是最常见的方式

父组件在调用子组件的时候,只需要在子组件标签内传递参数,子组件通过props属性就能接收父组件传递过来的参数

子组件向父组件传递
子组件向父组件通信的基本思路是,父组件向子组件传一个函数,然后通过这个函数的回调,拿到子组件传过来的值

兄弟组件之间的通信
如果是兄弟组件之间的传递,则父组件作为中间层来实现数据的互通,通过使用父组件传递

父组件向后代组件传递
父组件向后代组件传递数据是一件最普通的事情,就像全局数据一样

使用context提供了组件之间通讯的一种方式,可以共享数据,其他数据都能读取对应的数据

通过使用React.createContext创建一个context

context创建成功后,其下存在Provider组件用于创建数据源,Consumer组件用于接收数据,使用实例如下:

Provider组件通过value属性用于给后代组件传递数据

如果想要获取Provider传递的数据,可以通过Consumer组件或者或者使用contextType属性接收

21.说说你对react的理解?有哪些特性?

React,用于构建用户界面的 JavaScript 库,提供了 UI 层面的解决方案

遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效

使用虚拟DOM来有效地操作DOM,遵循从高阶组件到低阶组件的单向数据流

帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面

react 类组件使用一个名为 render() 的方法或者函数组件return,接收输入的数据并返回需要展示的内容

特性:
React特性有很多,如:

JSX语法

单向数据绑定

虚拟DOM

声明式编程

Component

22.说说Real diff算法是怎么运作的?

主要分为3层:tree层、component层、element层
(1)、tree层:tree层对DOM节点的跨层级移动的操作忽略不计,只对相同层级的DOM节点进行比较(即同一个父节点下的所有子节点),一旦发现节点不存在,直接删除掉该节点以及之下的所有子节点
(2)、component层:

遇到同一类型的组件遵循 tree diff,进行层级对比
遇到不同类型的组件,直接将这个不同的组件判断为脏组件,并替换该组件和之下所有的子节点
在同一类型的两个组件中,当知道这个组件的虚拟dom没有任何变化,就可以手动使用shouldComponentUpdate()来判断组件是否需要进行diff,进一步的提升了diff效率和性能
(3)、element层:对同一层级的元素节点进行比较,有三种情况:

面对全新的节点时,执行插入操作
面对多余的节点时,执行删除操作
面对换位的节点时,执行移动操作

diff算法是虚拟dom的一个必然结果,它是通过比较新旧dom的对比,将在不更新页面的情况下,将需要的内容局部更新
diff算法遵循深度优先,同层比较的原则
可以使用key值,可以更加精准的找到dom节点
react中diff算法主要遵循三个层级的策略, 1.tree层级 2.component层级 3.element层级
tree层级不会做任何修改,如果有不一样,直接删除创建
component层级从父级往子级查找,如果发现不一样,直接删除创建
element层有key值作比较,如果发现key值可以复用的话,就会将位置进行移动,如果没有,则执行删除操作

23.说说你对React中虚拟dom的理解?

虚拟DOM是一棵以JavaScript对象作为基础的树,每一个节点可以将其称为VNode,用对象属性来描述节点,实际上它是一层对真实DOM的抽象,最终可以通过渲染操作使这棵树映射到真实环境上,简单来说Virtual DOM就是一个Js对象,是更加轻量级的对DOM的描述,用以表示整个文档。

Virtual DOM是一种编程概念,在这个概念里,UI以一种理想化的,或者说虚拟的表现形式被保存于内存中,并通过如ReactDOM等类库使之与真实的DOM同步,这一过程叫做协调。这种方式赋予了React声明式的API,您告诉React希望让UI是什么状态,React就确保DOM匹配该状态,这样可以从属性操作、事件处理和手动DOM更新这些在构建应用程序时必要的操作中解放出来。

与其将Virtual DOM视为一种技术,不如说它是一种模式,人们提到它时经常是要表达不同的东西。在React的世界里,术语Virtual DOM通常与React元素关联在一起,因为它们都是代表了用户界面的对象,而React也使用一个名为fibers的内部对象来存放组件树的附加信息,上述二者也被认为是React中Virtual DOM 实现的一部分。

24.说说你对react hook的理解?

React Hook是React 16.8中的新特性。它可以让你在不编写class组件的情况下使用state以及其他React特性(增强了函数式组件的功能)。

React中的Hook方法:

UseState():

useState()用于为函数组件引入状态。在useState()中,数组第一项为一个变量,指向状态的当前值。类似this.state,第二项是一个函数,用来更新状态,类似setState

UseEffect():

useEffect()接受两个参数,第一个参数是你要进行的异步操作,第二个参数是一个数组,用来给出Effect的依赖项。只要这个数组发生变化,useEffect()就会执行

useRef():

相当于class组件中的createRef的作用,ref.current获取绑定的对象

UseContext():

接收context状态树传递过来的数据

UseReducer():

接受reducer函数和状态的初始值作为参数,返回一个数组,其中第一项为当前的状态值,第二项为发送action的dispatch函数

UseMemo()和useCallback():

接收的参数都是一样,第一个参数为回调,第二个参数为要依赖的数据
共同作用:仅仅依赖数据发生变化, 才会调用,也就是起到缓存的作用。useCallback缓存函数,useMemo 缓存返回值。

25.说说你对受控组件和非受控组件的理解?应用场景?

由React控制的输入表单元素而改变其值的方式,称为受控组件。
比如,给表单元素input绑定一个onChange事件,当input状态发生变化时就会触发onChange事件,从而更新组件的state。

非受控组件
非受控组件指的是,表单数据由DOM本身处理。即不受setState()的控制,与传统的HTML表单输入相似,input输入值即显示最新值。
在非受控组件中,可以使用一个ref来从DOM获得表单值。

26.说说Connect组件的原理是什么?

Connect组件是一个高阶组件,它可以将Redux store中的state和action creator作为props传递给被包裹的组件。Connect组件通过订阅Redux store的变化,来更新被包裹组件的props,从而实现组件与Redux store的连接。Connect组件的原理是使用了React的Context和高阶组件的特性,通过Provider和Consumer组件将Redux store传递给被包裹组件,并使用Redux的subscribe方法订阅store的变化,从而更新被包裹组件的props

27.说说react 中jsx语法糖的本质?

JSX语法糖是一种JavaScript语法扩展,它可以让我们在JavaScript代码中编写类似HTML的标记,从而更方便地描述UI组件的结构和样式。JSX语法糖本质上是一种语法糖,它会被转换为React.createElement函数调用,从而创建React元素。JSX语法糖的优点是可以提高代码的可读性和可维护性,缺点是需要学习一些新的语法和规则。

28.说说你对redux中间件的理解?常用的中间件有哪些?实现原理?

redux中间件是一种机制,它可以在Redux action被发起之后,到达reducer之前,对action进行拦截、处理和转换。中间件可以用于处理异步操作、日志记录、错误处理、路由跳转等场景。

常用的中间件包括thunk、saga、logger等。

其中,thunk中间件可以让我们在action creator中返回一个函数,从而处理异步操作;saga中间件可以让我们使用Generator函数来处理异步操作和复杂的业务逻辑;logger中间件可以让我们记录Redux action的日志,从而方便调试和排查问题。

中间件的实现原理是基于Redux的applyMiddleware函数和函数式编程的思想。applyMiddleware函数接收一个或多个中间件作为参数,返回一个enhancer函数,该函数接收createStore函数作为参数,返回一个增强版的createStore函数。增强版的createStore函数可以在dispatch action之前,对action进行拦截、处理和转换,然后再将处理后的action传递给下一个中间件或reducer。中间件的处理过程是通过函数柯里化和compose函数来实现的,这样可以让多个中间件按照顺序依次执行,并且让每个中间件只需要关注自己的逻辑,从而提高代码的可读性和可维护性。

29.说说AMD、CMD、commonJS模块化规范的区别?

AMD、CMD、commonJS都是JavaScript模块化规范,它们的主要区别在于模块定义和加载方式的不同。

AMD(Asynchronous Module Definition)规范是由RequireJS提出的,它采用异步方式加载模块,可以在页面加载时并行加载多个模块,从而提高页面加载速度。AMD规范的模块定义方式是通过define函数来定义,可以指定模块的依赖关系和导出内容。

CMD(Common Module Definition)规范是由SeaJS提出的,它采用同步方式加载模块,可以在需要时按需加载模块,从而提高页面响应速度。CMD规范的模块定义方式是通过define函数来定义,可以指定模块的依赖关系和导出内容。

CommonJS规范是由Node.js提出的,它采用同步方式加载模块,可以在服务器端使用。CommonJS规范的模块定义方式是通过module.exports和require函数来定义和加载模块,可以实现模块的复用和封装。

总的来说,AMD和CMD适用于浏览器端的模块化开发,CommonJS适用于服务器端的模块化开发。AMD和CMD的主要区别在于模块的加载方式,AMD采用异步方式加载模块,CMD采用同步方式加载模块。而CommonJS的模块定义和加载方式与AMD和CMD有所不同,它采用module.exports和require函数来定义和加载模块。

30.说说package.json中版本号的规则?

package.json中的版本号遵循语义化版本规范(Semantic Versioning),格式为“主版本号.次版本号.修订号”,例如“1.2.3”。其中,主版本号表示不兼容的API修改,次版本号表示向下兼容的功能性新增,修订号表示向下兼容的问题修复。在package.json中,版本号通常被定义在dependencies、devDependencies、peerDependencies等字段中,用于指定依赖包的版本范围。常用的版本范围包括:精确版本号、波浪号版本号、插入号版本号、X号版本号等。精确版本号表示精确匹配指定版本号,波浪号版本号表示匹配指定版本号及其兼容的更新版本,插入号版本号表示匹配指定版本号及其向下兼容的更新版本,X号版本号表示匹配指定版本号的主版本号和次版本号,忽略修订号。

31.说说React jsx转换成真实DOM的过程?

使用React.createElement或JSX编写React组件,实际上所有的 JSX 代码最后都会转换成React.createElement(…) ,Babel帮助我们完成了这个转换的过程。
createElement函数对key和ref等特殊的props进行处理,并获取defaultProps对默认props进行赋值,并且对传入的孩子节点进行处理,最终构造成一个虚拟DOM对象
ReactDOM.render将生成好的虚拟DOM渲染到指定容器上,其中采用了批处理、事务等机制并且对特定浏览器进行了性能优化,最终转换为真实DOM

编写jsx代码–babel编译jsx–
编译后jsx执行React.createElement的调用—在createElement方法中生成虚拟dom
最终返回给ReactDom.render生成真实dom

32.说说你对@reduxjs/toolkit的理解?和react-redux有什么区别?

@reduxjs/toolkit是一个官方提供的Redux工具包,它可以帮助我们更方便地编写Redux代码,减少样板代码的重复,提高开发效率和代码质量。@reduxjs/toolkit提供了以下几个主要的API:

configureStore:用于创建Redux store,可以自动集成常用的中间件和开发工具,从而简化了store的创建和配置过程。

createSlice:用于创建Redux reducer和action creator,可以自动处理action的类型和payload,从而简化了reducer和action creator的编写和管理。

createAsyncThunk:用于创建异步的thunk action,可以自动处理异步操作的状态和结果,从而简化了异步操作的编写和管理。

createEntityAdapter:用于创建实体的adapter,可以自动处理实体的CRUD操作,从而简化了实体的管理和操作。

相比于传统的Redux开发方式,@reduxjs/toolkit可以减少样板代码的重复,提高开发效率和代码质量。同时,@reduxjs/toolkit还可以自动处理一些常见的Redux问题,如异步操作、实体管理等,从而让开发者更专注于业务逻辑的实现。与react-redux的区别在于,react-redux是一个与React集成的Redux库,它提供了Provider和connect等API,可以让我们更方便地在React应用中使用Redux。而@reduxjs/toolkit是一个Redux工具包,它提供了一些API,可以让我们更方便地编写Redux代码,减少样板代码的重复,提高开发效率和代码质量。两者的目的不同,但都可以让我们更好地使用Redux。

33.React render方法的原理,在什么时候会触发?

React的render方法是用于将React元素渲染为真实DOM节点的方法。当组件的props或state发生变化时,React会调用render方法重新渲染组件,并将新的React元素与旧的React元素进行比较,从而更新真实DOM节点。如果新旧两个React元素相同,那么React不会重新渲染组件,从而提高性能。

render方法的触发时机包括:

组件挂载时:当组件第一次被渲染到页面上时,React会调用render方法生成React元素,并将其转换为真实DOM节点。

组件更新时:当组件的props或state发生变化时,React会调用render方法重新渲染组件,并将新的React元素与旧的React元素进行比较,从而更新真实DOM节点。

强制更新时:当组件调用forceUpdate方法时,React会忽略shouldComponentUpdate方法的返回值,强制调用render方法重新渲染组件。

总的来说,React的render方法是用于将React元素渲染为真实DOM节点的方法,它会在组件挂载、更新和强制更新时被调用。通过比较新旧两个React元素的差异,React可以高效地更新页面,从而提高性能。

34.如何通过原生js实现一个节流函数和防抖函数?

// 节流函数

function throttle(fn, delay) {let timer = null;return function() {const context = this;const args = arguments;if (!timer) {timer = setTimeout(function() {fn.apply(context, args);timer = null;}, delay);}};
}
// 防抖函数

function debounce(fn, delay) {let timer = null;return function() {const context = this;const args = arguments;clearTimeout(timer);timer = setTimeout(function() {fn.apply(context, args);}, delay);};
}

35.说说你对koa中洋葱模型的理解?

Koa是一个精简的node框架,被认为是第二代Node框架,其最大的特点就是独特的中间件流程控制,是一个典型的洋葱模型,它的核心工作包括下面两个方面:

将node原生的req和res封装成为一个context对象。
基于async/await的中间件洋葱模型机制

Koa的洋葱模型是以next()函数为分割点,先由外到内执行Request的逻辑,然后再由内到外执行Response的逻辑,这里的request的逻辑,我们可以理解为是next之前的内容,response的逻辑是next函数之后的内容,也可以说每一个中间件都有两次处理时机。洋葱模型的核心原理主要是借助compose方法

36.说说如何借助webpack来优化前端性能?

JS代码压缩
CSS代码压缩
Html文件代码压缩
文件大小压缩
图片压缩
Tree Shaking
代码分离
内联 chunk

37.说说你对webSocket的理解?

WebSocket是HTML5提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。它基于TCP传输协议,并复用HTTP的握手通道。浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接, 并进行双向数据传输。它最大的特点是:服务器可以向客户端主动推动消息,客户端也可以主动向服务器推送消息。
WebSocket原理
客户端向 WebSocket 服务器通知一个带有所有接收者ID的事件,服务器接收后立即通知所有活跃的客户端,只有ID在接收者ID序列中的客户端才会处理这个事件

38.说说React生命周期中有哪些坑?如何避免?

getDerivedStateFromProps容易编写反模式代码,使受控组件和非受控组件区分模糊
componentWillMount在react中已经被标记弃用,不推荐使用,主要的原因是因为新的异步架构会导致它多次被调用,所以网络请求以及事件绑定应该放到componentDidMount中
componentWillReceivedProps同样也被标记弃用,被getDerivedStateFromProps所取代,主要是因为性能问题
shouldComponentUpdate通过返回 true或者 false 来确定是否需要触发新的渲染。主要用于性能优化。
componentWillUpdate 同样是由于新的异步渲染机制,而被标记废弃,不推荐使用,原先的逻辑可结合 getSnapshotBeforeUpdate 与 componentDidUpdate改造使用。
如果在 componentWillUnmount 函数中忘记解除事件绑定,取消定时器等清理操作,容易引发 bug。
如果没有添加错误边界处理,当渲染发生异常时,用户将会看到一个无法操作的白屏,所以一定要添加。

39.调和阶段setState干了什么?

React中的调和阶段是指React将组件的变化与DOM的变化匹配的过程,其中setState方法是触发组件重新渲染的重要方式。
调和阶段中,setState方法会触发组件重新渲染,并将更新后的state与之前的state合并得到新的state。然后React会比较新的state和之前的state的差异,通过一系列优化算法,计算出需要更新的DOM节点。
在计算出需要更新的DOM节点后,React会将这些节点标记为“脏节点”,然后进行批量更新,即将所有需要更新的节点一次性更新,而不是每个节点分别更新。这样可以提高性能,减少不必要的DOM操作,避免页面的闪烁。
在批量更新结束后,React会将所有需要更新的节点一次性更新到页面上,完成整个组件渲染的过程。
需要注意的是,setState方法是异步的,这意味着在调用setState方法后并不会立即更新state和DOM,而是将更新任务添加到更新队列中,等待下一次调和阶段时才会执行更新。如果需要在setState更新后立即获取最新的state或DOM,可以使用回调函数或在componentDidUpdate生命周期函数中进行操作

40.说说redux的实现原理是什么,写出核心代码?

原理:将应用的状态统一放到state中,由store来管理state,reducer的作用是返回一个新的state去更新store中的state,按redux的原则,ul层每一次状态都应通过action去触发,action传入对应的reducer中,reducer返回了一个新的state更新store中存放的state,这样就完成了一次状态的更新,subscribe是为store订阅监听函数,这些订阅后的监听函数是在每一次dispatch发起后依次执行,可以添加中间件对提交的dispatch进行重写,createStore创建仓库,接收reducer作为参数,bindactioncreateor绑定store.dispatch和action的关系,combineReducers合并多个reducers,applyMIddleware洋葱模型中间件,重写dispatch,compose整个多个中间件

createStore可以帮助创建 store
store.dispatch 帮助派发 action , action 会传递给 store
store.getState 这个方法可以帮助获取 store 里边所有的数据内容
store.subscrible 方法订阅 store 的改变,只要 store 发生改变, store.subscrible 这个函数接收的这个回调函数就会被执行

41.React合成事件的原理?

react合成事件是react模拟原生dom时间所有能力的一个事件对象,即浏览器原生事件的跨浏览器包装器,它根据W3C规范来定义合成事件,兼容所有浏览器,拥有与浏览器原生事件相同的接口。合成事件是为了防止很多事件直接绑定在原生的dom元素上,造成一些不可控的情况。

42.为什么react元素有一个$$type属性?

为了防止xss攻击,因为symbol无法被序列化,所以react可以通过有没有type属性来判断出当前的element对象是从数据库来的还是自己生成的,如果没有$$type这个属性,react会拒绝处理该元素

43.说说你对fiber架构的理解?解决了什么问题?

react fiter架构是对react做出的一个重大改变与优化,是对react核心算法的一次重新实现,JavaScript引擎和页面渲染引擎两个线程是互斥的,当其中一个线程执行时,另一个线程只能挂起等待,如果JavaScript线程长时间的占用了主线程,那么渲染页面的更新就不得不长时间的等待,界面长时间不更新,会导致页面响应度变差,用户可能会感觉到卡顿。
解决了:
为每个增加了优先级,优先级高的任务可以中断优先级低的任务
增加了异步任务
dom diff树变成了链表,一个dom对应两个fiber,对应两个队列

44.说说你对redux中间件的理解?常用的中间件有哪些?实现原理?

中间件(Middleware)是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的

redux-thunk:用于异步操作
redux-logger:用于日志记录

redux 中间件通过改写 store.dispatch 方法实现了action -> reducer的拦截
原redux的数据流:view -> action -> reducer -> store
加上中间件后变成了: view -> action -> middleware -> reducer -> store

45.说说你对事件循环event loop的理解?

事件循环分为两种,分别是浏览器事件循环和node.js事件循环,本文主要对浏览器事件循环进行描述。
我们都知道JavaScript是一门单线程语言,指主线程只有一个。Event Loop事件循环,其实就是JS引擎管理事件执行的一个流程,具体由运行环境确定。目前JS的主要运行环境有两个,浏览器和Node.js。

浏览器的事件循环分为同步任务和异步任务;所有同步任务都在主线程上执行,形成一个函数调用栈(执行栈),而异步则先放到任务队列(task queue)里,任务队列又分为宏任务(macro-task)与微任务(micro-task)。下面的整个执行过程就是事件循环

宏任务大概包括::script(整块代码)、setTimeout、setInterval、I/O、UI交互事件、setImmediate(node环境)

微任务大概包括::new promise().then(回调)、MutationObserver(html5新特新)、Object.observe(已废弃)、process.nextTick(node环境)
若同时存在promise和nextTick,则先执行nextTick

JavaScript代码运行时,任务分为宏任务和微任务两种,Event Loop也将任务队列分为宏队列和微队列分别管理宏任务和微任务,宏队列和微队列也具备队列特性:先进先出。

46.数组常用方法及作用,至少15个?

1、向数组的末尾添加元素,原数组改变(push)

2、从数组的末尾删除元素,原数组改变(pop)

3、向数组的开头添加元素,原数组改变(unshift)

4、从数组的开头删除元素,原数组改变(shift)

5、数组排序,不改变原数组(sort)

6、替换或删除数组中的元素,改变原数组(splice)

7、数组反转(reverse)

8、连接两个数组(concat)

9、从数组中截取元素(slice)

10、查到数组中的元素
indexOf()
inCludes()
11、遍历数组:forEach
14、数组过滤方法(filter)
15、数组映射方法(map)
16、判断数组中的元素是否满足条件,返回第一个满足条件的下标(findIndex)
17、判断数组中的元素是否满足条件,返回第一个满足条件的元素(find)
18、判断一个变量是否是数组(isArray)

47.说说你对vue中mixin的理解?

说你对mixin的理解
mixin是一种类,在vue中就是js文件,主要的作用是作为功能模块引用。因为在项目中,可能不同组件会有相同的功能,比如控制元素的显示和隐藏,如果他们的变量和规则也完全相同的话,就可以把这个功能单独提取出来,放在mixin.js中,再引入,就可以实现一样的功能了。引入的方法也分为全局混入和局部混入,局部混入就是在每个组件中引入,全局混入就是在main.js中通过Vue.mixin()引入。

48.for…in循环和for…of循环的区别?

1、推荐在循环对象属性的时候使用 for…in,在遍历数组的时候的时候使用 for…of
2、for…in 循环出的是 key,for…of 循环出的是 value
3、注意,for…of 是 ES6 新引入的特性。修复了 ES5 引入的 for…in 的不足
4、for…of 不能循环普通的对象(如通过构造函数创造的),需要通过和 Object.keys()搭配使用
5、for…in 循环:只能获得对象的键名,不能获得键值
for…of 循环:允许遍历获得键值

49.Js数据类型判断都有哪几种方式?至少说出5种?它们的区别是什么?

1.typeof判断
typeof返回的类型都是字符串形式
2. Constructor
实例constructor属性指向构造函数本身
constructor 判断方法跟instanceof相似,但是constructor检测Object与instanceof不一样,constructor还可以处理基本数据类型的检测,不仅仅是对象类型
3.instanceof可以判类型是否是实例的构造函数
instanceof 后面一定要是对象类型,并且大小写不能错,该方法适合一些条件选择或分支。

4.Object.prototype.toString.call()
判断类型的原型对象是否在某个对象的原型链上
5. 通过object原型上的方法判断
比如array.isArray()来判断是不是一个数组

6.===(严格运算符)
通常出现在我们的条件判断中,用来判断数据类型的话就会非常的有局限性,比如判断一个变量是否为空,变量是否为数据等

50.说说你对Object.defineProperty()的理解?

定义:Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象

为什么能实现响应式
通过defineProperty 两个属性,get及set

get
属性的 getter 函数,当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值

set
属性的 setter 函数,当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。默认为 undefined

51.说说Real DOM和Virtual DOM的区别?优缺点?

二者的区别如下:
虚拟dom不会进行排版与重绘操作而真实的dom会频繁的进行排班与重绘
虚拟dom的总损耗是‘虚拟dom增删改+真实dom差异增删改+排版与重绘’,真实dom的总损耗是‘真实dom完全增删改+排版与重绘’
优缺点:
真实dom优势: 易用, 缺点:效率低:解析速度慢,内存占用量过高,性能差:频繁操作真实dom,易于导致重绘与回流
虚拟dom优势:简单方便:如果手动操作真实dom来完成页面,频繁又容易出错,在大规模应用下维护起来也很困难, 性能方面:使用虚拟dom能够有效避免真实dom数频繁更新,减少多次引起重绘与回流,提高性能,跨平台:react借助虚拟dom,带来了跨平台的能力,一淘代码多端运行, 缺点:在一些性能要求极高的应用中虚拟dom无法进行针对性的极致优化,首次渲染大量的dom时,由于多了一层虚拟dom的计算,速度比正常稍慢

52.说说React中setState执行机制?

一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state,当需要修改里面的值的状态时需要通过调用setState来改变,从而达到更新组件内部数据的作用。setState的第一个参数可以是一个对象或者是一个函数,而第二个参数是一个回调函数,用于可以实时的获取到更新之后的数据,在使用setState更新数据的时候,setState的更新类型分为同步更新和异步更新。在组件生命周期或react合成事件中,setState是异步,在setTimeOut或者原生dom事件中,setState是同步。

53.说说react的事件机制?

react基于浏览器的事件机制自身实现了一套事件机制,包括事件注册,事件的合成,事件冒泡,事件派发等,在react中这套事件被称之为合成事件。合成事件是react模拟原生dom事件所有能力的一个事件对象,即浏览器原生事件的跨浏览器包装器,根据W3C规范来定义合成事件,兼容所有的浏览器,拥有与浏览器原生事件相同的接口。
react事件和原生事件的区别:事件命名方式不同,事件处理函数接口不同
react 的所有事件并没有绑定到具体的dom节点上而是绑定在了document 上,然后由统一的事件处理程序来处理,同时也是基于浏览器的事件机制(冒泡),所有节点的事件都会在 document 上触发。
react事件机制总结如下:
1.react上注册的事件最终都会绑定在document这个dom上,而不是react组件对应的dom(减少内存开销就是因为所有的事件都绑定在document上,其他节点没有绑定事件),
2.react自身实现了一套事件冒泡机制,所以这也就是为什么我们event.stopPropagation()无效的原因
3.react通过队列的形式,从触发的组件向父组件回溯,调用他们的JSX中定义的callback,
4.react有一套自己的合成事件SynthenticEvent

54.说说你对受控组件和非受控组件的理解?应用场景?

受控组件就是受我们控制的组件,组件的状态全程响应外部数据,有value和onChange事件,输入的时候触发事件函数,在函数内部实现state的更新,受控组件我们一般需要一个初始状态和一个状态更新事件函数
非受控组件就是不受我们控制的组件,一般情况下是在初始化的时候接受外部数据,然后自己在内部存储其自身状态,可以使用ref查询dom并查找其当前值。
大部分推荐使用受控组件来实现表单,在受控组件中,表单数据由react组件负责处理,非受控组件控制能力较弱,表单数据由dom本身处理更加方便快捷,代码量少

55.说说你对fiber架构的理解?解决了什么问题?

fiber架构是对react做出的一个重大改变与优化,是对react核心算法的一次重新实现,
在react中主要做了以下操作:为每个增加了优先级,优先级高的任务可以中断优先级低的任务,增加了异步任务,dom diff树变成了链表,一个dom对应两个fiber,对应两个队列,找到被中断的任务,重新执行,从架构角度来看,fiber是对react核心算法的重写,从编码角度看,fiber是react内部所定义的一种数据结构,它是fiber树结构的节点单位,也是react16新架构下的虚拟dom

56.如何使用css实现一个三角形?

采用的是均分原理,盒子都是一个矩形或者正方形,从形状的中心向四个角上下左右划分四个部分,首先需要把元素的宽高设为0,然后设置边框样式。
width:0;
height:0;
border-top:40px solid transparent;
border-left:40px solid transparent;
border-right:40px solid transparent;
border-bottom:40px solid transparent;

57.什么是强缓存和协商缓存?

①强缓存:客户端访问服务端返回的缓存
②协商缓存:通过在服务器返回的数据中去定义缓存时长,每次向服务器发送请求都会先发送一个get请求,去查询缓存时长是否过期,不过期则使用,过期则发送新请求

58.说说javascript内存泄漏的几种情况?

JavaScript内存泄漏是指不再使用的内存没有被及时释放,导致内存占用过高,最终导致程序运行缓慢或崩溃。常见的几种情况如下:
全局变量:在全局作用域中声明的变量没有及时释放,导致占用内存过高。解决方法是使用let或const关键字声明变量,避免变量污染全局作用域。
闭包:在函数内部使用闭包时,如果没有及时释放引用,会导致内存泄漏。解决方法是在函数执行完毕后,手动解除引用,或使用let关键字声明变量。
定时器:在使用setTimeout或setInterval等定时器时,如果没有及时清除定时器,会导致内存泄漏。解决方法是在定时器执行完毕后,手动清除定时器。
DOM元素:在使用document.createElement等DOM操作时,如果没有及时清除DOM元素,会导致内存泄漏。解决方法是在不需要使用DOM元素时,手动清除DOM元素。
循环引用:在对象之间存在循环引用时,如果没有及时解除引用,会导致内存泄漏。解决方法是使用WeakMap等内置对象来避免循环引用。
总之,要避免JavaScript内存泄漏,需要注意内存管理和垃圾回收机制,及时释放不再使用的内存,避免占用过高的内存空间。

59.ts中类的修饰符?

1.只读修饰符:readonly
2.静态修饰符:static,静态属性和方法只能通过类本身和子类访问,实例无法访问
3.访问修饰符:public,protected,pravite
默认为public,没有访问限制
protected:只能在基类和子类访问,实例不能直接访问,可以通过方法当中间人
pravite:只能在基类中访问

60.ts中的泛型?

泛型是指附属于函数、类、接口、类型别名之上的类型,当某个函数的参数,返回值和内部使用时的类型无法确定的情况下,就可以使用泛型来进行约束

泛型用法:
1.在函数中使用泛型:

function test (arg:T):T{
console.log(arg);
return arg; } test(111);// 返回值是number类型的 111 test<string | boolean>(‘hahaha’)//返回值是string类型的 hahaha test<string |
boolean>(true);//返回值是布尔类型的 true

使用方式类似于函数传参,传什么数据类型,T就表示什么类型,T也可以换成任意字符串

2.在接口中使用泛型:
// 注意,这里写法是定义的方法哦

interface Search {
<T,Y>(name:T,age:Y):T }

let fn:Search = function <T, Y>(name: T, id:Y):T {
console.log(name, id)
return name; }

fn(‘li’,11);//编译器会自动识别传入的参数,将传入的参数的类型认为是泛型指定的类型

3.在类中使用泛型

class Animal {
name:T;
constructor(name: T){
this.name = name;
}
action(say:T) {
console.log(say)
}
}
let cat = new Animal(‘cat’);
cat.action(‘mimi’)

61.ts中的interface 与type区别?

interface是定义接口的关键字,type是声明类型别名的关键字,两者都可以用来定义对象或者函数的结构,type是引用,interface是定义,不同点:type在声明类型别名之后实际上是一个赋值操作,他需要将别名与类型关联起来,interface是定义了一个接口类型。type能够表示非对象类型,而interface只能表示对象类型。interface可以继承其他的接口,类等对象类型,type不支持继承。interface接口名总是会直接显示在编译器的诊断信息和代码编辑器的只能提示中,而type的名字只在特定情况下才会显示出来,只有当类型别名表示数组类型,元祖类型以及类或者接口的泛型实例类型时才展示。interface具有声明合并的行为,而type不会,这也意味着我们可以通过声明合并的方式给interface定义的类型进行属性扩展。type可以通过typeof来获取实例的类型从而进行赋值操作

62.ts中never和void的区别?

never表示永远不会有值的一种类型,任何类型都不能赋值给never类型的变量。void表示没有任何类型,可以用到void的有:1.函数没写return 2.只写了return,没有写具体的返回值 3.return的是undefined

void类型和never类型都是ts新增的类型,这两者的共同点是都常见用于声明函数的返回值的类型。 void,表示函数返回值空,即undefined。 never类型则是表示函数没有返回值,也绝不会(never)有返回值的情况发生。

可以使用变量声明方法去声明void类型,除了underfined之外不能对void类型变量赋值(null也不行),所以平时声明一个void类型的变量没有什么大用,一般也只有在函数没有返回值时去声明。

never类型,也是用于函数的返回值,表示函数永远不会返回结果,js中有函数不返回结果的情况,一般不写return 就返回 undefined,但是这个never是什么都不返回。

63.ts中的函数的理解?

TypeScript函数可以创建有名字的函数和匿名函数
1.ts定义函数:定义的是什么返回类型,返回类型必须和定义的一致,不然会报错,定义匿名函数和定义有名函数一样,根据定义的类型返回相应的类型
2.ts中定义方法的传参:传递参数的时候,对参数定义类型,就比如我上面定义了name的类型是个字符串类型,age类型是个数字类型,所以我们调取方法的时候,根据类型对应好传参,是不会报错的,但是如果明明要求传字符串类型,却写了数字类型,就会报错
3.ts方法可选参数:es5里面的方法的实参和形参可以不一样,但是ts中必须一样,如果不一样就要配置可选参数。
4.ts默认参数:es5里面没法设置默认参数,es6和ts中都可以设置默认参数。
这个默认参数和上面的可选参数类似,你称为默认可选参数都可以。
5.ts剩余参数:你想同时操作多个参数,或者你并不知道会有多少参数传递进来。 在JavaScript里,你可以使用arguments来访问所有传入的参数。在TypeScript里,你可以把所有参数收集到一个变量里。
6.ts函数重载:java中方法的重载,重载指的是两个或者两个以上同名函数,但它们的参数不一样,这是会出现函数重载的情况。
typeScript中的重载,通过为同一个函数提供多个函数类型定义来试下多种功能的目的。
ts为了兼容es5,以及es6重载的写法和java中有区别
7.ts箭头函数:箭头函数跟JavaScript一样

64.class组件和函数组件区别?

1.class组件是有状态的组件,可以定义state的状态,函数组件没有状态
2.class组件是有生命周期的,函数组件没有生命周期
3.class组件是有this对象的,函数组件没有this对象
4.组件调用:class组件实例化后调用render方法调用,函数组件直接调用
5.class组件内部,render方法return返回渲染jsx模板,函数组件直接返回即可
6.ref获取子组件的对象,class组件可以直接获取到,函数组件无法直接获取到   forwardRef
7.绑定bind改变this指向,只适用于class组件

65.useMemo和usecallback如何提升性能

父组件中引入子组件,传递变量的时候,父组件有任何数据的更新的话都会导致子组件的重新渲染,改变的数据跟子组件无关的话,导致性能问题,这个时候我们用useMemo或者memo对组件的数据进行缓存的操作
如果绑定的属性中有方法的话,这时候会发现数据改变的时候,memo会失效,这个使用useCallback对方法进行缓存,避免子组件的重新渲染,提高性能

66.React-router-dom V5和V6有什么区别?

v5中的Switch标签换成了Routes标签
v5中的exact属性没有了,v6是默认精确匹配
v5中的component属性,v6用的element,element属性是组件标签
v6中重定向导航组件换成Navigate
v6中路由嵌套直接采用组件包裹的方式,可以不适用path绝对路径
v6中的<Outlet>标签相当于vue中的router-view
获取参数和Hooks的变化
props获取v6中props值没有参数数据,必须采用Hooks的路由获取数据
withRouter在v6版本中也被去掉了

67.React中Hooks及含义

Hooks也是钩子函数,可以让我们在函数组件中使用class组件的特性,比如生命周期,比如state数据的定义等等
常见的Hooks有:useState,useEffect,useMemo,useCallback,useRef,useContext,useReducer
useState:函数组件中设置state数据,参数默认值,返回的结果是一个数组,数组第一个值变量值,第二个值是一个函数,修改state的数据函数,相当于setState方法
useEffect:相当于生命周期函数,第一个参数是一个回调函数,第二个参数是个数组(可以为空),第一个参数返回一个函数的时候,相当于componentWillUNmount生命周期,第二个参数为空数组的时候,componentDidMount,第二个如果不填的话,只要页面更新就会触发,componentDidUpdate生命周期,第二个参数的值可以监听某些数据的变化,把监听的变量放人第二个参数接口,useEffect是一个副作用函数,页面渲染完成后触发
useMemo,useCallback性能提升:父组件中引入子组件,传递变量的时候,父组件有任何数据的更新的话都会导致子组件的重新渲染,改变的数据跟子组件无关的话,导致性能问题,这个时候我们可以使用useMemo或者memo对组件的数据进行缓存操作,如果绑定的属性中有方法的话,这时候会发现数据改变的时候,memo会失效,这个使用useCallback对方法进行缓存,避免子组件的重新渲染,提升性能
useRef:相当于类组件createRef创建ref结点对象标识符的let ref = useRef ref.current获取当前的节点对象
useContext:useContext作用用来获取context状态树对象的内容,let context = useContext(对象的内容值)
useReducer:useReducer是对useState数据的增强,useState无法处理比较复杂的逻辑,可以吧函数组件的数据进行一个集中式的管理,并且可以处理逻辑操作,参数是reducer的纯函数,返回的值是一个state数据,dispatch方法,第二个参数可以慌死state的初始化的值

68.React生命周期有哪些变化?

getDerivedStateFromProps  替换 componetWillMount , componentWillReceiveProps 生命周期

getSnapShotBeforeUpdate  替换了componentWillUpdate的生命周期函数

69.什么是高阶组件?如何使用?用过哪些高阶组件

高阶函数就是一个函数可以作为另一个函数的参数,这个函数就是高阶函数
高阶组件就是一个组件可以作为另一个组件的参数,这个组件就是高阶组件
memo,forwardRef,withRouter,connect
高阶组件的主要作用是复用,条件判断也可以使用HOC
react中的高阶组件主要有两种形式:
属性代理和反向继承,
属性代理:是一个函数接收一个WrappedComponent组件作为参数传入,
并返回了一个继承了React.Component组件的类,且在该类的render方法中返回被传入的WrappedComponent组件,
反向继承:是一个函数接收一个WrappedComponent组件的类,且在该类的render方法中返回super.render()方法

70.React中受控组件和非受控组件

由React控制的输入表单元素而改变其值的方式,称为受控组件。
比如,给表单元素input绑定一个onChange事件,当input状态发生变化时就会触发onChange事件,从而更新组件的state。
应用场景:动态表单,编辑

非受控组件
非受控组件指的是,表单数据由DOM本身处理。在初始化的时候接受外部数据,然后自己在内部存储其自身状态,即不受setState()的控制,与传统的HTML表单输入相似,input输入值即显示最新值。
在非受控组件中,可以使用一个ref来从DOM获得表单值。
应用场景:添加,对dom元素的控制

71.说说你对Promise的理解?

promise是异步编程的一种解决方案,promise解决异步的优点:链式操作减低了编码难度,代码可读性明显增强。
promise对象有三种状态:pending(进行中),fulfilled(已成功),rejected(已失败),
promise的特点有:对象的状态不受外界影响,只有异步操作的结果,可以决定当前是哪一种状态,一旦状态改变(从pending变为fulfilled和从pending变为rejected),就不会再变,任何时候都可以得到这个结果
promise的用法:promise对象是一个构造函数,用来生成promise实例,promise构造函数接收一个函数作为参数,该函数的两个参数分别是resolve和reject,resolve函数的作用是将promise对象的状态从未完成变为成功,reject函数的作用是将promise对象的状态从未完成变为失败。
promise构建出来的实例存在以下方法:then(), catch(), finally()
then(): then是实例状态发生改变时的回调函数,第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数
catch: catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数
finally(): finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作,then方法返回的是一个新的Promise实例,也就是promise能链式书写的原因

	Promise构造函数存在以下方法:
	all()
	race()
	allSettled()
	resolve()
	reject()
	try()

72.Promise和async/await有什么区别?

promise和 async/await都是解决异步编程的一种方式,但是async/await使得异步代码看起来像同步代码。
函数前面多了一个async关键字。await关键字只能用于async定于的函数内。async函数会隐式地返回一个Promise,该promise的resolve值就是return的值。

73.虚拟DOM和真实DOM的区别

两者的区别如下:虚拟dom不会进行排版与重绘操作,而真实dom会频繁的进行排版与重绘操作,虚拟dom的总损耗是‘虚拟dom增删改+真实dom差异增删改+排版与重绘’,真实dom的总损耗是‘真实dom完全增删改+排版与重绘’
真实dom的优势:
易用
缺点:效率低,解析速度慢,内存占用量过高,性能差,频繁操作真实dom,易于导致重绘与回流
虚拟dom的优势如下:
简单方便:如果手动操作真实dom来完成页面,频繁又容易出错,在大规模应用下维护起来也很困难,
性能方面:使用虚拟dom,能够有效避免真实dom树频繁更新,减少多次引起重绘与回流,提高性能
跨平台:react借助虚拟dom,带来了跨平台的能力,一套代码多端运行
缺点:在一些性能要求极高的应用中虚拟dom无法进行针对性的极致优化,首次渲染大量dom时,由于多了一层虚拟dom的计算,速度比正常的稍慢

74.state和props有什么区别?

相同点:
	两者都是JavaScript对象
	两者都是用于保存信息
	props和state都能触发渲染更新
不同点:
	props是外部传递给组件的,而state是在组件内部被组件自己管理的,一般在constructor中初始化
	props在组件内部是不可修改的,但state在组件内部可以进行修改
	state是多变的,可以修改

75.React组件通信的流程

父传子:在父组件的子组件标签上绑定自定义属性,挂载传递的数据,子组件中props接收传递的数据,直接使用即可
子传父:在父组件中子组件标签上绑定一个属性,传递一个方法给子组件,子组件中通过props来接收这个方法,
直接调用,传递给相应的参数即可
非父传子:
	1.状态提升(中间人模式):react中的状态提升概括来说就是将多个组件需要共享的状态提升到它们最近的父组件,
	在父组件上改变这个状态然后通过props分发给子组件
	2.context状态树传参

76.JavaScript的数据类型有哪些?

引用数据类型和基本数据类型
基本数据类型:Number,String,Boolean,Undefined,Null,Symbol,
引用数据类型:Object,Array,Function,

77.React中setState的运行机制的理解?

一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state,当需要修改里面的值的状态需要调用setState来改变,从而达到更新组件内部数据的作用,setState第一个参数可以是一个对象或者是一个函数,而第二个参数是一个回调函数,用于可以实时的获取到更新之后的数据,在使用setState更新数据的时候,setState的更新类型分成:异步更新和同步更新,在组件生命周期或者react合成事件中,setState是异步的,在setTimeOut和原生dom事件中,setState是同步的。对同一个值进行多次 setState, setState 的批量更新策略会对其进行覆盖,取最后一次的执行结果

78.说说你对React中事件机制的理解?

react基于浏览器的事件机制自身实现了一套事件机制,包括事件注册,事件合成,事件冒泡,事件派发等,在react中这套事件机制被称之为合成事件。合成事件是react模拟原生dom事件所有能力的一个事件对象,即浏览器原生事件的跨浏览器包装器根据W3C规范来定义合成事件,兼容所有的浏览器,拥有与浏览器原生事件相同的接口,react事件跟原生事件的区别:事件命名方式不同,事件处理函数书写不同。
react事件机制总结如下:
1.react上注册的事件最终都会绑定在document这个dom上,而不是react组件对应的dom(减少内存开销就是因为所有的事件都绑定在document上,其他节点没有绑定事件)
2.react自身实现了一套事件冒泡机制,所以这就是为什么我们event.stopPropagetion()无效的原因
3.react通过队列的形式,从触发的组件向父组件回溯,然后调用jsx中定义的callback
4.react有一套自己的合成事件SyntheticEvent

79.说说你对vue中生命周期的理解?

vue生命周期:
概念:vue组件从创建到销毁的一个过程,在不同的阶段给我们提供了不同的钩子函数,
方便我们操作对不同阶段的一些逻辑处理
钩子函数:
	beforeCreate:创建前
	created:创建后
	beforeMount:挂载前
	mounted:挂载后
	beforeUpdate:更新前
	updated:更新后
	beforeDestroy:销毁前
	destroyed:销毁后
	activated:组件激活时
	deactivated:组件未激活时
	强制销毁方法:$destroy()(强制销毁vue实例的一切 | 强制销毁组件)

80.vue2的响应式原理是什么?

vue响应式也叫作数据双向绑定,大致原理是:首先我们需要通过object.defineProperty()方法把数据设置为getter和setter的访问形式,这样我们就可以在数据被修改时在setter方法设置监视修改页面信息,也就是说每当数据被修改,就会触发对应的set方法,然后我们可以在set方法中去调用dom的方法,此外,如果页面有input用v-model绑定数据,我们需要在这种绑定了data的input元素上添加监听,每当input事件被触发时,就修改对应的data。
vue实现数据响应式,是通过数据劫持侦测数据变化,发布订阅模式进行依赖收集与视图更新,换句话说就是observe,watcher以及compile三者相互配合。
observe实现数据劫持,递归给对象属性,绑定setter和getter函数,属性改变时,通知订阅者。
compile解析模板,把模板中变量换成数据,绑定更新函数,添加订阅者,收到通知就执行更新函数。
watcher作为onserve和compile中间的桥梁,订阅observe属性变化的消息,触发compile更新函数。

81.说说你对盒子模型的理解?

盒子模型就是类似于一个封装的盒子,一个网页就是由一个一个盒子堆积而成。里面装着的是html要叙述的内容,它包括:外边距(margin),内边距(padding),边框(border),实际内容(content)四个属性,盒模型有两种,标准模型和IE模型,标准模型的宽度不包括内边距,IE模型的宽度要包括内边距
margin:外边距,在元素外创建的空白,空白通常指不能放其他元素的区域
padding:内边距,清除内容周围的区域,内边距是透明的,取值不能为负,受盒子的background属性影响
border:边框,围绕元素内容的内边距的一条或多条线,由粗细,样式,颜色三部分组成
content:实际内容,显示文本和图像

82.说说你对BFC的理解?

BFC是Box Formatting Context的缩写,中文翻译为‘块级格式化上下文’,BFC是一种在网页布局中非常重要的概念,它是一个独立的渲染区域,其中元素按照一定的规则进行布局和渲染,
BFC具有以下特性:
BFC中的元素在垂直方向上互相影响,即同属于一个BFC的元素在垂直方向上会按照一定的规则进行布局,避免互相影响
BFC中的元素不会超过其容器的边界,即BFC中的元素不会溢出其容器的边界,而是会自动调整其大小和位置.
BFC中的元素可以清除浮动,即BFC中的元素可以包含浮动元素,从而避免由于浮动元素导致的布局问题
BFC中的元素可以防止margin重叠,即在BFC中,相邻的两个元素的margin不会重叠,从而避免布局上的混乱
总之,BFC是一种强大的布局工具,它能够帮助我们解决许多常见的布局问题,提高网页布局的可靠性和可维护性。

83.说说你对Redux以及Redux中间件的理解?

redux是实现集中管理react的工具,遵循三大基本原则:
单一数据源,state是只读的,使用纯函数来执行修改
redux不只是应用在react中,还可以与其他的界面库一起使用,如vue。
如何使用redux:首先创建一个store的公共数据区域,然后创建一个reducer,本质是一个函数,接收两个参数,state和action,返回state,传递给store,建立连接,要获取store里的数据需要通过store.getState()来获取当前的state,更改store里面的数据是需要通过dispatch来派发action,

中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务,衔接网络上应用系统的各个部分或者不同的应用,能够达到资源共享,功能共享的目的。当需要支持异步操作,错误处理,日志监控就可以用到中间件,redux中,中间件就是在dispatch过程,在分发action时进行拦截处理,其本质上是一个函数,对store.dispatch方法进行了改造,在发出action和执行reducer这两步之间添加了其他功能。
常用的中间件有: redux-thunk:用于异步操作,redux-logger:用于日志记录,
redux-thunk中间件会判断当前传来的数据类型,如果是一个函数将会给函数传入参数值(dispatch,getState),dispatch函数用于我们之后再次派发action,getState函数考虑到我们之后的一些操作需要依赖原来的状态,用于让我们可以获取之前的一些状态

84.Vue中数据更新视图不更新的原理是什么?

数组通过下标修改元素、对象通过key添加元素
解决办法:Vue.set(参数一:要修改的对象或者数组,参数二:下标 或者要修改的键名,参数三:要修改成什么) 或者 this.$set()
注意:通过Vue.set()方法修改的元素,要通过Vue.delete()方法来删除
语法:Vue.delete(参数一:要删除的对象或者数组,参数二:要删除的下标,或者键名)

85.Vue中data为什么是一个函数不能是对象?

根实例对象data可以是对象也可以是函数(根实例是单例),不会产生数据污染情况
组件实例对象data必须为函数,目的事为了防止多个组件实例对象之间共用一个data,产生污染。采用函数的形式,initData时会将其作为工厂函数都会返回全新的data对象

86.Vue和React中key的作用是什么?

帮助我们更好地管理组件和元素,提高应用程序的性能和可维护性,用于管理组件的重用和更新,是每个组件和元素的唯一标识符

87.说说什么是闭包,有哪些应用场景?

闭包是指在函数内部创建一个新的作用域,并且该作用域可以访问函数外部的变量,简单来说,闭包就是函数和函数内部能够访问到的变量的组合。
闭包的应用场景有很多,其中一些比较常见的包括:封装变量,实现模块化,延迟执行,缓存变量,实现回调函数,但是由于闭包会占用内存和资源,因此开发者在使用闭包时需要注意内存管理和性能优化

88.说说你对原型和原型链的理解?场景

原型:JavaScript的所有对象中都包含了一个[proto]内部属性,这个属性对应的就是该对象的原型,JavaScript的函数对象,除了原型[proto]之外,还预置了prototype属性,当函数对象作为构造函数创建实例时,该prototype属性值将被作为实例对象的原型[proto]
原型链:当一个对象调用的属性或者方法自身不存在时,就会去自己的[proto]关联的前辈prototype对象上去找,如果没找到,就会去该prototype原型[proto]关联的前辈prototype去找,以此类推,直到找到属性/方法或undefined位置,从而形成了所谓的原型链

89.介绍一下 Tree Shaking?

Tree Shaking 指的就是当我引入一个模块的时候,我不引入这个模块的所有代码,我只引入我需要的代码,这就需要借助 webpack 里面自带的 Tree Shaking 这个功能来帮我们实现。
Tree-shaking的本质是消除无用的js代码。无用代码消除在广泛存在于传统的编程语言编译器中,编译器可以判断出某些代码根本不影响输出,然后消除这些代码,这个称之为DCE(dead code elimination)
在 webpack 项目中,有一个入口文件,相当于一棵树的主干,入口文件有很多依赖的模块,相当于树枝。实际情况中,虽然依赖了某个模块,但其实只使用其中的某些功能。通过 Tree-Shaking,将没有使用的模块摇掉,这样来达到删除无用代码的目的。

90.短轮询、长轮询和 WebSocket 间的区别?

短轮询:就是客户端向服务端接二连三的询问是否有新消息
长轮询:当服务器收到客户端发来的请求后,服务器端不会直接进行响应,而是先将这个请求挂起,然后判断服务器端数据是否有更新。如果有更新,则进行响应,如果一直没有数据,则到达一定的时间限制(服务器端设置)才返回。 。 客户端JavaScript响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。
长轮询和短轮询比起来,明显减少了很多不必要的http请求次数,相比之下节约了资源。长轮询的缺点在于,连接挂起也会导致资源的浪费。
WebSocket:webSocket是html5下的一种协议,实现了浏览器端与服务器端的通信,可以节省资源,实时通信。约定了一个通信规范,通过一个握手的机制,将浏览器端与服务器端建立连接,方便她们之间的通信。
从兼容性角度考虑,短轮询>长轮询>WebSocket;
从性能方面考虑,WebSocket>长轮询>短轮询。

91.说说你对同步和异步的理解?

同步和异步的区别在于操作执行的时候是否会阻塞程序的进行,阻塞的情况下为同步,不阻塞的情况下为异步,在实际编程中需要根据任务的需求和场景来选择适当的处理方式。
同步指的是再执行某个操作时,如果这个操作需要等待一定的时间才能完成,那么程序在执行完这个操作之前会一直处于阻塞状态,也就是说程序会停在这里一直等待这个操作完成,并且不能做其他的事情,同步通常使用阻塞方式进行编写。
异步则是指当执行某个操作时,如果这个操作需要等待一定的时间才能完成,那么程序不会一直停在这里等待操作完成,而是先去执行其他的任务,等操作完成时再去获取执行结果,异步的特点是不会让程序阻塞,可以继续执行其他的任务。

92.数组方法及使用场景?

增:
	push():添加到数组末尾,返回数组的最新长度
	unshift():添加到数组开头,返回新的数组长度
	splice():传入三个参数,分别是开始位置,0(要删除的元素数量),插入的元素,返回空数组
	concat():创建当前数组的副本,添加到副本末尾,返回副本数组,不会改变原数组
删:
	pop():删除数组最后一项,返回被删除的项
	shift():删除数组的第一项,返回被删除的项
	splice():传入两个参数,分别是开始位置,删除元素的数量,返回包含删除元素的数组
	slice():创建一个包含原有数组中一个或多个元素的新数组,不会影响原数组
改:
	splice():传入三个参数,分别是开始位置,要删除元素的数量,要插入的任意多个元素,返回删除元素的数组,
	对原数组产生影响
查:
	indexof():返回查找的元素再数组中的位置,如果没找到则返回-1
	includes():返回要查找的元素在数组中的位置,找到返回true,否则false
	find():返回第一个匹配元素
排序方法:
	reverse():将数组方向反转
	sort():接收一个比较函数,用于判断哪个值应该排在前面
转换方法:
	join():接收一个参数,即字符串分隔符,返回包含所有项的字符串
迭代方法:
	some():至少一个元素满足则返回true
	every():所有元素都满足则返回true
	forEach():对数组的每一项都运行传入的函数,没有返回值
	filter():对数组每一项都运行传入的函数,函数返回true的项会组成数组之后返回
	map():对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组

93.字符串方法及使用场景?

增:
	concat():用于将一个或者多个字符串拼接成一个新的字符串
删:
	slice(),substr(),substring(),这三个方法都返回调用它们的字符串的一个子字符串,而且都接收一或两个参数
改:
	trim(),trimLeft(),trimRight(),删除前,后或者前后所有空格符,再返回新的字符串
	repeat(),接收一整个参数,表示要将字符串复制多少次,然后返回拼接所有副本后的结果
	padEnd(),复制字符串,如果小于指定长度,则在相应一遍填充字符,直至满足长度条件
	toLowerCase(),toUpperCase(),大小写转化
查:
	chatAt():返回给定索引位置的字符串,由传给方法的整数参数指定
	indexof():从字符串开头去搜索传入的字符串,并返回位置(如果没找到,则返回-1)
	startWith(),includes():从字符串中搜索传入的字符串,并返回一个表示是否包含的布尔值
转换方法:
	split():把字符串按照指定的分割符,拆分成数组中的每一项
模板匹配方法:
	match():接收一个参数,可以是一个正则表达式,也可以是一个RegExp对象,返回数组
	search():接收一个参数可以是正则表达式字符串,也可以是一个RegExp对象,找到则返回匹配索引,否则返回-1
	replace():接受两个参数,第一个参数为匹配的内容,第二个参数为替换的元素(可用函数)

94.说说你对React中refs的理解?作用?

refs在计算机中成为弹性文件系统(Resilient File System,简称ReFs)
react中的refs提供了一种方式,允许我们访问dom节点或者在render方法中创建的react元素本质为reactDOM.render()返回的组件实例,如果是渲染组件则返回的是组件实例,如果渲染dom则返回的是具体的dom节点。
创建ref的形式:
1.传入字符串,使用时通过this.refs传入的字符串的格式获取对应的元素
2.传入对象,对象是通过react.createRef()方式创建出来,使用时获取到创建的对象中存在current属性就是对应的元素
3.传入参数,该函数会在dom被挂载时进行回调,这个函数会传入一个元素对象,可以自己保存,使用时,直接拿到之前保存的元素对象即可
4.传入hook,hook是通过useRef()方式创建,使用时通过生成hook对象的current属性就是对应的元素
应用场景:
对dom元素的焦点控制,内容选择,控制
对dom元素的内容设置及媒体播放
对dom元素的操作和对组件实例的操作
集成第三方库

95.说说你对弹性盒子布局的理解?

Flexible简称flex,意思是弹性布局,可以简便,完整,响应式的实现各种页面布局,采用flex布局的元素,成为flex容器container。关于flex常用的属性,我们可以划分为容器属性和容器成员属性
容器属性有:
flex-direction(决定主轴的方向)
flex-wrap(决定容器内项目是否可以换行)
flex-flow
justify-content(定义了项目在主轴上的对齐方式)
align-items(定义项目在交叉轴上如何对齐)
align-content(定义多跟轴线的对齐方式)
容器成员属性有:
order(定义项目的排列顺序)
flex-grow(定义项目的比例放大)
flex-shrink(定义项目的缩小比例)
flex-basis(设置元素再主轴上的初始尺寸)
flex
align-self(允许单个项目有与其他项目不一样的对齐方式)

96.防抖和节流有什么区别,使用场景?

本质上是优化高频率执行代码的一种手段
节流:n秒内只运行一次,若在n秒内重复触发,只有一次生效
防抖:n秒后再执行该事件,若在n秒内被重复触发,则重新计时
防抖和节流的相同点:
都可以通过使用setTimeOut实现
目的都是降低回调执行频率,节省计算资源
不同点:
函数防抖,在一段连续操作结束后,处理回调,利用clearTimeOut和setTimeOut实现。函数节流,在一段连续操作中,每段时间只执行一次,频率较高的事件中使用来提高性能
函数防抖关注一定时间连续触发的时间,只在最后执行一次,而函数节流每一段时间内只执行一次

	应用场景:
	防抖在连续的事件,只需触发一次回调的场景有:
		搜索框搜索输入。只需用户最后一次输入完,再发送请求
		手机号、邮箱验证输入检测
		窗口大小resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
	节流在间隔一段时间执行一次回调的场景有:
		滚动加载,加载更多或滚到底部监听
		搜索框,搜索联想功能

97.React和Vue的区别,相同点和不同点?

相同点:
1.都用虚拟dom实现快速渲染
2.都支持组件化,都有props的概念,允许父组件往子组件传递数据(不允许子组件改变父组件传来的props值)
3.都是数据驱动视图
4.都支持服务端渲染
5.都有管理状态 react有redux , vue有vue
6.都有支持native的方案 react的react native, vue的weex
7.都有组合功能 react使用高阶组件,vue使用mixins
8.都有自己的构建工具,可以快速搭建开发环境
不同点:
1.实现原理不同,react是函数式组件思想,vue是组件响应式思想
2.设计结构不同, vue使用的是可变数据,数据由data属性在vue对象中管理,react更强调数据不可变,由setState()更新状态
3.数据流不同,vue通过v-model进行双向数据流,react是单向数据流,一般通过setState/onChange
4.渲染方式不同,react是通过jsx模板,等计算完毕之后在渲染,vue是通过一种拓展的html语法进行渲染,边计算边渲染
5.框架本质不同,Vue本质是MVVM框架,由MVC发展而来。React是前端组件化框架,只针对MVC的view层。
6.通信方式不同,vue: 父通过props向子传递数据或回调(一般传数据);子通过事件向父发消息;通过provide / inject 来实现父向子注入数据,可跨层级传递。react:父通过props向子传递数据或回调;子通过回调向父发消息;通过 context进行跨层级的通信。

98.说说React中虚拟DOM和真实DOM的区别?

两者的区别如下:虚拟dom不会进行排版与重绘操作,而真实dom会频繁的进行排版与重绘操作,虚拟dom的总损耗是‘虚拟dom增删改+真实dom差异增删改+排版与重绘’,真实dom的总损耗是‘真实dom完全增删改+排版与重绘’
真实dom的优势:
易用
缺点:效率低,解析速度慢,内存占用量过高,性能差,频繁操作真实dom,易于导致重绘与回流
虚拟dom的优势如下:
简单方便:如果手动操作真实dom来完成页面,频繁又容易出错,在大规模应用下维护起来也很困难,
性能方面:使用虚拟dom,能够有效避免真实dom树频繁更新,减少多次引起重绘与回流,提高性能
跨平台:react借助虚拟dom,带来了跨平台的能力,一套代码多端运行
缺点:在一些性能要求极高的应用中虚拟dom无法进行针对性的极致优化,首次渲染大量dom时,由于多了一层虚拟dom的计算,速度比正常的稍慢

99.说说你对Redux的理解?项目中Redux目录是怎么划分的?应用场景有哪些?

redux是实现集中管理react的工具,遵循三大基本原则:
单一数据源,state是只读的,使用纯函数来执行修改
redux不只是应用在react中,还可以与其他的界面库一起使用,如vue。
如何使用redux:首先创建一个store的公共数据区域,然后创建一个reducer,本质是一个函数,接收两个参数,state和action,返回state,传递给store,建立连接,要获取store里的数据需要通过store.getState()来获取当前的state,更改store里面的数据是需要通过dispatch来派发action,

100.说说你对React-router的理解?V5和V6的区别?

react-router可以实现无刷新的条件下切换显示不同的页面,路由的本质就是页面的url发生改变时,页面的显示结果可以根据url的变化而变化,但是页面不会刷新。

v5中的Switch标签换成了Routes标签
v5中的exact属性没有了,v6是默认精确匹配
v5中的component属性,v6用的element,element属性是组件标签
v6中重定向导航组件换成Navigate
v6中路由嵌套直接采用组件包裹的方式,可以不适用path绝对路径
v6中的<Outlet>标签相当于vue中的router-view
获取参数和Hooks的变化
props获取v6中props值没有参数数据,必须采用Hooks的路由获取数据
withRouter在v6版本中也被去掉了	

101.元素水平垂直居中的方法有哪些?

单行文本的垂直居中:只需要让元素高度和行高相等即可;
多行文本的垂直居中:① 不固定高度的垂直居中——通过设置上下的padding值即可实现,② 固定高度的垂直居中——通过使用表格属性实现,具体为使用display设置行内元素的父盒子为table,然后将行内元素设置为display为table-cell,然后配合样式vertical-align:middle来实现垂直居中
块级元素水平垂直居中的方法:使用flex布局实现水平垂直居中 :①justify-content:center;实现主轴上子元素居中;(默认水平) ②align-items:center;实现侧轴上子元素居中;(默认垂直)
绝对定位配合margin的方式实现水平垂直居中
绝对定位配合transfrom:translate的方式实现水平垂直居中

102.css3动画都做过哪些?

css3实现动画的方式有:transition 实现渐变动画, transform 转变动画, animation 实现自定义动画

103.说说你对BOM对象的理解?常见的BOM对象有哪些?

BOM是浏览器对象模型,提供了独立于内容与浏览器窗口进行交互的对象,把浏览器当成一个对象来对待,常见的BOM对象有:
1.window,window是BOM的顶级对象
2.location
3.navigator
4.screen
5.history

104.css如果要画一个三角形? 有哪些方法?

采用的是均分原理
盒子都是一个矩形或正方形,从形状的中心,向4个角上下左右划分4个部分
首先,需要把元素的宽度、高度设为0。然后设置边框样式。

width: 0;
height: 0;
border-top: 40px solid transparent;
border-left: 40px solid transparent;
border-right: 40px solid transparent;
border-bottom: 40px solid #ff0000;

105.ES6中数组的方法有哪些?

forEach, map, filter, find, every, some, reduce

106.说说DOM常见的操作有哪些?

创建节点
查询节点
更新节点
添加节点
删除节点

1.创建节点:createElement,创建新元素 createTextNode, 创建一个文本节点 createDocumentFragment, 创建一个文档碎片 createAttribute, 创建属性节点

2.获取节点:querySelector, 传入css选择器,如果页面上没有指定的元素时,返回 null querySelectorAll,
返回一个包含节点子树内所有与之相匹配的Element节点列表,如果没有相匹配的,则返回一个空节点列表 document.getElementById(‘id属性值’);返回拥有指定id的对象的引用
document.getElementsByClassName(‘class属性值’);返回拥有指定class的对象集合
document.getElementsByTagName(‘标签名’);返回拥有指定标签名的对象集合
document.getElementsByName(‘name属性值’); 返回拥有指定名称的对象结合
document/element.querySelector(‘CSS选择器’); 仅返回第一个匹配的元素
document/element.querySelectorAll(‘CSS选择器’); 返回所有匹配的元素
document.documentElement; 获取页面中的HTML标签
document.body; 获取页面中的BODY标签
document.all[‘’]; 获取页面中的所有元素节点的对象集合型

3.更新节点:innerHTML, 修改一个dom节点的文本内容,可以直接通过HTML片段修改dom节点, innerText,textContent, 自动对字符串进行编码,保证无法设置任何HTML标签, style, DOM节点的style属性对应所有的CSS,可以直接获取或设置

4.添加节点:innerHTML, appendChild, insertBefore, setAttribute

5.删除节点:removeChild

107.js本地存储的方式有哪些? 有什么区别?

coolie:不超过4kb, 在过期时间之前一直有效,即使会话关闭或者关闭浏览器, 会自动将数据传递到服务器,服务器也可以写cookie到客户端
localstorage: 存储大小可以达到5M, 只要不主动删除,数据会永久存储, 不会将数据发送给服务器,仅在本地存储, 存储信息在同一域中是共享的,受同源策略的限制, 缺点:无法像cookie一样设置过期时间, 只能存入字符串,无法直接存入对象
sessionStorage: 存储大小可以达到5M,页面关闭,sessionStorage将会删除数据,不会将数据发送给服务器,仅在本地储存
indexDB: 储存理论上没有上限,所有的操作都是异步的,相比localstorage同步操作性能更高,尤其是数据量大的时候,原生支持存储js对象 缺点: 操作非常繁琐,本身有一定的门槛

108.说说你对event Loop的理解?

事件循环分为两种,分别是浏览器事件循环和node.js事件循环,本文主要对浏览器事件循环进行描述。
我们都知道JavaScript是一门单线程语言,指主线程只有一个。Event Loop事件循环,其实就是JS引擎管理事件执行的一个流程,具体由运行环境确定。目前JS的主要运行环境有两个,浏览器和Node.js。

浏览器的事件循环分为同步任务和异步任务;所有同步任务都在主线程上执行,形成一个函数调用栈(执行栈),而异步则先放到任务队列(task queue)里,任务队列又分为宏任务(macro-task)与微任务(micro-task)。下面的整个执行过程就是事件循环

宏任务大概包括::script(整块代码)、setTimeout、setInterval、I/O、UI交互事件、setImmediate(node环境)

微任务大概包括::new promise().then(回调)、MutationObserver(html5新特新)、Object.observe(已废弃)、process.nextTick(node环境)
若同时存在promise和nextTick,则先执行nextTick

JavaScript代码运行时,任务分为宏任务和微任务两种,Event Loop也将任务队列分为宏队列和微队列分别管理宏任务和微任务,宏队列和微队列也具备队列特性:先进先出。

109.Vue2的响应式的原理是什么?

vue响应式也叫作数据双向绑定,大致原理是:首先我们需要通过object.defineProperty()方法把数据设置为getter和setter的访问形式,这样我们就可以在数据被修改时在setter方法设置监视修改页面信息,也就是说每当数据被修改,就会触发对应的set方法,然后我们可以在set方法中去调用dom的方法,此外,如果页面有input用v-model绑定数据,我们需要在这种绑定了data的input元素上添加监听,每当input事件被触发时,就修改对应的data。
vue实现数据响应式,是通过数据劫持侦测数据变化,发布订阅模式进行依赖收集与视图更新,换句话说就是observe,watcher以及compile三者相互配合。
observe实现数据劫持,递归给对象属性,绑定setter和getter函数,属性改变时,通知订阅者。
compile解析模板,把模板中变量换成数据,绑定更新函数,添加订阅者,收到通知就执行更新函数。
watcher作为onserve和compile中间的桥梁,订阅observe属性变化的消息,触发compile更新函数。

110.Vue中数据更新视图不更新的原因是什么?

数组通过下标修改元素、对象通过key添加元素
解决办法:Vue.set(参数一:要修改的对象或者数组,参数二:下标 或者要修改的键名,参数三:要修改成什么) 或者 this.$set()
注意:通过Vue.set()方法修改的元素,要通过Vue.delete()方法来删除
语法:Vue.delete(参数一:要删除的对象或者数组,参数二:要删除的下标,或者键名)

111.Vue中nextTick的作用是什么?

Vue中的this. n e x t T i c k 的作用是产生一个回调函数,在下一轮 d o m 更新后执行该回调函数。 用处 : 当某操作需要等数据被修改更新之后再操作,则可以将操作放在 t h i s . nextTick的作用是产生一个回调函数,在下一轮dom更新后执行该回调函数。  用处: 当某操作需要等 数据被修改更新之后 再操作,则可以将操作放在this. nextTick的作用是产生一个回调函数,在下一轮dom更新后执行该回调函数。用处:当某操作需要等数据被修改更新之后再操作,则可以将操作放在this.nextTick的回调函数中执行。因为并不是vue数据一旦修改就会立即更新。

112.说说你对Vue中虚拟DOM和diff算法的理解?

Virtual dom,也就是我们常说的虚拟节点,它是通过JS的Object对象模拟DOM中的节点,然后再通过特定的render方法将其渲染成真实的DOM的节点。
DOM Diff指的是通过Diff算法去比较虚拟DOM的变化

113.ES6中新增的新特性?

1.let const 定义变量的块级作用域
2.解构赋值
3.扩展运算符,模板字符串
4.箭头函数
5.默认参数,rest剩余参数
6.for…of
7.symbol
8.set/map数据结构
9.promise
10.ES module
11.class类,proxy属性代理
12.数组中ES6的方法: filter, map, reduce, every, some, find, findindex, include, fill, flat, copywith,array, isarray, array.from

114.说说你对弹性盒子的理解?使用场景有哪些?常用属性?

Flexible简称flex,意思是弹性布局,可以简便,完整,响应式的实现各种页面布局,采用flex布局的元素,成为flex容器container。关于flex常用的属性,我们可以划分为容器属性和容器成员属性
容器属性有:
flex-direction(决定主轴的方向)
flex-wrap(决定容器内项目是否可以换行)
flex-flow
justify-content(定义了项目在主轴上的对齐方式)
align-items(定义项目在交叉轴上如何对齐)
align-content(定义多跟轴线的对齐方式)
容器成员属性有:
order(定义项目的排列顺序)
flex-grow(定义项目的比例放大)
flex-shrink(定义项目的缩小比例)
flex-basis(设置元素再主轴上的初始尺寸)
flex
align-self(允许单个项目有与其他项目不一样的对齐方式)

115.说说你对css预编译语言的理解?使用场景有哪些?

css预编译语言扩充了 Css 语言,增加了诸如变量、混合(mixin)、函数等功能,让 Css 更易维护、方便,本质上,预处理是Css的超集,包含一套自定义的语法及一个解析器,根据这些语法定义自己的样式规则,这些规则最终会通过解析器,编译生成对应的 Css 文件
Css预编译语言在前端里面有三大优秀的预编处理器,分别是:sass, less, stylus

116.说说原生Ajax的实现原理是什么,实现步骤?

Ajax的原理简单来说是在用户和服务器之间加了—个中间层(AJAX引擎),通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。使用户操作与服务器响应异步化。这其中最关键的一步就是从服务器获得请求数据。Ajax的过程只涉及JavaScript、XMLHttpRequest和DOM。XMLHttpRequest是ajax的核心机制
实现步骤:
1)创建 XMLHttpRequest 对象
2)通过 open 方法配置请求方式和请求地址
3)为 XMLHttpRequest 对象添加 readstatechange 事件,监听请求和服务端的响应
4)通过 send 方法发送请求
5)在 readstatechange 事件中通过 readystate 属性以及 status 属性判断服务器端的响应情况,如果 readystate 为 4 并且 status 为 200 则表明服务端正确返回了预期的响应,在此判断中根据客户端的响应编写代码

117.为什么要有跨域?JS跨域怎么解决?

跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域;
我们都知道要想访问一个网站,首先要知道这个网站的URL,才能够进入该网站。而URL是由协议、域名、端口组成的(协议,浏览器自动填充;域名的端口默认为80,所以通常这两项不用输入)。而跨域就是说去访问的网站信息中的协议、域名、端口号这三者之中任意一个与当前页面的URL地址不同就是跨域。即使两个不同的域名指向同一个ip地址,也是跨域(即非同源)
解决跨域:
1. 通过jsonp跨域
2、 document.domain + iframe跨域
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域资源共享(CORS)
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域

118.new 一个对象具体做了什么?

1、创建一个空对象;

2、将空对象的原型,指向于构造函数的原型;

3、将空对象作为构造函数的上下文(改变this指向);

4、对有返回值的构造函数做判断处理

119. ES6常用的新特性有哪些?

1.let const 定义变量的块级作用域
2.解构赋值
3.扩展运算符,模板字符串
4.箭头函数
5.默认参数,rest剩余参数
6.for…of
7.symbol
8.set/map数据结构
9.promise
10.ES module
11.class类,proxy属性代理
12.数组中ES6的方法: filter, map, reduce, every, some, find, findindex, include, fill, flat, copywith,array, isarray, array.from

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值