90%的面试问题(前端)
-
MVC框架(Model-View-Controller,模型-视图-控制器):
-
Model(模型):代表数据和业务逻辑,负责数据的存储和处理。
-
View(视图):是用户界面的表示,通常是UI元素,例如网页、图形用户界面等。
-
Controller(控制器):是Model和View之间的协调者,负责处理用户输入、更新Model和通知View更新。
-
MVVM框架(Model-View-ViewModel,模型-视图-视图模型):
-
Model(模型):与MVC中的Model相同,代表数据和业务逻辑。
-
View(视图):与MVC中的View相同,是用户界面的表示。
-
ViewModel(视图模型):是连接Model和View的桥梁,它有两个方向:一是将Model转化成View,即将后端传递的数据转化成所看到的页面;二是将View转化成Model,即将所看到的页面转化成后端的数据。
总的来说,MVVM和MVC的主要区别在于MVVM中的ViewModel对Model和View进行了抽象,使得UI和业务逻辑分离。
2.vue双向绑定的原理
在Vue中,当初始化数据时,会对数据进行递归遍历,把每一个属性都转换成getter和setter。通过Object.defineProperty()方法实现数据劫持,当数据变化时,会触发setter,setter会通知所有订阅者,订阅者就会更新页面视图,从而实现了数据双向绑定。
当数据模型发生变化时,Vue会自动更新渲染视图;而当用户交互引起视图发生变化时,Vue会自动更新数据模型,从而实现了数据双向绑定。
总的来说,Vue的双向绑定机制通过对数据的劫持,实现了数据与视图之间的同步更新,提高了开发效率和可维护性。
3、分别阐述vue2和vue3的生命周期
Vue 2 和 Vue 3 的生命周期在某些方面是相似的,但在其他方面则有所不同。以下是两个版本的生命周期的详细阐述:
Vue 2 的生命周期
在 Vue 2 中,一个组件的生命周期通常包括以下阶段:
-
创建阶段:包括 beforeCreate 和 created 两个生命周期钩子。在这个阶段,组件实例化并初始化数据、属性和方法等。
-
挂载阶段:包括 beforeMount 和 mounted 两个生命周期钩子。在这个阶段,组件将模板编译成虚拟 DOM,并将虚拟 DOM 挂载到真实 DOM 上。
-
更新阶段:包括 beforeUpdate 和 updated 两个生命周期钩子。在这个阶段,组件的数据会发生变化,Vue 会重新计算虚拟 DOM 与真实 DOM 的差异,并更新 DOM。
-
销毁阶段:包括 beforeDestroy 和 destroyed 两个生命周期钩子。在这个阶段,Vue 销毁组件实例并解除其所有的事件监听器和子组件。
Vue 3 的生命周期
在 Vue 3 中,由于使用了 Composition API 和 Options API 的混合,生命周期的钩子函数和结构发生了一些变化。以下是 Vue 3 组件的生命周期概述:
-
创建阶段:
-
使用
setup函数和 Composition API(如ref、reactive等)来初始化数据、属性和方法。 -
onMounted钩子在组件挂载后立即调用。
-
-
挂载阶段:
-
onMounted钩子负责将组件挂载到 DOM 上。
-
-
更新阶段:
-
当组件的数据发生变化时,Vue 会重新计算虚拟 DOM 与真实 DOM 的差异,并更新 DOM。
-
onUpdated钩子在更新完成后调用。
-
-
销毁阶段:
-
onUnmounted钩子负责在组件卸载后执行清理操作(如取消网络请求、清除计时器等)。 -
注意:Vue 3 没有提供直接的
beforeDestroy和destroyed钩子,但你可以使用onUnmounted来替代。
-
不带setup
beforeDestroy=beforeUnmounted
destory=unmounted
监听
捕获错误errorCaptured
捕获render:renderTracked、renderTriggered
使用keep-alive
activated 插入
deactivated移除
当组件实例在服务器上被渲染之前要完成的异步函数:serverPrefetch
总结来说,Vue 3 的生命周期更加依赖于 Composition API 和新的生命周期钩子,这使得代码组织更加灵活和可维护。然而,这需要开发者适应新的 API 和模式,以便充分利用 Vue 3 的新功能和性能优化。
4.v-if和v-show有什么区别
v-show只是简单地控制元素的display属性,而v-if是条件渲染,条件为真时,元素被渲染,条件为假时,元素被销毁。
5.async和await是什么?有什么作用?
async和await是es7的语法,作用就是ansync声明一个function是异步的,而await等待一个一部方法完成。很好替代promise的then。
async语法返回一个promise对象,一但遇到await就会先返回。
6.常用的数组方法有哪些
-
join(连接字符):将数组中的元素通过给定的连接字符连成一个字符串。
-
push(值/变量):从数组的末尾向数组添加元素。
-
pop():从数组的末尾删除元素,一次只删除一个,返回的是被删除的元素。
-
shift():从数组的首部删除元素。
-
unshift(值/变量):从数组的首部插入元素。
-
sort():排序函数。默认的是从小到大。若需要降序排序,需要自己写比较函数。
-
reverse():将数组元素颠倒顺序。
-
concat():连接两个或多个数组。
-
slice():切片函数:从原数组中截取部分元素组成新的数组。
slice和substring接收的是起始位置和结束位置(不包括结束位置),而substr接收的则是起始位置和所要返回的字符串长度。
7.数组有哪几种循环方式分别有什么作用?
1.*every()**方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。
2.fi1ter()*方法创建一个新数组,其包含通过所提供函政实现的测试的所有元素。
3.foreach 方法对数组的每个元素执行一次提供的函数。
4.some()**方法测试是否至少有一个元素可以通过被提供的函数方法,该方法返回一个Boolean类型的值。
8、常用的字符串方法
-
charAt()方法从一个字符串中返回指定的字符。
-
concat()方法将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回。
-
include()方法用于判断一个字符串是否包含在另一个字符串中,根据清况返回true或false。
-
indexof()方法返回调用它的ssring.对象中第一次出现的指定值的索引。
-
match()方法检索返回一个字符串匹配正则表达式的的结果。
-
padstar()方法用另一个字符串填充当前字符串(重复,如果需要的话),以便产生的字将串达到给定的长度
-
replace()方法返回一个由替换值。
-
slice()方法提取某个字符串的一部分,并返回一个新的字符串,且不会改动原字符串。
-
split()“方法使用指定的分隔符字符串将一个string对象分割成字符串数组,以将字符串分隔为子字符串,以确定每个拆分的位置。
-
trim()方法会从一个字符串的两端删除空白字符。
9.什么是原型链?
new 一个function方法时候,打印出来会有prototype属性,这个指向它本身,prototype有proto属性又指向构造函数原型对象。
10.什么是闭包?手写一个闭包函数
闭包就是在一个function的方法内部也有一个function并且能访问到其内部变量
function a(){
var a=1;
function b(){
console.log(a)
}
return b;
}
let c=a()
闭包可以设置为私有变量,可以延长作用域,但是会造成内存泄漏。
11.常用的继承有哪些?
-
原型链继承:
特点:1、实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性。(新实例不会继承父类实例的属性!
缺点:1、新实例无法向父类构造函数传参。2、继承单一。3、所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!)。
2.构造函数继承:call,apply
特点:1、只继承了父类构造函数的属性,没有继承父类原型的属性。2、解决了原型链继承缺点1、2、3.3、可以继承多个构造函数属性(call多个)。4、在子实例中可向父实例传参。
缺点:1、只能继承父类构造函数的属性。2、无法实现构造函数的复用。(每次用每次都要重新调用)3、每个新实例都有父类构造函数的副本,臃肿。
3.组合继承。
4.原型继承式:重点:用一个函数包装一个对象,然后返回这个的数的调用,这个函数就变成了个可以随意增添属性的实例或对象。object.create()就是这个原理。特点:类似于复制一个对象,用函数来包装。缺点:1、所有实例都会继承原型上的同性。2、无法实现复用。(新实例属性都是后面添加的)。
5.class继承:通过extend和super
12.后台管理中权限管理如何实现?
登录:当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个token,拿到token之后(我会将这个token存贮到cookie中,保证刷新页面后能记住用户登录状态),前端会根据token再去拉取一个user_info的接口来获取用户的详细信息(如用户权限,用户名等等信息)。权限验证:通过token获取用户对应的权限,动态根据用户的权眼算出其对应有权眼的路由,通过router.addRoutes 动态挂载这些路由。
具体思路:
登录成功后,服务端会返回一个token(该token的是一个能唯一标示用户身份的一个key),之后我们将token存储在本地cookie之中,这样下次打开页面或者刷新页面的时候能记住用户的登录状态,不用再去登录页面重新登录了。
ps:为了保证安全性,我同现在后台所有token有效明(Expires/Max-Age)都是Session,就是当浏览晶关闭了就丢失了。重新打开游览器都需要重新登录验证,后端也会在每周固定一个时间点重新刷新token,让后台用户全部重新登录一次,确保后台用户不会因为电脑遗失或者其它原因被人随意使用账号。
用户登录成功之后,我们会在全局钩子router.beforefach 中拦戴路由,判断是否已获得token,在获得token之后我们就要去获取用户的基本信息了
页面会先从 cookie中查看是否存有token,没有,就走一遍上一部分的流程重新登录,如果有token,就会把这个token 返给后端去拉取user_info,保证用户信息是最新的。当然如果是做了单点登录得功能的话,用户信息存储在本地也是可以的。当你一台电脑登录时,另一台会被提下线,所以总会重新登录获取最新的内容。
先说一说我权限控制的主体思路,前端会有一份路由表,它表示了每一个路由可访问的权限,当用户登录之后,通过token获取用户的role,动态根据用户的role算出其对应有权限的路由,再通过router.addsoutes动态捷载路由。但这些控制都只是页面级的,说白了前端再怎么做权限控制都不是绝对安全的,后端的权限验证是逃不掉的。
使用vuex管理:具体如下:
创建vue实例的时候将vue-router挂载,但这个时候vue-router挂载一些登录或者不用权限的公用的页面。
当用户登录后,获取用role,将role和路由表每个页面的需要的权限作比较,生成最终用户可访问的路由表。
调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由.
14.es6有哪些特性?
let和const具有块级作用域,不存在变量提升的问题。新增了箭头函数,简化了定义函数的写法,同时可以巧用箭头函数的this、(注意箭头函数本身没有this,它的this职决于外部的环境),新增了promise解决了回调地域的问题,新增了模块化、利用import、export来实现导入、导出。新增了结构赋值,ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。新增了class类的慨念,它类似于对象。
15.v-for循环为什么一定要绑定key?
页面上的标签都对应具体的虚拟dom对象(虚拟dom就是js对象),循环中,如果没有唯一key,页面上删除一条标签,由于并不知道删除的是那一条!所以要把全部虚拟dom重新渲染,如果知道key为x标签被删除掉,只需要把渲染的dom为x的标签去掉即可
16.组件中的data为什么要定义一个函数而不是一个对象?
每个组件都是Vue的实例。组件共享data属性,当data为引用其他类型的值时,改变其中一当data的值是同一个会影响到其他。
17.常见盒子居中方法:
flex,定位,块元素
18.平时怎么解决跨域问题的?
在Web开发中,跨域问题是一个常见的挑战。跨域问题指的是在浏览器中,当一个网页的JavaScript代码尝试访问不同域名、不同端口或不同协议的资源时,浏览器为了安全考虑会阻止这种跨域请求。
以下是一些常用的方法来解决跨域问题:
-
JSONP (JSON with Padding):JSONP是一种利用
<script>标签的src属性可以跨域访问的特性来实现跨域请求的方法。它通过在服务器上动态生成一个JavaScript函数调用,以实现跨域数据的传输。但是,JSONP只支持GET请求,并且需要服务器端支持JSONP。 -
CORS(Cross-Origin Resource Sharing):CORS是一种基于HTTP头部的机制,它允许服务器声明哪些源(域名、协议、端口)的请求可以获得资源的访问权限。在服务器端设置合适的CORS响应头,例如
Access-Control-Allow-Origin,可以让浏览器绕过跨域限制。 -
代理服务器:可以设置一个代理服务器,将浏览器的请求发送到同一域名下的服务器,然后由代理服务器转发请求到目标域名上。代理服务器可以在后端进行跨域请求,然后将结果返回给前端。
-
WebSocket:WebSocket是一种基于TCP的协议,它允许在不同域名下建立持久的双向通信连接。通过WebSocket建立连接后,可以在客户端和服务器之间传输数据,绕过跨域限制。
-
使用后端服务:可以将跨域请求的逻辑放在后端服务器上,然后通过与后端服务器进行通信来获取数据。前端向自己的服务器发送请求,再由服务器转发请求到目标服务器上,并将结果返回给前端。这种方式可以避免浏览器的跨域限制。
请注意,具体使用哪种方法解决跨域问题取决于你的应用需求和架构。每种方法都有自己的优缺点,选择适合你项目的解决方案是非常重要的。
19.cookie,localstorage,sessionstorage有什么区别?
Cookie、LocalStorage和SessionStorage是在Web开发中用于在浏览器端存储数据的三种常见方式,它们之间有以下区别:
-
存储容量:Cookie的存储容量较小,通常为4KB左右。LocalStorage和SessionStorage的存储容量较大,通常为5MB或更多。
-
生命周期:Cookie具有过期时间,可以在设置的过期时间之前一直存在于浏览器中。LocalStorage的数据没有过期时间,除非被手动清除。SessionStorage的数据在浏览器会话结束时清除,即当用户关闭浏览器窗口时。
-
数据与服务器的交互:Cookie在每个HTTP请求中都会被发送到服务器,包括对同一域名下的所有请求。LocalStorage和SessionStorage的数据仅在浏览器端存储,不会自动发送到服务器。
-
访问权限:Cookie可以设置为具有域名和路径限制,以控制哪些请求可以访问它们。LocalStorage和SessionStorage只与创建它们的页面相关,同一域名下的其他页面无法直接访问存储在其中的数据。
-
API接口:Cookie的操作需要使用
document.cookieAPI进行读取和设置。LocalStorage和SessionStorage分别提供了localStorage和sessionStorage对象作为API接口,可以通过这些对象进行数据的读取、设置和删除。
总体而言,Cookie适用于在客户端和服务器之间传递数据,而LocalStorage和SessionStorage更适合在浏览器端存储和获取临时数据或用户偏好设置。选择使用哪种存储方式取决于你的需求和数据的特性。
20.this的指向有哪些?
1。普酒函数中的this指向windows
2、定时器中的thi指向windove 3、箭头经数没有this,它的this指向取决于外部环境
4、事件中的this脂向事件的A用者。
5、构造读数中this和原型对象中的this,都是指向构造函数new出来实例对象
6、类class中者的this 指向由constructor构造器nevw出来的实例对象
7.、自调用函数中的this指向window
21.什么是递归,递归有哪些优缺点?
递归:如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解:函数内部自己调用自己,这个街数就是递归函数。
优点:结构清断、可读性强。
缺点:效率低、调用栈可能会溢出,其交每一次函数资用会在内存栈中分配空间。而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。
22.谈谈平时用了哪些方法优化
减少http请求次数、打包压缩上线代码、使用懒加载、使用雪碧图、动态渲染组件,CDN加或包。
23.vue是挂在到哪个标签上的
vue实例最后会挂载在body标签里面,所以我们在vue中是获取不了body标签的,如果要使用body标签的话需要用原生的方式获取。
24.什么是深拷贝,什么是浅拷贝?
浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。
25.js的运行机制是怎么样的?
js是一个单线程、异步、非阻塞I/O模型、 event loop事件循环的执行机制
所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous) .同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
阻塞(Blocking):
阻塞模型是传统的编程模型,它意味着一个操作需要完成才能继续执行下一个操作。例如,如果你有一个需要从磁盘读取大量数据的操作,这个操作可能需要花费一些时间。在阻塞模型中,这个操作会阻止其他代码的执行,直到它完成。这意味着其他代码必须等待这个操作完成才能执行。
在JavaScript中,由于其单线程的特性,阻塞模型可能会导致性能问题。例如,如果你有一个需要长时间运行的循环或操作,它会阻止JavaScript引擎处理其他代码,导致界面响应变慢。
非阻塞(Non-blocking):
非阻塞模型是为了解决阻塞模型的问题而设计的。在非阻塞模型中,长时间运行的操作不会阻止其他代码的执行。相反,这些操作会在后台运行,而其他代码可以继续执行。
在JavaScript中,非阻塞模型主要通过异步编程实现。异步编程是一种处理长时间运行的操作而不阻塞其他代码的方法。例如,你可以使用setTimeout、setInterval、Promise、async/await等JavaScript特性来实现异步编程。
总的来说,阻塞和非阻塞的主要区别在于它们如何处理长时间运行的操作。阻塞模型会阻止其他代码的执行,而非阻塞模型则允许其他代码在长时间运行的操作完成之前继续执行。在JavaScript中,由于其单线程的特性,非阻塞模型是更常见的编程模型,因为它可以避免性能问题并提高用户体验。
26.请写至少三种数组去重的方法?(原生js)
//利用filter
function unique(arr) {
return arr.fi1ter(function(item,index,arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexof(item,0)=n= index;
);
var arr = [1,1, 'true' , 'true ' ,true,true,15,15,fa1se,fa1se,undefined , undefined,nu11 , nu11,NaN,NaN, 'NaN',o,o,'a', 'a',(],03];
console.log(unique(arr))
//利用ES6 set去重(ES6中最常用)
function unique (arr) {
return Array.from(new set(arr))}
var arr = [1,1,'true' , 'true' ,true,true, 15,15 ,fa1se,false,undefined , undefined,nu11, nu11,NaN,NaN, ' NaN',o,o,'a','a',{,];
console.log(unique(arr))
//[1,"true", true,15,false,undefined,nu11,NaN,“NaN",o,"a"",0,0]
//利用for嵌套for,然后splice去重(ESS中最常用)
function unique(arr){
for(var i=0; i<arr.length; i++){
for(var j=i+l;j<arr.length; j++){
if(arr[i]=-arr[j]){
//第一个等同于第二个.splice方法删除第二个
arr.splice(j,1);
j--;
}
}
}
return arr;}
var arr =[1,1, 'true' ,'true ' ,true,true,15,15,fa1se,false,undefined, undefined,nu11,nu11,NaN,NaN, 'NaN',0,0, 'a','a',0,];
console. log(unique(arr))
//[1,"true",15,fa1se,undefined,NaN,NaN,“NaN","a",..},...3]//NaN和门没有去重,两个nu11直接消失了
27.请写出至少两种数组排序方法(原生js)
// 快速排序
function quicksort(elements){
if(elements . 1ength <=1){
return elements;
}
var pivotIndex=Math.floor(elements.length/ 2);
var pivot=elements.splice(pivotIndex,1)[0];
var left=[];
var right=[];
for(var i=0;i<elements . length;i++){
if(elements[i]<pivot){
left.push(elements[i]);
}else{
right.push(elements[i])
}
}
return quicksort(left).concat([pivot] ,quicksort(right));
return quicksort(left).concat([pivot] ,quicksort(right));
// concat()方法用于连接两个或者多个数组:该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
}
var elements=[3,5,6,8,2,4,7,9,1,10];
document.write(quicksort(elements));
//插入排序
function sort(elements){
//假设第0个元素是一个有序数列,第1个以后的是无序数列,//所以从第1个元素开始将无序数列的元素插入到有序数列中去for (var i =1; i<=elements . 1ength; i++) {
1/升序
if(elements[i]< elements[i-1]){
//取出无序数列中的第i个作为被插入元素var guard=elements[i];
//记住有序数列的最后一个位置,并且将有序数列的位置扩大一个var j=i-1;
elements[i]=elements[j];/ /比大小;找到被插入元素所在位置
while (j>=O && guard <elements[j]){
elements[j+1]=elements[j];
j--;
)
elements[j+1]=guard;//插入
)
}
)
var elements=[3,5,6,8,2,4,7,9,1,10];document.write("没调用之前: '+elements);document.write( "<br>');
sort(elements) ;
document.write("被调用之后:'+elements);
//冒泡排序
function sort(elements){
for(var i=0 ;i<elements. length-1;i++){
for(var j=0 ;j<elements.length-1-i;j++){
if(elements[j] > elements[j+1]){
var swap=elements[j];
elements[j]=elements[j+1];elements[j+1]=swap;
)
})
}
var elements=[3,5,6,8,2,4,7,9,1,10];console.log( 'before '+elements);
sort(elements);
console.log( 'after '+elements);
28、知道loadash吗?它有哪些常见API
Lodash是一个一致性、模块化、高性能的JavaScript实用工具库。
_.cloneDeep 深度拷贝 _.reject 根据条件去除某个元素。 _.drop(array, [n=1])作用:将array中的前n个元素去掉,然后返回剩余的部分.
29、http请求方式有哪些
get.post.put、delete等
30、平时用什么工具打包?babel是什么?
WebPack是一个模块打包工具,你可以使用WebPack管理你的模块依赖,并编绎输出模块们所需的静态文件。它能够很好地管理、打包Web开发中所用到的HTML、Javascript、CSS以及各种静态文件(图片、字体等),让开发过程更加高效。对于不同类型的资源,webpack有对应的模块加载器。webpack模块打包器会分析模块间的依赖关系,最后生成了优化且合并后的静态资源。
babel可以帮助我们转换一些当前浏览器不支持的语法,它会把这些语法转换为低版本的语法以便浏览器识别。
31、谈谈set,map
set是es6提供的一种新的数据结构,它类似于数组,但是成员的值都是唯一的。
map是es6提供的一种新的数据结构,它类似于对象,也是键值对的集合,但是键的范围不仅限于字符串,各种类型的值都可以当做键。也就是说,Object结构提供了"字符串一值"的对应,Map结构提供了"值一值""的对应,是一种更完善的Hash结构实现。如果你需要"键值对"的数据结构,Map比 Object更合适.。
32.清楚浮动方法有哪些
为什么要清除浮动,因为浮动的盒子脱离标准流,如果父盒子没有设置高度的话,下面的盒子就会撑上来。
1.额外标签法(在最后一个浮动标签后,新加一个标签,给其设置clear: both; )(不推荐)
2.父级添加overflow属性(父元素添加overflow:hidden)(不推荐)
3.使用after伪元素清除浮动(推荐使用)
33、常见的布局方法有哪些
页面布局常用的方法有浮动、定位、flex、grid网格布局、栅格系统布局 浮动: ·优点:兼容性好。 ·缺点:浮动会脱离标准文档流,因此要清除浮动。我们解决好这个问题即可。绝对定位 ·优点:快捷。 ·缺点:导致子元素也脱离了标准文档流,可实用性差。flex布局(CSS3中出现的) ·优点:解决上面两个方法的不足,flex布局比较完美。移动端基本用flex布局。网格布局(grid) .CSS3中引入的布局,很好用。代码量简化了很多。 利用网格布局实现的一个左右300px中间自适应的布局
34.图片懒加载如何实现的?
就是我们先设置图片的data-set属性(当然也可以是其他任意的,只要不会发送http请求就行了,作用就是为了存取值)值为其图片路径,由于不是src,所以不会发送http请求。然后我们计算出页面 scrollTop的高度和浏览器的高度之和,如果图片距离页面顶端的坐标Y(相对于整个页面,而不是浏览器窗口)小于前两者之和,就说明图片就要显示出来了(合适的时机,当然也可以是其他情况),这时候我们再将data-set属性替换为src属性即可。
35.vue中的computed和watch的区别是什么?
computed计算属性就是为了简化template里面模版字符串的计算复杂度、防止模版太过冗余。它具有缓存特性 computed用来监控自己定义的变量,该变量不在data里面声明,直接在computed里面定义,然后就可以在页面上进行双向数据绑定展示出结果或者用作其他处理; watch主要用于监控vue实例的变化,它监控的变量当然必须在data里面声明才可以,它可以监控一个变量,也可以是一个对象,一般用于监控路由、input输入框的值特殊处理等等,它比较适合的场景是一个数据影响多个数据,它不具有缓存性 watch:监测的是属性值,只要属性值发生变化,其都会触发执行回调函数来执行一系列操作。computed:监测的是依赖值,依赖值不变的情况下其会直接读取缓存进行复用,变化的情况下才会重新计算。
36.什么vuex ,谈谈你对它的理解?
1.首先vuex的出现是为了解决web组件化开发的过程中,各组件之间传值的复杂和混乱的问题2将我们在多个组件中需要共享的数据放到store中, 3.要获取或格式化数据需要使用getters, 4.改变store中的数据,使用mutation,但是只能包含同步的操作,在具体组件里面调用的方式 this. $store. commit('xxxx ')
-
Action也是改变store中的数据,不过是提交的mutation,并且可以包含异步操作,在组件中的调用方式this.$store.dispatch('xxx');在actions里面使用的commit(调用mutation')
37、数据类型的判断方法有哪些,优点和缺点?
typeof:简单;只能检测基本类型
instanceof:能检查引用类型;不能检测基本类型且不支持跨iframe
object.prototype.toString.call 检出所有类型;ie6以下undefined和null为object
38.知道symbol吗
es6停入新的原始类型symbol,表示独一无二的值。
39.请描述一下ES6中的class类
es6中的class可以把它看成是es5中构造函数的语法糖,它简化了构造函数的写法,类的共有属性放到constructor里面 1.通过class关键字创建类,类名我们还是习惯性定义首字母大写。
⒉类里面有个constructor函数,可以接受传递过来的参数,同时返回实例对象 3.constructor函数只要new生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数 4.多个函数方法之间不需要添加逗号分隔5.生成实例new不能省略 6.语法规范,创建类类名后面不要加小括号,生成实例类名后面加小括号,构造函数不需要加function 1.继承中,如果实例化子类输出一个方法.先看子类有没有这个方法,如果有就先执行子类的 ⒉.继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则) 3.如果子类想要继承父类的方法,同时在自己内部扩展自己的方法,利用super调用父类的构造函数,super必须在子类this之前调用 7.时刻注意this的指向问题,类里面的共有的属性和方法一定要加this使用. 1.constructor中的this指向的是new出来的实例对象 2.自定义的方法,一般也指向的new出来的实例对象
3.绑定事件之后this指向的就是触发事件的事件源 4.在ES6中类没有变量提升,所以必须先定义类,才能通过类实例化对象
40、谈谈盒子模型
在标准盒子模型中,width和height指的是内容区域的宽度和高度。增加内边距、边框和外边距不会影响内容区域的尺寸,但是会增加元素框的总尺寸。
41.promis是什么作用?
Promise是异步编程的一种解决方案.简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,可以从改对象获取异步操作的消息。 它可以解决回调地狱的问题,也就是异步深层嵌套问题 .catch() ·获取异常信息.finally() ·成功与否都会执行(不是正式标准)
promise.all:所有的promise都执行完执行,或者执行一次失误后执行。
promise.race:一个promise执行完即可执行
promise之所以无限制的.then 是因为原型链,每次resolve和reject都能返回一个新的promise
42.箭头函数有哪些特征,请简单描述一下它?
箭头函数没有自己的this,this指向定义箭头函数时所处的外部执行环境的this即时调用call/apply/bind也无法改变箭头函数的this 箭头函数本身没有名字 箭头函数不能new,会报错 箭头函数没有arguments,在箭头函数内访问这个变量访问的是外部执行环境的arguments箭头函数没有prototype
43.移动端有哪些问题怎么解决
点击事件30OMS延迟问题解决方案:下载fastclick的包
H5页面窗口自动调整到设备宽度,并禁止用户缩放页面
<meta name="viewport" content="width=device-width,initia1-scale=1.0,minimum-scale=1.0,maximum-scale=1.0, user-scalable=no">
忽略Android平台中对邮箱地址的识别
<meta name="format-detection" content="emai1=no">
当网站添加到主屏幕快速启动方式,可隐藏地址栏,仅针对ios的safari <! -- ios7.0版本以后,safari上已看不到效果-->
<meta name="apple-mobile-web-app-capable" content="yes ">
44.一个页面输入url到页面上加载显示完成,这个过程发生了什么?
01.浏览器查找域名对应的IP地址(DNS查询:浏览器缓存->系统缓存->路由器缓存->ISP DNS缓存->根域名服务器) 02.浏览器向Web服务器发送一个HTTP请求(TCP三次握手) 03.服务器301重定向(从http:/lexample.com重定向到 http://www.example.com)04.浏|览器跟踪重定向地址,请求另一个带 www的网址 05.服务器处理请求(通过路由读取资源) 06.服务器返回一个HTTP响应(报头中把Content-type 设置为'text/htm')07.浏览器进DOM树构建 08.浏览器发送请求获取嵌在HTML中的资源(如图片、音频、视频、CSS、JS等) 09.浏览器显示完成页面 10.浏览器发送异步请求
45.安全问题:CSRF 和XSS攻击?
CSRF:利用A本身漏洞,去请求A的api。http-only
Xss:外部链接。token
46.cookie和session区别
-
1、 cookie数据存放在客户的汶览器上,session数据放在服务器上。 2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗 。考虑到安全应当使用session. ·3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能 。考虑到减轻服务器性能方面。应当使用COOKIE. 4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie.5、所以个人建议: 1。将登陆信息等重要信息存放为SESSION 2。其他信息如果需要保留,可以放在COOKIE中
47.防抖和节流的区别
在浏览器使用点击事件会有300毫秒的延迟,防抖:执行最后一次点击事件,节流:执行第一次点击,推迟事件。
防抖函数的作用是在事件触发后,等待一段时间后执行相应的操作。如果在等待时间内又发生了该事件,那么会重新计时,等待时间重新开始计算。这样可以避免事件频繁触发导致的性能问题。
function debounce(func, delay) {
let timerId;
return function(
节流函数(Throttle): 节流函数的作用是在一定时间间隔内,稀释事件的触发频率,确保一段时间内只执行一次操作。这对于一些频繁触发的事件(如滚动、鼠标移动等)非常有用,可以减少不必要的操作和资源消耗。
function throttle(func, delay) {
let timerId;
let lastExecTime = 0;
return function(
48、为什么要用会话密钥?
会话密钥是在通信过程中生成的临时密钥,有以下几个原因:1.保障机密。2.提供安全。3.提高性能。4.减少密钥管理。
49.SSL如何实现加密通信的?
-
握手过程:客户端向服务器发送加密连接请求,服务器返回证书和公钥。客户端验证证书,并生成临时的对称会话密钥。服务器使用私钥解密会话密钥并确认握手。
-
加密通信:使用会话密钥对通信数据进行加密和解密,确保机密性和安全性。
-
数据完整性验证:使用消息认证码(MAC)验证通信数据的完整性,防止篡改。
-
证书验证:使用数字证书验证服务器和客户端身份。
通过上述步骤,SSL实现了加密通信的目标,确保通信数据的机密性、完整性和安全性。
50.DNS具体解析过程?
-
用户在浏览器中输入域名。
-
操作系统检查本地 DNS 缓存,如果有对应的解析结果,则返回结果。否则,继续下一步。
-
操作系统发送解析请求给本地域名服务器。
-
本地域名服务器查询自身缓存,如果有解析结果,则返回结果。否则,继续下一步。
-
本地域名服务器执行递归查询,向根域名服务器发送查询请求。
-
根域名服务器返回下一级域名服务器的地址。
-
本地域名服务器继续向下一级域名服务器发送查询请求,直到找到权威域名服务器。
-
权威域名服务器返回解析结果给本地域名服务器。
-
本地域名服务器将解析结果返回给操作系统,同时保存在本地 DNS 缓存中。
-
操作系统将解析结果返回给应用程序(如浏览器),以建立连接。
51.双向链表是什么?
双向链表是一种具有前驱指针和后继指针的链表数据结构,允许在正方向和反方向遍历。提供了更高效的插入和删除和查找的操作,但是需要更多的内存空间。
52.ssr问题?vue如何实现ssr?
后端渲染页面返回给前端。
Vue.js提供了官方支持的服务器端渲染(Server-Side Rendering,SSR)解决方案,可以将Vue组件在服务器端进行渲染,生成带有初始内容的HTML页面,然后将其发送给客户端。以下是实现Vue SSR的基本步骤:
-
创建Vue应用:首先,你需要创建一个Vue.js应用程序,包括Vue组件、路由和状态管理等。
-
配置服务器:在服务器端,你需要配置一个Node.js服务器来处理SSR请求。可以使用Express、Koa或其他Node.js框架来搭建服务器。
-
创建渲染器:使用Vue提供的
createRenderer方法创建一个渲染器。渲染器可以将Vue组件渲染为HTML字符串。
-
处理路由请求:在服务器端,根据接收到的路由请求,使用Vue Router将请求的URL与Vue组件关联起来。
-
获取组件数据:在渲染之前,你可能需要获取Vue组件所需的数据。可以使用Vue提供的
asyncData方法或其他异步数据获取方式来获取数据。
-
渲染组件:使用渲染器将Vue组件渲染为HTML字符串,并将组件的数据注入到渲染上下文中。
-
生成HTML:将渲染后的HTML字符串插入到HTML模板中,生成最终的HTML页面。
-
发送响应:将生成的HTML页面发送给客户端。
53.修改vue的组件状态会发生什么?
修改 Vue 组件的状态会触发组件的重新渲染。当组件的状态发生变化时,Vue 会重新计算组件的依赖关系,并重新渲染组件。这可能导致组件的界面重新加载,或者重新显示一些数据。
54.连续检测到两次改变,vue会更新几次?
vue会将两次合并,只更新一次。
55.axios的底层原理是什么?
axios的底层原理是基于底层浏览器api(XMLHttpRequest)和(fetch)通过promise实现的处理方式。
56.什么是vite?
Vite作为一个基于浏览器原生ESM的构建工具,它省略了开发环境的打包过程,利用浏览器去解析imports,在服务端按需编译返回。同时,在开发环境拥有速度快到惊人的模块热更新,且热更新的速度不会随着模块增多而变慢。因此,使用Vite进行开发,至少会比Webpack快10倍左右。
57.ref和reactive的区别,两者之间怎么转化?
ref 和 reactive 是 Vue 3 中用于响应式数据的两种主要方法。它们有一些区别,同时也存在一些转换的方法。数据类型:ref 返回一个响应式对象,而 reactive 返回一个响应式对象或数组。
import { ref, reactive } from 'vue';
const myRef = ref(0);
const myReactive = reactive({ myValue: myRef.value });
import { reactive } from 'vue';
const myReactive = reactive({ myValue: 0 });
const myRef = ref(myReactive.myValue);
58.单点登陆原理
用户在第一次访问需要认证的系统时,输入用户名和密码。认证系统校验用户身份并生成一个加密的Token,并将其存储在认证系统中。用户再访问其他系统时,会带着这个Token进行访问请求。被访问系统接收到请求后,会将Token发送给认证系统进行校验,如果Token有效,则认为用户已经登录,不需要再次输入用户名和密码。认证系统将校验结果返回给被访问系统,被访问系统依据该结果决定是否授权用户访问。
59.CDN是什么?CDN解决什么问题?
CDN的全称是Content Delivery Network,即内容分发网络。它是一种新型的网络架构,主要作用是减少网络拥堵和加速数据传输,从而提高网站的性能和可用性。
CDN采取了分布式网络缓存结构(即国际上流行的Web Cache技术),通过在现有的Internet中增加一层新的网络架构,将网站的内容发布到最接近用户的Cache服务器内,通过DNS负载均衡的技术,判断用户来源就近访问Cache服务器取得所需的内容。这样设计可以提高网络性能,解决跨运营商、跨地区、服务器负载能力过低、带宽过少等带来的网站打开速度慢等问题。
此外,CDN还具有安全保护、统计分析等功能,可以防御DDoS攻击、恶意请求和数据篡改等手段来保护网站的安全,提高网站的稳定性和可靠性;同时提供详细的访问统计和分析报告,帮助网站管理员了解用户的访问情况和行为习惯,从而优化网站的设计和内容,提高用户体验和转化率。
60.keep-alive和多路由复用区别?
keep-alive是缓存动态组件
多路由复用是通过在同一页面中复用多路由实例。通过配置router-view的key值来实现。
61. type和interface的区别?
type关键字用于创建类型别名。它允许你为现有的类型创建一个新的名称,以便在代码中更方便地引用它。通过类型别名,你可以为复杂类型提供一个简洁的名称,或者为联合类型、交叉类型等创建自定义名称。
type Point = {
x: number;
y: number;
};
type Circle = {
center: Point;
radius: number;
};
const c: Circle = {
center: { x: 0, y: 0 },
radius: 5,
};
type A = {
name: string;
age: number;
};
type B = {
address: string;
phone: string;
};
type C = A & B;
const obj: C = {
name: "John",
age: 30,
address: "123 Main St",
phone: "555-1234"
};
interface关键字用于定义对象的形状。它描述了一个对象应该具有哪些属性和方法。接口是一种合同或契约,用于确保对象符合特定的结构。接口继承允许一个接口继承另一个接口的成员,这样可以避免重复定义相同的成员。
interface Point {
x: number;
y: number;
}
interface Circle {
center: Point;
radius: number;
}
const c: Circle = {
center: { x: 0, y: 0 },
radius: 5,
};
interface DerivedInterface extends BaseInterface {
// 添加或覆盖继承的成员
}
62. new Set和new map如何使用
键值对:Set只存储值,而Map存储键值对。也就是说,Set的输入是值,而Map的输入是键和值。
唯一性:在Set中,所有的元素都是唯一的,不会有重复的值存在。而在Map中,键必须是唯一的,但值可以是重复的。
var s = new Set();
s.add(1);
s.add(2);
console.log(s.size); // 输出 2
console.log(s.has(1)); // 输出 true
console.log(s.has(3)); // 输出 false
s.delete(1);
console.log(s.has(1)); // 输出 false
var m = new Map();
m.set('Adam', 67);
m.set('Bob', 59);
console.log(m.size); // 输出 2
console.log(m.get('Adam')); // 输出 67
console.log(m.has('Adam')); // 输出 true
m.delete('Adam');
console.log(m.has('Adam')); // 输出 false
63.new proxy和object.defineprototype的区别?
Object.defineProperty 适用于对现有对象的特定属性进行操作和控制,而 Proxy 更适用于对整个对象或对象的多个属性进行拦截、自定义和控制。
const target = {};
const handler = {
get: function(target, property) {
console.log(`获取属性:${property}`);
return target[property];
},
set: function(target, property, value) {
console.log(`设置属性:${property} = ${value}`);
target[property] = value;
}
};
const proxy = new Proxy(target, handler);
proxy.name = 'John'; // 设置属性:name = John
console.log(proxy.name); // 获取属性:name 输出:John
const obj = {};
Object.defineProperty(obj, 'fullName', {
get: function() {
return this.firstName + ' ' + this.lastName;
},
set: function(value) {
const [firstName, lastName] = value.split(' ');
this.firstName = firstName;
this.lastName = lastName;
},
enumerable: true,
configurable: true
});
obj.fullName = 'John Doe';
console.log(obj.firstName); // 输出:John
console.log(obj.lastName); // 输出:Doe
console.log(obj.fullName); // 输出:John Doe
64.React的hooks有哪些分别有哪些作用?
-
useState:用于在函数组件中添加状态管理。
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
-
useEffect:用于在函数组件中执行副作用操作。
import React, { useState, useEffect } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
useEffect(() => {
// 在组件挂载后执行副作用操作
fetchData();
}, []);
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
const jsonData = await response.json();
setData(jsonData);
};
return (
<div>
{data ? (
<ul>
{data.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
) : (
<p>Loading data
-
useContext:用于在函数组件中访问 React 上下文。
import React, { useContext } from 'react';
const ThemeContext = React.createContext('light');
function ThemeButton() {
const theme = useContext(ThemeContext);
return <button style={{ background: theme }}>Button</button>;
}
-
useReducer:用于在函数组件中管理复杂的状态逻辑。import React, { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
const increment = () => {
dispatch({ type: 'increment' });
};
const decrement = () => {
dispatch({ type: 'decrement' });
};
return (
<div>
<p>Count: {state.count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}-
useCallback:用于在函数组件中缓存回调函数。
-
import React, { useState, useCallback } from 'react';
function ParentComponent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<p>Count: {count}</p>
<ChildComponent onClick={handleClick} />
</div>
);
}
function ChildComponent({ onClick }) {
return <button onClick={onClick}>Increment</button>;
}
-
useMemo:用于在函数组件中缓存计算结果。import React, { useMemo } from 'react';
function ExpensiveCalculation({ a, b }) {
const result = useMemo(() => {
// 执行昂贵的计算
return a * b;
}, [a, b]);
return <p>Result: {result}</p>;
}
-
useRef:用于在函数组件中创建可变的引用。import React, { useRef } from 'react';
function TextInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus</button>
</div>
);
} -
useLayoutEffect:与useEffect类似,但在 DOM 更新之后同步执行副作用操作。
import React, { useLayoutEffect, useState } from 'react';
function MeasureElement() {
const [width, setWidth] = useState(0);
const measureWidth = () => {
const elementWidth = document.getElementById('my-element').clientWidth;
setWidth(elementWidth);
};
useLayoutEffect(() => {
measureWidth();
window.addEventListener('resize', measureWidth);
return () => {
window.removeEventListener('resize', measureWidth);
};
}, []);
return (
<div>
<p>Width: {width}px</p>
<div id="my-element">Element</div>
</div>
);
}
-
useContext:用于在函数组件中访问 React 上下文。
import React, { useContext } from 'react';
const UserContext = React.createContext();
function App() {
return (
<UserContext.Provider value="John">
<UserProfile />
</UserContext.Provider>
);
}
function UserProfile() {
const user = useContext(UserContext);
return <p>Welcome, {user}!</p>;
}
-
useDebugValue:用于在自定义 Hook 中提供调试信息。import React, { useDebugValue, useState } from 'react';
function useCounter(initialCount) {
const [count, setCount] = useState(initialCount);
useDebugValue(`Count: ${count}`);
const increment = () => {
setCount(count + 1);
};
return { count, increment };
} -
useImperativeHandle:用于自定义暴露给父组件的实例值。
import React, { useRef, useImperativeHandle } from 'react';
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
getValue: () => {
return inputRef.current.value;
}
}));
return <input ref={inputRef} />;
}
FancyInput = React.forwardRef(FancyInput);
12.自定义 Hook:你还可以自己创建自定义的 Hook,以便在多个组件中共享逻辑。
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
const response = await fetch(url);
const jsonData = await response.json();
setData(jsonData);
setLoading(false);
};
fetchData();
}, [url]);
return { data, loading };
}
function DataComponent() {
const { data, loading } = useFetch('https://api.example.com/data');
if (loading) {
return <p>Loading data
-
forwardRef是一个高阶组件(Higher-Order Component),它允许你将ref传递给子组件,以便在父组件中访问子组件的 DOM 节点或实例。import React, { forwardRef } from 'react';
const FancyButton = forwardRef((props, ref) => {
return <button ref={ref}>{props.children}</button>;
});
function App() {
const buttonRef = React.createRef();
const handleClick = () => {
// 在父组件中操作子组件的 DOM 节点或实例
buttonRef.current.focus();
};
return (
<div>
<FancyButton ref={buttonRef}>Click me</FancyButton>
<button onClick={handleClick}>Focus Button</button>
</div>
);
}
65.vue中keep-alive有几种状态
-
Inactive(非激活状态):当
<keep-alive>包裹的组件被初始渲染或被切换隐藏时,组件处于非激活状态。在非激活状态下,组件的生命周期钩子函数activated不会被调用。 -
Active(激活状态):当之前缓存的组件再次被显示时,组件进入激活状态。在激活状态下,组件的生命周期钩子函数
activated会被调用,可以在其中执行需要的逻辑。 -
Deactivated(停用状态):当缓存的组件被切换隐藏时,组件进入停用状态。在停用状态下,组件的生命周期钩子函数
deactivated会被调用,可以在其中执行需要的逻辑。
66. vue的solt如何使用?
-
默认插槽:默认插槽是最常见的插槽类型。它允许你在组件的模板中定义一个或多个
<slot>标签,并在使用组件时插入内容。默认插槽没有特定的名称,因此会匹配未命名的<slot>标签。<template>
<div>
<h2>组件标题</h2>
<slot></slot>
</div>
</template>-
具名插槽:具名插槽允许你在组件中定义多个具有特定名称的插槽,并在使用组件时选择性地插入内容。可以使用
<slot>标签的name属性来指定插槽的名称。
-
<template>
<div>
<h2>组件标题</h2>
<slot name="content"></slot>
<slot name="footer"></slot>
</div>
</template>
```
使用具名插槽时,可以在组件使用处通过 `<template>` 标签和 `slot` 特性来指定要插入的具名插槽。
示例:
````vue
<template>
<div>
<MyComponent>
<template #content>
<p>这是内容插槽的内容。</p>
</template>
<template #footer>
<button>保存</button>
<button>取消</button>
</template>
</MyComponent>
</div>
</template>
-
作用域插槽:作用域插槽是一种特殊的插槽类型,它允许你将数据从父组件传递到子组件的插槽内容中。作用域插槽使用
<slot>标签的v-slot或#语法来指定插槽的名称,并使用插槽 Props 来接收传递的数据。
<template>
<div>
<h2>组件标题</h2>
<slot name="content" :data="slotData"></slot>
</div>
</template>
```
使用作用域插槽时,可以在组件使用处通过 `<template>` 标签和 `v-slot` 或 `#` 语法来指定插槽的名称,并使用 Props 来接收数据。
示例:
````vue
<template>
<div>
<MyComponent>
<template v-slot:content="slotProps">
<p>
67.vue2和vue3有什么不同
| vue2 | vue3 | |
|---|---|---|
| 脚手架 | 命令式 | 可视化 |
| API类型 | 选项式 | 组合式 |
| 双向绑定 | 利用ES5的APIObject.definePropert()对数据进行劫持,并结合发布订阅模式方式实现 | 利用ES6的Proxy Api对数据进行代理的方式实现 |
| 根节点 | 只能单个 | 可以多个 |
| 数据监听 | computed watch | computed watch watchEffect |
| 指令优化级 | v-for优先于v-if生效 | v-if优先于v-for生效 |
| Diff算法 | 双端比较算法 | 去头尾的最长递增子序列算法 |
| 生命周期 | beforeCreate,create,beforeMount,mounted,beforeUpdate,updated | setup开始创建组件。onBeforeMount组件挂载之前执行。onMounted组件挂载到页面之后执行。onBeforeUpdate组件更新之前,onUpdated组件更新之后。 |
68.什么是事件循环机制
事件循环(Event Loop)是一种用于处理异步操作的执行机制,常见于JavaScript等语言和环境中。它的目的是确保程序能够以非阻塞的方式处理异步任务,并保持响应性。
事件循环机制的核心思想是将任务分为同步任务和异步任务,并通过事件循环来调度它们的执行。事件循环主要包含以下几个组成部分:
-
调用栈(Call Stack):调用栈是一种用于管理函数调用的数据结构,它记录了当前正在执行的函数的位置。当一个函数被调用时,它会被推入调用栈中,当函数执行完毕后,它会从调用栈中弹出。
-
任务队列(Task Queue):任务队列用于存储异步任务的回调函数。当异步任务完成时,它会将对应的回调函数推入任务队列中。
-
事件循环(Event Loop):事件循环是一个持续运行的循环,它不断地从任务队列中取出任务,并将其推入调用栈中执行。当调用栈为空时,事件循环会检查任务队列,如果任务队列不为空,则将下一个任务推入调用栈中执行。
事件循环的基本流程如下:
-
执行全局同步代码,将同步任务推入调用栈中执行。
-
遇到异步任务(如定时器、网络请求等),将其回调函数注册到对应的异步环境中(如浏览器的Web API)。
-
异步任务完成后,将对应的回调函数推入任务队列中。
-
当调用栈为空时,事件循环会检查任务队列。
-
如果任务队列不为空,则将下一个任务的回调函数推入调用栈中执行。
-
重复步骤4和步骤5,直到任务队列为空。
69.call,apply,bind区别,如何使用?
-
.call方法:.call方法用于调用函数,并指定函数执行时的上下文和参数列表。它接受多个参数,第一个参数是要指定的上下文对象,后续参数是函数的参数列表。function greet() {
console.log(`Hello, ${this.name}`);
}
const person = { name: 'Alice' };
greet.call(person); // 输出: Hello, Alice -
.apply方法:.apply方法与.call方法类似,也用于调用函数并指定函数执行时的上下文和参数列表。不同之处在于.apply方法接受的参数是一个数组或类数组对象,其中第一个元素是要指定的上下文对象,第二个元素及之后的元素是函数的参数列表。function greet(message, punct) {
console.log(`${message}, ${this.name}${punct}`);
}
const person = { name: 'Alice' };
greet.apply(person, ['Hello', '!']); // 输出: Hello, Alice! -
.bind方法:.bind方法用于创建一个新的函数,新函数的执行上下文被绑定到指定的对象。与.call和.apply不同,.bind方法不会立即调用函数,而是返回一个绑定了指定上下文的新函数。function greet() {
console.log(`Hello, ${this.name}`);
}
const person = { name: 'Alice' };
const greetPerson = greet.bind(person);
greetPerson(); // 输出: Hello, Alice
浙公网安备 33010602011771号