2021-2024 年 Vue 面试题 汇总
一、Web面试题
1.web常见的五种前端布局方式
(1)文档布局(text)
文档流本质是 nomal flow (普通流、常规流)
元素在文档中的特点 块级元素,和内联元素。
缺点:在移动端不可以使用pc端的页面,两个页面的布局是不一致的,移动端需要自己再设计一个布局并使用不同的域名呈现
(2)浮动布局(float)
浮动一般用于让块级元素排成一行在同一行显示
任何元素都可浮动,如一个元素设置为浮动元素,浮动元素无论最初是什么都会变成一个行块级元素,拥有disolay:inline-block属性float:left沿着父容器顶部向左推入容器,如果卡住推不动,判断当前位置能否全部显示该子容器,如果能完成 如果不能继续找空档往里推
缺点:脱离文档流,需要清除浮动,高度塌陷(float的破坏性主要是指它会使父容器的高度塌陷,也就是父元素在高度计算的时候会忽略浮动的元素)。
优点:兼容性好,计较简单容易上手
(3)定位布局(position)
relative属性:脱离文档流,但保留占位符,其偏移位置是相对于本身在正常文档流中时的位置, 脱离文档流是说设置了relative属性之后,元素不在z-index:0;的正常文档流中,其z-index的值>0但保留占位符,就是在正常文档流中位置依旧保留着,后续元素不能占位,然后相对于本身正常位置进行偏移。
absolute:完全脱离文档流,不保留占位符,其元素定位是相对于其父级以上,一直追溯至设置了position:relative或fixed属性的外层元素(也包含块元素),如果其外层元素均匀未设置position属性,则定位是相对于HTML文档进行的绝对定位,并不是相对于body的定位
缺点:脱离文档流,会导致后续元素全部脱离文档流,可用行差。
优点: 就是 让元素拥有了 inline-block 宽高限制
(4)流式布局(streaming)
元素的宽高用百分比做单位,元素宽高按屏幕分辨率调整,布局不发生变化。屏幕尺度跨度过大的情况下不能正常显示
缺点:如果尺度过大或者过小在屏幕上就不能正常显示
优点:兼容性比较好,当屏幕分辨率变化时元素大小不变布局也不变
(5)弹性布局(flex)
弹性布局是css3-display:flex,
缺点:浏览器的兼容性比较差只能到ie9以及以上。
优点:使用方便,根据flex规则很容易达到一定的布局效果。
(6)自适应布局(adaptation)
自适应布局分辨为不一样屏幕分辨率定义布局,可以创建多个静态布局,每一个静态布局对应一个分辨率范围,屏幕分辨率变化时,页面里面的元素的位置会变化而大小不会变,可使用(@media媒体查询)给不同的尺寸和介质的设备切换不同的样式在优秀的响应范围设计下可以给配范围内的设备最好的体验,在同一个设备下实际还是固定的布局
缺点:如果需要不同页面那么还需要写多页的代码
优点:屏幕分辨率变化时,页面里面元素的位置变化而大小不会变化
(7)响应式布局( BootStrap)
为不同的屏幕分辨率定义布局,同时,在每个布局中,应用流式布局的理念,即页面元素宽度随着窗口调整而自动适配。可以把响应式布局看作是流式布局和自适应布局设计理念的融合。即:创建多个流体式布局,分别对应一个屏幕分辨率范围。响应式几乎已经成为优秀页面布局的标准,每个屏幕分辨率下面会有一个布局样式,即元素位置和大小都会变媒体查询+流式布局。通常使用 @media 媒体查询 和网格系统 (Grid System) 配合相对布局单位进行布局,实际上就是综合响应式、流动等上述技术通过 CSS 给单一网页不同设备返回不同样式的技术统称。
缺点:媒体查询是有限的,也就是可以枚举出来的,只能 适应主流的宽高,要匹配足够的屏幕大小,工作量比较大,设计也需要多个版本
优点:适应pc和移动端,如果只够的耐心,效果会很完美。
2.什么是盒子模型?
在网页中,一个元素占有空间的大小由几个部分构成,其中包括元素的内容(content),元素的内边距(padding),元素的边框(border),元素的外边距(margin)四个部分。这四个部分占有的空间中,有的部分可以显示相应的内容,而有的部分只用来分隔相邻的区域或区域。4个部分一起构成了css中元素的盒模型。
3.行内元素有哪些?块级元素有哪些? 空(void)元素有那些?
(1)行内元素:a、b、span、img、input、strong、select、label、em、button、textarea
(2)块级元素:div、ul、li、dl、dt、dd、p、h1-h6、blockquote
(3)空元素:即系没有内容的HTML元素,例如:br、meta、hr、link、input、img
4.CSS实现垂直水平居中
(1)margin: 0 auto
.元素{
width:100px;
height:100px;
line-height:100px;
margin:0 auto;
}
(2)元素水平垂直居中
<div class="wrap">
<div class="example">
</div>
</div>
.wrap {
position: relative;
background-color: orange;
width: 300px;
height: 300px;
}
.example {
background-color: red;
width: 100px;
height: 100px;
position: absolute;
left: 50%;
top: 50%;
margin: -50px 0 0 -50px;
}
(3)flex布局
.元素 {
background-color: #FF8C00;
width: 200px;
height: 200px;
display: flex;
justify-content: center; /*使子项目水平居中*/
align-items: center; /*使子项目垂直居中*/
}
(4) 绝对布局
<div class="warp">
<div class="example">
居中显示
</div>
</div>
.warp {
position: relative;
background-color: orange;
width: 200px;
height: 200px;
}
.example {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: red;
width: 100px;
height: 100px;
margin: auto;
}
(5)给子元素相对定位,在通过translaY()得到垂直居中
.warp {
position: relative;
background-color: orange;
width: 200px;
height: 200px;
}
.example {
position: relative;
top:50%;
transform:translateY(-50%);
background-color: red;
width: 100px;
height: 100px;
margin: 0 auto;
}
(6)利用inline-block的vertical-align: middle去对齐after伪元素
.warp {
text-align: center;
overflow: auto;
width: 200px;
height: 200px;
background-color: orange;
}
.example {
display: inline-block;
background-color: red;
vertical-align: middle;
width: 100px;
height: 100px;
}
.warp:after {
content: '';
display: inline-block;
vertical-align: middle;
height: 100%;
margin-left: -0.25em;
}
(7)display: flex-box
.warp {
display: -webkit-flex;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-box;
display: flex;
-webkit-box-align: center;
-moz-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
-webkit-box-pack: center;
-moz-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
width: 200px;
height: 200px;
background-color: orange;
}
.example {
width: 100px;
height: 100px;
background-color: red;
}
5.简述同步和异步的区别
(1)同步是阻塞模式,异步是非阻塞模式。
(2)同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;
(3)异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
6.一次完整的HTTP事务是怎样的一个过程
基本流程:
a. 域名解析
b. 发起TCP的3次握手
c. 建立TCP连接后发起http请求
d. 服务器端响应http请求,浏览器得到html代码
e. 浏览器解析html代码,并请求html代码中的资源
f. 浏览器对页面进行渲染呈现给用户
7.TCP的三次握手四次挥手。
三次握手
1)客户端发送syn包(seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认
2)服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态
3)客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手
四次挥手。
1)服务器向客户端发送FIN,ACK位置1得TCP报文段。
2)客户端向服务器返回ACK位置1,得TCP报文段。
3)客户端向服务器发送FIN,ACK位置1,得TCP报文段。
4)服务器向客户端返回ACK位置1,得TCP报文段。
8.HTTP和HTTPS的区别
(1)http: 超文本传输协议,是互联网上广泛的一种网络协议,是一个用户客户端和服务端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少
(2)https: 一安全为目标的HTTP通道,在http下加入了SSL层。SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密
(3)https协议需要申请证书,一般免费证书比较少,所以需要一定的费用
http是超文本传输协议,信息是明文传输。https则是具有安全性的ssl加密传输协议
http和https使用的是完全不相同的两种连接方式,用的端口也不一样,http是80端口,https是443端口,所以一般安全级别比较高的服务都为采用https协议来进行信息加密避免敏感信息泄露
9.浏览器是如何渲染页面的
(1)从浏览器地址栏的请求链接开始
(2)浏览器通过DNS解析查到域名映射的IP地址
(3)成功之后浏览器端向此IP地址取得连接
(4)成功连接之后,浏览器端将请求头信息 通过HTTP协议向此IP地址所在服务器发起请求
(5)服务器接受到请求之后等待处理,最后向浏览器端发回响应
(6)此时在HTTP协议下,浏览器从服务器接收到 text/html类型的代码
(7)浏览器开始显示此html,并获取其中内嵌资源地址
(8)然后浏览器再发起请求来获取这些资源,并在浏览器的html中显示
10.浏览器存储方法及其优缺点
(1) cookie
h5之前,存储主要用cookies,缺点是在请求头上带着数据,导致流量增加。大小限制4k
(2)localStorage
以键值对(Key-Value)的方式存储,永久存储,永不失效,除非手动删除。IE8+支持,每个域名限制5M
(3)sessionStorage
sessionStorage操作的方法与localStroage是一样的,区别在于 sessionStorage 在关闭页面后即被清空,而 localStorage 则会一直保存。很多时候数据只需要在用户浏览一组页面期间使用,关闭窗口后数据就可以丢弃了,这种情况使用sessionStorage就比较方便
(4)cookie、localStorage、sessionStorage之间的区别
存储大小限制不同,cookie数据不能超过4k,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭
作用域不同,sessionStorage不在不同的浏览器页面中共享,即使是同一个页面;localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的
Web Storage 支持事件通知机制,可以将数据更新的通知发送给监听者
二、Vue面试题
1. 对于MVVM的理解
MVVM (Model-View-ViewModel)
1)Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。
2)View 代表UI 组件,它负责将数据模型转化成UI 展现出来。
3)ViewModel 监听模型数据变化和视图变化、处理用户交互;同步View 和 Model的对象,连接Model和View。
4)在MVVM架构下,View 和 Model 之间并没有直接的联系,通过ViewModel进行交互,Model 和 ViewModel 的交互是双向, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
5)ViewModel 通过双向数据绑定把 View 层和 Model 层连接起来,View 和 Model 之间的同步是自动的,无需人为干涉,开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
MVC(Model-View-Controller)
1)M->Model:代表数据模型负责管理和处理数据以及业务逻辑
2)V->View:视图,负责展示数据,
3)C->Controller:控制器,负责处理输入信息,传递给Model进行处理。还负责View更新
MVC:强调控制器的作用,视图和模型之间的交互需要通过控制器来完成。
MVVM:强调数据绑定,视图和视图模型之间的交互是自动的,通过数据绑定机制实现。
2. Vue实现双向数据绑定
(1)Vue实现数据双向绑定主要是:数据劫持结合发布者-订阅者的方式,通过Object.defineProperty来劫持各属性的setter,getter,数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
(2)vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令,最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。
3. Vue组件间的参数传递
(1)父组件向子组件传值:子组件通过props方法接受数据
(2)子组件向父组件传值:子组件通过e m i t ( ) 方 法 传 递 参 数 , 父 组 件 通 过 emit()方法传递参数,父组件通过emit()方法传递参数,父组件通过on()监听
(3)兄弟组件传值:eventBus,就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。项目比较小时,用这个比较合适。
4. v-show 与 v-if 区别
(1)v-show和v-if的区别:v-show是css切换,v-if是完整的销毁和重新创建。
(2)v-show和v-if的使用:频繁切换时用v-show,运行时较少改变时用v-if
5. Vue的生命周期(重点 问的概率 大概 80%)
(1)Vue的生命周期:beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed
(2)生命周期详解:
beforeCreate:(创建前) 在数据观测和初始化事件还未开始
created:(创建后) 完成数据观测,属性和方法的运算,初始化事件,$el属性还没有显示出来
beforeMount:(载入前) 在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。
mounted:(载入后) 在el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程中进行ajax交互。
beforeUpdate:(更新前) 在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。
updated:(更新后) 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
beforeDestroy:(销毁前) 在实例销毁之前调用。实例仍然完全可用。
destroyed:(销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
(3)什么是vue生命周期:Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期
(4)vue生命周期的作用是什么:它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑
(5)vue生命周期总共有几个阶段:它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后
(6)第一次页面加载会触发哪几个钩子:会触发 下面这几个beforeCreate, created, beforeMount, mounted
(7)DOM 渲染在 哪个周期中就已经完成:DOM 渲染在 mounted 中就已经完成了
6. 绑定 class 的数组用法
(1)对象方法 v-bind:class=“{‘orange’: isRipe, ‘green’: isNotRipe}”
(2)数组方法v-bind:class=“[class1, class2]”
(3)行内 v-bind:style=“{color: color, fontSize: fontSize+‘px’ }”
7. 计算属性computed和 监听watch 的区别
(1)computed 是一个对象时,它有哪些选项:有get和set两个选项
计算属性默认只有 getter,不过在需要时你也可以提供一个 setter
(2)computed 和 watch有什么区别:只是需要动态值,那就用计算属性;需要知道值的改变后执行业务逻辑,才用 watch
(3)watch 是一个对象时,它有哪些选项:handler,deep 是否深度,immeditate 是否立即执行
computed 有缓存、不支持异步操作、响应式数据变化时触发; watch 无缓存、支持异步操作、监听的数据变化时触发
8. Vue的路由实现:hash模式 和 history模式
(1)hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面
(2)history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误
9. 组件中 data 为什么是函数
(1)因为组件是用来复用的,JS 里对象是引用关系,这样作用域没有隔离,而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题
10. vue.js的两个核心是什么
(1)数据驱动和组件化
11. vue中 key 值的作用
(1)使用key来给每个节点做一个唯一标识
(2)key的作用主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。
12.v-for 与 v-if 的优先级
(1)v-for的优先级比v-if高
13.vue-router有哪几种路由守卫
(1)全局守卫:beforeEach
(2)后置守卫:afterEach
(3)全局解析守卫:beforeResolve
(4)路由独享守卫:beforeEnter
14.route和 router的区别是什么
(1)$router为VueRouter的实例,是一个全局路由对象,包含了路由跳转的方法、钩子函数等
(2)$route 是路由信息对象||跳转的路由对象,每一个路由都会有一个route对象,是一个局部对象,
**包含path,params,hash,query,fullPath,matched,name等路由信息参数**
15.vuex有哪几种属性(1)有五种,分别是 State、 Getter、Mutation 、Action、 Module。
State:state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新
Getter:getters 可以对State进行计算操作,它就是Store的计算属性
Mutation:有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)
Action:Action 提交的是 mutation,而不是直接变更状态。
Module:由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决这个问题,Vuex 允许我们将 store 分割成模块
15.路由懒加载
使用原因:在单页应用中,如果没有应用懒加载,运用webpack 打包后的文件将会异常的大,造成进入首页时:需要加载的内容过多,延时过长,不利于用户体验,而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用8寸原理: vue异步组件技术:异步加载,vue-router配置路由使用 vue 的异步组件技术,实现按需加载。
](https://github.com/YvetteLau/Blog/issues/35)
17.组件中写 name 选项有什么作用
1)、项目使用 keep-alive 时,可搭配组件的 name 进行缓存过滤。
2)、DOM 做递归组件时需要调用自身 name3)、vue-devtools 调试工具里显示的组件名称是由 vue 中组件name 决定的
18.Proxy 相比于 defineProperty 的优势
Vue3.0 摒弃了 Object.defineProperty,改为基于 Proxy 的观察者机制探索。
首先说一下 Object.defineProperty 的缺点:
-
①Object.defineProperty 无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实施响应。
-
②Object.defineProperty 只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue2.X 里,是通过递归 + 遍历 data 对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象才是更好的选择。而要取代它的 Proxy 有以下两个优点
-
可以劫持整个对象,并返回一个新对象。
-
有多种劫持操作(13 种)补充:
-
Proxy 是 ES6 新增的一个属性,翻译过来的意思就是代理,用在这里表示由它来“代理”某些操作。Proxy 让我们能够以简洁易懂的方式控制外部对象的访问,其功能非常类似于设计模式中的代理模式。
-
Proxy 可以理解为,在目标对象之前设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
-
使用 Proxy 的核心优点是可以交由它来处理一些非核心逻辑(如:读取或设置对象的某些属性前记录日志;设置对象的某些属性值前,需要验证;某些属性的访问控制等)。从而可以让对象只需要关注核心逻辑,达到关注点分离,降低对象复杂度等目的。
19.Vue3.0 的了解
大致有三个点,第一个是关于提出的新 API setup()函数,第二个说了对于 Typescript 的支持,最后说了关于替换 Object.defineProperty 为 Proxy 的支持。详细说了下关于 Proxy 代替带来的性能上的提升,因为传统的原型链拦截的方法,无法检测对象及数组的一些更新操作,但使用 Proxy 又带来了浏览器兼容问题。
三、JS面试题
1.call、apply的区别,原生JS实现bind。(call,apply,bind三者用法和区别:角度可为参数、绑定规则,运行效率、运行情况)。
定义:
apply():调用一个对象的一个方法,用另一个对象替换当前对象,例如:B.apply(A,arguments);即A对象应用B对象的方法。
call():调用一个对象的一个方法,用另一个对象替换当前对象,例如:B.call(A,args1,args2,···);即A对象调用用B对象的方法。
作用:①改变this指向;②借用别的对象的方法;③单纯的调用函数;④实现继承;
apply、call和bind的区别:
相似之处:
①都是用来改变函数的this对象的指向的;
②第一个参数都是this要指向的对象;
③都可以利用后续参数传参;
区别:
①apply、call、bind的第一个参数都是this要指向的对象,
但apply只有两个参数,第二个参数为一个数组,需要传输的参数值须全部放到数组中。
而call、bind一样,参数用逗号分开。
②apply、call返回的的是一个值,
而bind返回的是一个函数,需要执行这个函数才会得到值。
2.this关键字(指向)
this是JavaScript语言的一个关键字,它是函数运行时,在函数体内部自动生成一个对象,只能在函数体内部使用。函数的不同使用场合,this有不同的值。总的来说this就是函数运行时所在的环境对象。
1、纯粹的函数调用:这是函数的最通常的用法,属于全局调用,因此this就代表全局对象
2、作为对象方法的调用:函数还可以作为某个对象的方法调用,这时this就指这个上级对象。
3、作为构造函数调用:所谓构造函数,就是通过这个函数,可以生成一个新对象。这时this就指这个新对象。
4、箭头函数:箭头函数没有自己的this,继承外层上下文绑定的this;
5、隐式绑定:函数的调用是在某个对象上触发的,即调用位置上存在上下文对象,典型隐士调用:xxx.fn()
6、apply的调用:apply( )是函数的一个方法,作用是改变函数的调用对象。它的第一个参数就表示改变后的调用这个函数的对象。因此这时this指的就是这第一个参数。apply( )的参数为空时,默认调用全局对象。这时运行结果为0,证明this指的是全局对象。
3.原型、原型链、构造函数、实例、继承?
原型(proto):每个对象都有__proto__属性,__proto__指向创建他的构造函数的原型对象(prototype)。
原型链:是对象都有一个原型,通过__proto__可以访问原型,访问的原型又是对象,这样依次下去,就会构成一个对象的序列,该结构成为原型链。
构造函数:
① 构造函数的首字母必须大写,用来区分于普通函数;
② 内部使用的this对象,来指向即将要生成的实例对象;
③ 使用new来生成实例对象。(简单明了说法:一个原型对象是另一个原型对象的实例,相关的原型对象层层递进,就构成了实例与原型的链条,就是原型链。当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链,当然如果最终找不到返回null。) 1. prototype每个函数都有一个prototype属性,被称为显示原型 2._ proto 每个实例对象都会有 proto 属性,其被称为隐式原型 每一个实例对象的隐式原型 proto _属性指向自身构造函数的显式原型prototype 3. constructor每个prototype原型都有一个constructor属性,指向它关联的构造函数。 4. 原型链获取对象属性时,如果对象本身没有这个属性,那就会去他的原型__proto__上去找,如果还查不到,就去找原型的原型,一直找到最顶层(Object.prototype)为止。Object.prototype对象也有__proto__属性值为null。 这里需要注意的是Object是属于原型链的顶层,所有构造函数的的prototype都指向 Object.prototype`
继承:强烈推荐各位去这个网站github.com/YvetteLau/B…
4.作用域链、闭包、作用域;
⑴ 作用域链
定义:一个函数在访问变量的时候,优先使用自己的局部变量,如果没有这个变量的申明,则向上级访问,一直访问到全局。全局都没有的话,语法错误:is not defined。
⑵闭包closure
定义:当一个函数的返回值是另外一个函数,而返回的那个函数如果调用了其父函数的内部变量,且返回的那个函数在外部被执行,就产生了闭包.闭包是一个环境,具体指的就是外部函数–高阶函数
闭包的特性:
①函数嵌套函数;
②内部函数可以直接访问外部函数的内部变量或参数;
③变量或参数不会被垃圾回收机制回收。
闭包的优点:
①变量长期驻扎在内存中;
②避免全局变量的污染;
③私有成员的存在。
闭包的缺点: 常驻内存,增大内存的使用量,使用不当会造成内存泄漏。
⑶作用域:
全局作用域:window。
局部作用域:函数内部定义的。
5.window的onload事件和domcontentloaded
window.onload:当一个资源及其依赖资源已完成加载时,将触发onload事件。
document.onDOMContentLoaded:当初始的HTML文档被完全加载和解析完成之后,DOMContentLoaded事件被触发,而无需等待样式表、图像和子框架的完成加载。
区别:
①onload事件是DOM事件,onDOMContentLoaded是HTML5事件。
②onload事件会被样式表、图像和子框架阻塞,而onDOMContentLoaded不会。
③当加载的脚本内容并不包含立即执行DOM操作时,使用onDOMContentLoaded事件是个更好的选择,会比onload事件执行时间更早。
6.eventloop(事件循环):进程和线程,任务队列;
JS单线程详解:因为 js 是面向客户端的一门语言,主要是用户交互,操作dom,渲染数据。试想一下,如果是多线程,我们在一个线程删除了一个dom节点,另外一个线程添加了一个dom节点,以那个线程为主呢,就会出现混乱的情况。当然你可以说我们在操作一个dom之后加上锁,只允许一个线程操作,这样其实增加了程序的复杂度,并不是一个好办法。
单线程产生的问题:必须要等待前一个程序执行完毕才执行下一个,所以将程序分为了两类:同步任务和异步任务。异步任务又可以分为宏任务和微任务。
任务队列:为什么会有任务队列呢,还是因为 javascript 单线程的原因,单线程,就意味着一个任务一个任务的执行,执行完当前任务,执行下一个任务,这样也会遇到一个问题,就比如说,要向服务端通信,加载大量数据,如果是同步执行,js 主线程就得等着这个通信完成,然后才能渲染数据,为了高效率的利用cpu, 就有了同步任务和异步任务之分。
浏览器内核是多线程,JavaScript是单线程;
栈:先进后出的数据结构,存储基本数据类型的变量。
堆:主要负责引用数据类型的存储。
同步任务: 指的是在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
异步任务: 指的是不进入主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
宏任务macrotask: 可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。常见的宏任务:script, setTimeout, setInterval, setImmediate, I/O, UI rendering。
微任务microtask(异步): 可以理解是在当前task执行结束后立即执行的任务。常见的微任务:process.nextTick(Nodejs),Promise.then(), MutationObserver。
7.防抖和节流
(1)函数节流(throttle):事件频繁触发的情况下,在指定时间内只执行一次任务
(2)函数防抖(debounce):任务在事件触发后等待一段时间再执行,如果事件频繁触发,会重新计算等待时间
补充面试题
1.对BFC规范(块级格式化上下文)的理解
BFC 块级格式化上下文 一块独立的区域,有自己的规则,bfc中的元素与外界的元素互不影响
BFC是一块用来独立的布局环境,保护其中内部元素不受外部影响,也不影响外部。
怎么触发BFC
- float的值left或right
- overflow的值不为visible(默认)
- display的值为inline-block、table-cell、table-caption
- position的值为absolute(绝对定位)或fixed固定定位
2.BFC的应用
1、可以用来自适应布局
2、可以清除浮动
3.页面导入时,使用link和@import有什么区别
页面中使用CSS的方式主要有3种:行内添加定义style属性值,页面头部内嵌调用和外面链接调用,其中外面引用有两种:Link引入和@import导入,两者都是外部引用CSS的方式,但是存在一定的区别:
- 从属关系: link是标签,@import是css提供的.
- 加载差异: link: 结构和样式同时加载;而@import 先加载结构,后加载样式
- 兼容性:link没有兼容问题,@import不兼容ie5以下的浏览器.4.可操作性: link可以通过js操作dom插入link标签改变样式,而@import不能
4.简述src和href的区别
src用于替换当前元素
href用于在当前文档和引用资源之间确立联系.
src是source的缩写,指向外部资源的位置,指向的内容将会嵌入到文档中当前标签所在位置 href是Hypertext Reference的缩写,指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的链接
5.常见的兼容性问题?
1、不同浏览器margin和padding不同
2、ie6中,父级元素浮动以后,内部元素内容撑不开宽度
3、标签嵌套不规范,如p和h1-h6里面嵌套div
4、ie6小于19px,会当成19px处理,也就元素宽高小于19px的bug
5、图片3像素问题
6、IE8下给图片添加超链接时,图片会有蓝色边框
7、鼠标滑过时,不显示小手
························································································································································································
更新 ~~~~
vue3:
1、VUE3中为什么要用ProxyAPI代替definePropertyAPI¥¥¥vue2中、define.Property存在的问题新增属性、删除属性, 界面不会更新。直接通过下标修改数组, 界面不会自动更新使用Vue.set、Vue.delete或者vm. s e t 、 v m . set、vm. set、vm.delete这些APIvue3 的proxy
通过 Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、添加、删除。通过Reflect(反射): 对源对象的属性进行操作。1、Proxy 可以直接监听对象而非属性;2、Proxy 可以直接监听数组的变化;3、Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是Object.defineProperty 不具备的;4、Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;2、vue3对比vue2有哪些新特性1 . 数据响应式性能提升
响应式性能提升,由原来的 Object.defineProperty 改为基于ES6的 Proxy
解决了对象、数组更新后的检测, 大大优化了响应式监听的性能原来检测对象属性的变化, 需要一个个对属性递归监听, proxy 可以直接对整个对象劫持2 . 虚拟 DOM - 新算法 (更快 更小)
3 . 提供了 composition api, 可以更好的逻辑复用
它可以把各个功能模块独⽴开来,提⾼代码逻辑的可复⽤性,同时代码压缩性更强。
4 . 新增组件Fragment 不再限制 template 只有⼀个根⼏点。Teleport 传送门,允许我们将控制的内容传送到任意的 DOM 中。Supense 等待异步组件时渲染⼀些额外的内容,让应⽤有更好的⽤户体验。
5 . 更好的支持 typescript 重写, 有更好的 类型推导 (类型检测更为严格, 更稳定)
小结: vue3 性能更高, 体积更小, 更利于复用, 代码维护更方便
3、叙述一下vue3响应式实现原理vue3 响应式是使⽤ ES6 的 proxy 和 Reflect 相互配合实现数据响应式, 通过 new Proxy 代理了 obj 对象,然后通过 get、set、deleteProperty 函数代理了对象的读取、修改和删除操作,从而实现了响应式的功能。proxy 是深度监听,所以可以监听对象和数组内的任意元素,从⽽可以实现视图实时更新
4、vue3对比vue2优势性能更好;体积更小;更好的ts支持;更好的代码组织;更好的逻辑抽离;更多新的功能;
5、iframe的优缺点iframe(内联框架)是在文档中加载另一个html页面元素,本质上是在父页面上放置另一个网页
<iframe src=“xx.html” width=“xx” height=“xx”><iframe>
优点:1、使用方便 ,兼容性好 2、允许嵌入第三方内容
缺点:1、减慢页面加载速度 2、会产生很多页面,不容易管理
小程序的生命周期函数
onLoad() 页面加载时触发,只会调用一次,可获取当前页面路径中的参数。
onShow() 页面显示/切入前台时触发,一般用来发送数据请求;
onReady() 页面初次渲染完成时触发, 只会调用一次,代表页面已可和视图层进行交互。
onHide() 页面隐藏/切入后台时触发, 如底部 tab 切换到其他页面或小程序切入后台等。
onUnload() 页面卸载时触发,如redirectTo或navigateBack到其他页面时。
onPullDownRefresh() 下拉刷新的钩子函数 用户下拉刷新时会自动走到这个函数中
onReachBottom() 上翻到底的钩子函数
# 前端核心面试题整合版(含各文档独有信息)
前端核心面试题整合版
一、HTML5 新增特性
-
语义化标签:
<header>
(头部)、<footer>
(底部)、<nav>
(导航)、<main>
(主内容)、<article>
(独立内容)、<section>
(区块 / 逻辑区块)、<aside>
(侧边栏) -
多媒体:
<video>
(视频)、<audio>
(音频)、<source>
(多格式兼容) -
表单增强:新类型(
email
/tel
/date
/range
/color
)、新属性(required
/placeholder
/pattern
) -
绘图存储:
<canvas>
(像素绘图)、<svg>
(矢量图);localStorage
(持久存储)、sessionStorage
(会话存储) -
其他:地理定位(
navigator.geolocation
)、WebSocket(实时通信)、Web Workers(后台线程)
二、JavaScript 核心考点
1. 数据类型
-
基本类型(7 种,栈存储):Number(如 123、NaN)、String(如 “abc”)、Boolean(true/false)、Undefined(未赋值)、Null(空值)、Symbol(ES6,唯一标识)、BigInt(ES2020,大整数,如 9007199254740993n)
-
引用类型(堆存储):Object(如 {name:“a”})、Array(如 [1,2])、Function(如 ()=>{})、Date、RegExp、Map/Set(ES6)
2. 基础核心知识点
-
闭包:函数嵌套,内层函数可访问外层函数变量;用途包括实现防抖节流、模块化开发
-
原型链:每个对象拥有
__proto__
属性,指向其构造函数的prototype
,最终指向Object.prototype
-
异步:Promise(三种状态:pending/resolved/rejected)、async/await(语法糖,解决回调地狱问题)
-
防抖节流:
-
防抖:触发后延迟执行,重复触发则重置延迟时间(如搜索框输入联想)
-
节流:固定时间间隔内仅执行一次(如滚动加载数据)
-
-
深浅拷贝:
-
浅拷贝:通过
...
扩展运算符、Object.assign
实现,仅拷贝表层数据 -
深拷贝:通过
JSON.parse(JSON.stringify)
、递归遍历实现,拷贝所有层级数据
-
3. ES6 新增
-
声明方式:
let
(块级作用域变量)、const
(常量,声明后不可修改) -
函数特性:箭头函数(如 (a,b)=>a+b,无自身 this)、参数默认值(如 fn (a=1){})
-
语法优化:解构赋值(如 [a,b]=[1,2]、{name}={name:“a”})、扩展运算符(如…arr)、模板字符串(如
${变量}
) -
数据结构:Map(键可设为任意类型)、Set(自动去重的集合)
-
其他:
class
(类定义)、import/export
(模块化)、Promise
(异步处理)
4. ES2020 新增
-
基本类型:BigInt(用于表示大整数)
-
语法特性:
-
可选链(
obj?.a?.b
,避免对象属性不存在时报错) -
空值合并(
a??b
,仅当 a 为 null/undefined 时取 b)
-
-
其他:
Promise.allSettled
(等待所有 Promise 完成,无论成功失败)、globalThis
(统一全局对象)、动态import()
(异步导入模块)
三、CSS 基础核心
-
盒模型:
-
标准盒模型:width 仅表示内容宽度,border 和 padding 向外扩展
-
怪异盒模型:width = 内容宽度 + border 宽度 + padding 宽度,需设置
box-sizing:border-box
-
-
选择器优先级:
!important
(最高)> 行内样式(权重 1000)> ID 选择器(权重 100)> 类 / 伪类 / 属性选择器(权重 10)> 元素 / 伪元素选择器(权重 1)> 通配符选择器(权重 0) -
布局方式:
-
Flex 弹性布局:设置
display:flex
,通过justify-content
控制主轴对齐,align-items
控制交叉轴对齐 -
Grid 网格布局:设置
display:grid
,可灵活划分行与列
-
-
定位方式:
-
static:默认值,无特殊定位
-
relative:相对自身原有位置定位,仍占据原有空间
-
absolute:相对最近的已定位父元素定位,不占据原有空间
-
fixed:相对视口定位,固定在屏幕某个位置
-
-
其他:
-
BFC(块格式化上下文):解决浮动塌陷问题,触发方式包括
overflow:hidden
、position:absolute
等 -
清除浮动:通过
clear:both
、伪元素(如::after)实现
-
四、Vue2 核心考点
-
核心特性:双向绑定(通过
v-model
实现,基于Object.defineProperty
)、组件化开发、指令(如v-if
/v-for
/v-bind
) -
v-if
** vs **v-show
:-
v-if
:根据条件添加或删除 DOM 元素,初始条件为假时不渲染,切换开销较大 -
v-show
:通过修改display
属性控制元素显示隐藏,始终渲染 DOM,频繁切换场景更适用
-
-
双向绑定原理:通过
Object.defineProperty
劫持对象属性的get
和set
方法,数据变化时触发set
,通知视图更新;缺点是不支持数组下标修改 -
生命周期:
-
创建阶段:beforeCreate(实例创建前)、create(实例创建后)
-
挂载阶段:beforeMount(DOM 挂载前)、mounted(DOM 挂载后)
-
更新阶段:beforeUpdate(数据更新前)、updated(数据更新后)
-
销毁阶段:beforeDestroy(实例销毁前)、destroyed(实例销毁后)
-
注意:
mounted
及之后阶段才可获取 DOM 元素
-
-
Vuex 核心模块:
-
state:存储全局状态数据
-
mutations:同步修改 state 数据(唯一修改 state 的方式)
-
actions:异步操作,通过调用 mutations 修改 state
-
getters:对 state 数据进行计算处理,类似计算属性
-
-
组件通信:
-
父传子:通过
props
传递数据 -
子传父:通过
$emit
触发自定义事件传递数据 -
兄弟组件:通过 EventBus(事件总线)实现
-
跨级组件:通过
$attrs/$listeners
、Vuex 实现
-
-
常用指令:
v-for
(遍历数据,需添加key
属性)、v-if
(条件渲染,删除 DOM)、v-show
(显示隐藏,修改 display)
五、Vue3 核心考点
-
核心变化:Composition API(组合式 API,通过
setup
函数或<script setup>
语法使用)、响应式原理(基于 Proxy,替代 Vue2 的Object.defineProperty
) -
响应式优势:Proxy 可代理整个对象,支持数组修改、新增属性,解决 Vue2 响应式的局限性;而 Vue2 的
Object.defineProperty
仅能劫持单个属性 -
Composition API 优势:按功能逻辑组织代码(如 “表单处理逻辑”“列表渲染逻辑”),解决 Options API 中代码分散的问题,更适合复杂组件开发
-
ref
** vs **reactive
:-
ref
:用于包装基础类型数据,取值时需通过.value
-
reactive
:用于包装对象 / 数组类型数据,可直接操作数据,底层均基于 Proxy 实现
-
-
Pinia 替代 Vuex 原因:无需
mutations
(可在 actions 中直接修改状态)、原生支持 TypeScript、API 更简洁,是 Vue3 官方推荐的状态管理库 -
<script setup>
** 好处**:无需编写export default
、组件自动注册、支持顶层await
,提升开发效率 -
生命周期:在组合式 API 中需导入使用,如
onMounted
(DOM 挂载后)、onUpdated
(数据更新后) -
其他:Teleport( teleport 组件,可将组件渲染到指定 DOM 节点之外的位置)
六、React 核心考点
-
核心理念:组件化开发(分为函数组件、类组件)、JSX 语法(HTML-in-JS,将 HTML 结构嵌入 JavaScript 代码)
-
函数组件替代类组件原因:更轻量(无
this
指向问题)、通过 Hooks 实现状态管理和生命周期功能;类组件this
指向易混淆、代码冗余 -
useState
/useEffect
** 作用**:-
useState
:定义组件状态,返回[状态值, 修改状态的函数]
(如[val, setVal]
) -
useEffect
:处理组件副作用(如接口请求、事件订阅),可替代类组件的生命周期
-
-
虚拟 DOM/Diff 算法作用:
-
虚拟 DOM:用 JavaScript 对象模拟 DOM 结构,减少真实 DOM 操作
-
Diff 算法:对比新旧虚拟 DOM 的差异,仅更新变化的部分,提升页面渲染性能
-
-
useMemo
/useCallback
** 用例**:-
useMemo
:缓存计算结果,避免组件重渲染时重复计算 -
useCallback
:缓存函数引用,避免子组件因函数重新创建而不必要重渲染
-
-
组件通信:
-
父传子:通过
props
传递数据 -
子传父:父组件传递函数,子组件调用函数传递数据
-
跨级组件:通过 Context 实现
-
全局通信:通过 Redux、React Query 等状态管理库实现
-
-
函数组件:用
function
或箭头函数定义,接收props
参数,返回 JSX 结构 -
其他:React Router(路由管理库,核心组件如
<Route>
、<Link>
)
七、小程序 核心考点
-
目录结构:
-
pages:存放页面相关文件(每个页面包含 js、json、wxml、wxss 文件)
-
app.json:小程序全局配置(如页面路由、窗口样式)
-
app.js:小程序全局逻辑(如全局数据、生命周期)
-
wxss:小程序样式文件,类似 CSS,支持部分扩展语法
-
-
关键页面生命周期:
-
onLoad
:页面加载时触发,仅执行一次 -
onShow
:页面显示时触发,每次切回页面均会执行 -
onReady
:页面首次渲染完成时触发 -
onUnload
:页面卸载时触发
-
-
数据修改方式:需通过
this.setData({key: val})
修改数据(异步更新 DOM),不可直接赋值this.key = val
-
数据绑定和 Vue 的区别:
-
小程序:通过
{{}}
绑定数据,修改数据需调用this.setData
-
Vue:通过
{{}}
绑定数据,直接赋值this.key = val
即可自动更新视图
-
-
路由跳转常用方式:
-
wx.navigateTo
:保留当前页面,跳转到新页面,页面栈深度不超过 10 -
wx.redirectTo
:关闭当前页面,跳转到新页面 -
wx.switchTab
:跳转到 tabBar 页面,关闭其他非 tabBar 页面
-
-
本地存储方式:
-
同步存储:
wx.setStorageSync
(存储)、wx.getStorageSync
(获取),适用于简单场景 -
异步存储:
wx.setStorage
、wx.getStorage
,不阻塞主线程
-
-
接口请求:通过
wx.request
发起请求,需在小程序后台配置合法域名 -
配置合法域名原因:出于安全考虑,小程序限制仅能请求配置过的合法域名;开发环境可勾选 “不校验域名”,生产环境必须配置
八、UniApp 核心考点
-
特点:支持一套代码多端运行(覆盖小程序、App、H5 等平台),基于 Vue 语法开发
-
实现 “一套代码多端跑” 原理:基于 Vue 语法,通过编译器将代码编译为各平台原生代码(如小程序平台编译为微信 / 支付宝小程序代码,App 平台编译为 Android/iOS 原生代码)
-
多端差异处理:通过条件编译实现(语法:
//#ifdef 端标识
///#endif
),例如 App 端使用原生组件,H5 端使用 Web 组件 -
页面生命周期:
-
基础生命周期:同 Vue(
onLoad
/onShow
/onReady
) -
Uni 专属生命周期:
onPullDownRefresh
(下拉刷新)、onReachBottom
(上拉加载) -
注意:UniApp 页面生命周期基于各平台原生生命周期,Vue 生命周期嵌入其中,
onLoad
后触发mounted
-
-
路由:核心跳转方法包括
uni.navigateTo
(跳新页面)、uni.switchTab
(跳 tab 页面)、uni.redirectTo
(关闭当前页跳新页) -
常用 API:
-
接口请求:
uni.request
(多端兼容,替代各平台原生请求 API) -
本地存储:
uni.setStorageSync
(存储)、uni.getStorageSync
(获取)
-
-
组件:使用 UniApp 内置组件(如
<view>
/<text>
/<image>
),类似小程序组件,不支持 Vue 原生<div>
等标签 -
和原生小程序的区别:
-
语法:UniApp 用 Vue 语法,易上手;原生小程序用微信语法
-
开发效率:UniApp 一套代码多端运行,原生小程序需为各平台单独开发
-
九、Electron 核心考点
-
核心架构:
-
主进程:管理窗口、调用原生 API,整个应用仅一个主进程
-
渲染进程:每个窗口对应一个渲染进程,运行 Web 页面
-
通信方式:通过 IPC(Inter-Process Communication)实现进程间通信
-
-
主 / 渲染进程通信:
-
渲染进程→主进程:渲染进程通过
ipcRenderer.send
发送消息,主进程通过ipcMain.on
接收消息 -
主进程→渲染进程:主进程通过
webContents.send
向指定渲染进程发送消息
-
-
打包方式:主要使用
electron-builder
(或electron-packager
),配置目标平台(Windows/Mac)、应用图标等,最终生成.exe(Windows)、.dmg(Mac)等安装文件 -
调用本地文件系统:主进程中可直接使用 Node.js 的
fs
模块;渲染进程默认禁用 Node.js,需通过 IPC 让主进程调用fs
模块实现文件操作 -
优化性能方法:减少渲染进程数量(避免创建过多窗口)、禁用不必要的 Node.js 集成、使用
webview
加载复杂页面(实现进程隔离)
十、AI 前端应用
-
前端调用 AI 接口(如 ChatGPT):通过 Axios 发起 HTTP 请求(需后端转发解决跨域问题),传递
prompt
参数,接收接口返回数据后渲染到页面 -
处理 AI 接口延迟:添加加载动画提升用户体验、使用流式响应(SSE/WebSocket,接收部分数据即渲染)、缓存历史请求结果
-
前端 AI 模型轻量化方案:通过 TensorFlow.js 在前端部署轻量级 AI 模型(如图片分类模型),减少后端请求,降低响应延迟
-
调用 AI 接口安全注意:API 密钥存储在后端(避免前端暴露)、限制接口请求频率(防止滥用)、过滤敏感
prompt
(避免生成违规内容) -
前端 AI 应用常见场景:智能客服(集成 ChatGPT)、图片生成(调用 DALL-E/Stable Diffusion 接口)、文本处理(语法检查、翻译,如调用 DeepL API)
防止触底~~~~
防止触底~~~~
防止触底~~~~
防止触底~~~~
防止触底~~~~
防止触底~~~~
防止触底~~~~
防止触底~~~~
防止触底~~~~
防止触底~~~~
防止触底~~~~
防止触底~~~~
防止触底~~~~
防止触底~~~~
防止触底~~~~
防止触底~~~~
防止触底~~~~
防止触底~~~~