🎃 面试摸底
以下所有面试题资源文章由阿凯整理并收录,此栏目为学习内容。供参考学习。此后会不定期更新,谢谢您的关注与支持!
前路漫漫,当克己, 当慎独。工作再忙再累也要抽出时间休息哦!🌷
————送给在互联网行业矜矜业业的的每一位工作者
Gitee开源项目合集
Github开源项目合集
Dcloud开源插件合集
CSDN文章精选合集
语雀文章精选合集
💖Html
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
😚css
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
😘JavaScript
1. Js有哪些数据类型
JavaScript共有八种数据类型
基本数据类型: Undefined、Null、Boolean、Number、String、Symbol、BigInt。
引用数据类型:object,function,array
其中 Symbol 和 BigInt 是ES6 中新增的数据类型:
- Symbol 代表创建后独一无二且不可变的数据类型,它主要是为了解决可能出现的全局变量冲突的问题。
- BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。
2. 数据类型检测的方式有哪些
判断数据类型的方法一般可以通过:typeof、instanceof(英森特赛奥夫)、constructor(肯死抓科特)、toString四种常用方法
不同类型的优缺点 | typeof | instanceof | constructor | Object.prototype.toString.call |
---|---|---|---|---|
优点 | 使用简单 | 能检测出引用类型 | 基本能检测所有的类型(除了null和undefined) | 检测出所有的类型 |
缺点 | 只能检测出除null外的基本数据类型和引用数据类型中的function | 不能检测出基本类型,且不能跨iframe | constructor易被修改,也不能跨iframe | IE6下,undefined和null均为Object |
3.null和undefined区别
- 首先 Undefined 和 Null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null。
- undefined 代表的含义是未定义,null 代表的含义是空对象。一般变量声明了但还没有定义的时候会返回 undefined,null主要用于赋值给一些可能会返回对象的变量,作为初始化。
- undefined 在 JavaScript 中不是一个保留字,这意味着可以使用 undefined 来作为一个变量名,但是这样的做法是非常危险的,它会影响对 undefined 值的判断。我们可以通过一些方法获得安全的 undefined 值,比如说 void 0。
- 当对这两种类型使用 typeof 进行判断时,Null 类型化会返回 “object”,这是一个历史遗留的问题。当使用双等号对两种类型的值进行比较时会返回 true,使用三个等号时会返回 false。
4.如何判断 this 的指向
- 第一种是函数调用模式,当一个函数不是一个对象的属性时,直接作为函数来调用时,this 指向全局对象。
- 第二种是方法调用模式,如果一个函数作为一个对象的方法来调用时,this 指向这个对象。
- 第三种是构造器调用模式,如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。
- 第四种是 apply 、 call 和 bind 调用模式,这三个方法都可以显示的指定调用函数的 this 指向。其中 apply 方法接收两个参数:一个是 this 绑定的对象,一个是参数数组。call 方法接收的参数,第一个是 this 绑定的对象,后面的其余参数是传入函数执行的参数。也就是说,在使用 call() 方法时,传递给函数的参数必须逐个列举出来。bind 方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this 指向除了使用 new 时会被改变,其他情况下都不会改变。
5. for...in和for...of的区别
for…of 是ES6新增的遍历方式,允许遍历一个含有iterator接口的数据结构(数组、对象等)并且返回各项的值,和ES3中的for…in的区别如下
- for…of 遍历获取的是对象的键值,for…in 获取的是对象的键名;
- for… in 会遍历对象的整个原型链,性能非常差不推荐使用,而 for … of 只遍历当前对象不会遍历原型链;
- 对于数组的遍历,for…in 会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for…of 只返回数组的下标对应的属性值;
总结: for...in 循环主要是为了遍历对象而生,不适用于遍历数组;for...of 循环可以用来遍历数组、类数组对象,字符串、Set、Map 以及 Generator 对象。
6. 数组的遍历方法有哪些(没背)
方法 | 是否改变原数组 | 特点 |
---|---|---|
forEach() | 否 | 数组方法,不改变原数组的长度,没有返回值 |
map() | 否 | 数组方法,不改变原数组的长度,有返回值,可链式调用 |
filter() | 否 | 数组方法,过滤数组,返回包含符合条件的元素的数组,可链式调用 |
for...of | 否 | for...of遍历具有Iterator迭代器的对象的属性,返回的是数组的元素、对象的属性值,不能遍历普通的obj对象,将异步循环变成同步循环 |
every() 和 some() | 否 | 数组方法,some()只要有一个是true,便返回true;而every()只要有一个是false,便返回false. |
find() 和 findIndex() | 否 | 数组方法,find()返回的是第一个符合条件的值;findIndex()返回的是第一个返回条件的值的索引值 |
reduce() 和 reduceRight() | 否 | 数组方法,reduce()对数组正序操作;reduceRight()对数组逆序操作 |
7. forEach和map方法有什么区别
这方法都是用来遍历数组的,两者区别如下:
- forEach()方法会针对每一个元素提供执行的函数,如果遍历的元素是引用数据类型,则可以改变指针指向的堆内存里的值,该方法没有返回值;
- map()方法返回一个新数组,新数组中的值为原数组调用函数处理之后的值,如果遍历的元素是引用数据类型,则可以改变指针指向的堆内存里的值
8. 说说你对浅拷贝和深拷贝的理解
浅拷贝:
- 浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝
- 如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址
- 即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址
常见的浅拷贝:
- Object.assign
- Object.create
- slice
- concat()
- 展开运算符
深拷贝
深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性
常见的深拷贝方式有:
- _.cloneDeep()。loadsh
- jQuery.extend()
- JSON.stringify()
- 手写循环递归
9.什么是闭包?
- ✅ 官方说法:闭包就是指有权访问另一个函数作用域中的变量的函数。
- ✅ MDN说法:闭包是一种特殊的对象。它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。
深度回答
浏览器在加载页面会把代码放在栈内存( ECStack )中执行,函数进栈执行会产生一个私有上下文( EC ),此上下文能保护里面的使用变量( AO )不受外界干扰,并且如果当前执行上下文中的某些内容,被上下文以外的内容占用,当前上下文不会出栈释放,这样可以保存里面的变量和变量值,所以我认为闭包是一种保存和保护内部私有变量的机制。
10.闭包的作用
闭包有两个常用的用途;
- 闭包的第一个用途是使我们在函数外部能够访问到函数内部的变量。通过使用闭包,可以通过在外部调用闭包函数,从而在外部访问到函数内部的变量,可以使用这种方法来创建私有变量。
- 闭包的另一个用途是使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收。
11.闭包在项目中的引用场景,以及带来的问题
在实际的项目中,会基于闭包把自己编写的模块内容包裹起来,这样编写就可以保护自己的代码是私有的,防止和全局变量或者是其他的代码冲突,这一点是利用保护机制。
但是不建议过多的使用闭包,因为使用不被释放的上下文,是占用栈内存空间的,过多的使用会导致导致内存泄漏。
解决闭包带来的内存泄漏问题的方法是:使用完闭包函数后手动释放。
12.闭包的使用场景
return
回一个函数- 函数作为参数
- IIFE(自执行函数)
- 循环赋值
- 使用回调函数就是在使用闭包
- 节流防抖
- 函数柯里化
13.什么是作用域链
当在js
中使用一个变量的时候,首先js
引擎会尝试在当前作用域下去寻找该变量,如果没找到,再到它的上层作用域寻找,以此类推直到找到该变量或是已经到了全局作用域,这样的变量作用域访问的链式结构, 被称之为作用域链
深度回答
作用域链的本质上是一个指向变量对象的指针列表。变量对象是一个包含了执行环境中所有变量和函数的对象。作用域链的前端始终都是当前执行上下文的变量对象。全局执行上下文的变量对象(也就是全局对象)始终是作用域链的最后一个对象。
14.作用域链的作用
作用域链的作用是保证对执行环境有权访问的所有变量和函数的有序访问,通过作用域链,可以访问到外层环境的变量和函数。
15.说说Js中的预解析?
JS 引擎在运行一份代码的时候,会按照下面的步骤进行工作:
1.把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值
2.把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用
3.先提升 function,在提升 var
16.变量提升与函数提升的区别?
变量提升
简单说就是在 JavaScript 代码执行前引擎会先进行预编译,预编译期间会将变量声明与函数声明
提升至其对应作用域的最顶端
,函数内声明的变量
只会提升至该函数作用域最顶层
,当函数内部定义的一个变量与外部相同时
,那么函数体内的这个变量就会被上升到最顶端
。
函数提升
函数提升只会提升函数声明式写法,函数表达式的写法不存在函数提升
函数提升的优先级大于变量提升的优先级,即函数提升在变量提升之上
17.什么是箭头函数,有什么特征
使用 "箭头" ( => ) 来定义函数. 箭头函数相当于匿名函数, 并且简化了函数定义
箭头函数的特征:
1- 箭头函数没有this, this指向定义箭头函数所处的外部环境 2- 箭头函数的this永远不会变,call、apply、bind也无法改变 3- 箭头函数只能声明成匿名函数,但可以通过表达、式的方式让箭头函数具名 4- 箭头函数没有原型prototype,即不能用作为构造函数 5- 箭头函数不能当做一个构造函数 因为 this 的指向问题 6- 箭头函数没有 arguments 在箭头函数内部访问这个变量访问的是外部环境的arguments, 可以使用 ...代替
18.说说你对递归函数的理解
如果一个函数在内部调用自身本身,这个函数就是递归函数
其核心思想是把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解
一般来说,递归需要有边界条件、递归前进阶段和递归返回阶段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回
优点:结构清晰、可读性强
缺点:效率低、调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。
19.call、apply、bind三者的异同
共同点 :
- 都可以改变this指向;
- 三者第一个参数都是
this
要指向的对象,没有这个参数或参数为undefined
或null
,则默认指向全局window
不同点:
1.call 和 apply 会调用函数, 并且改变函数内部this指向.
2.call 和 apply传递的参数不一样,call传递参数使用逗号隔开,apply使用数组传递,且apply
和call
是一次性传入参数,而bind
可以分为多次传入
3.bind是返回绑定this之后的函数
- 应用场景
- call 经常做继承.
- apply经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
- bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向
20.说说面向对象的特性与特点
- 封装性
- 继承性
- 多态性
面向对象编程具有灵活、代码可复用、容易维护和开发的有点、更适合多人合作的大型软件项目
21.创建对象有哪几种方法?
字面量的形式直接创建对象
函数方法
(1)工厂模式,工厂模式的主要工作原理是用函数来封装创建对象的细节,从而通过调用函数来达到复用的目的。
(2)构造函数模式
(3)原型模式
(4)构造函数模式+原型模式,这是创建自定义类型的最常见方式。
(5)寄生构造函数模式
(6)动态原型模式
class创建
22.Array 数组对象,数组常用方法
1.join() 将一个数组转成字符串。返回一个字符串
2.reverse() 将数组中各元素颠倒顺序
3.delete 运算符只能删除数组元素的值,而所占空间还在,总长度没变(arr.length)
4.shift()删除数组中第一个元素,返回删除的那个值,并将长度减 1
5.pop()删除数组中最后一个元素,返回删除的那个值,并将长度减 1
6.unshift() 往数组前面添加一个或多个数组元素,长度会改变
7.push() 往数组结尾添加一个或多个数组元素,长度会改变
8.concat() 连接数组
9.slice() 切割数组,返回数组的一部分
10.splice()插入、删除或替换数组的元素
11.toLocaleString() 把数组转换成局部字符串
12.toString()将数组转换成一个字符串
13.forEach()遍历所有元素
14.every()判断所有元素是否都符合条件
15.sort()对数组元素进行排序
16.map()对元素重新组装,生成新数组
17.filter()过滤符合条件的元素
18.find() 查找 返回满足提供的测试函数的第一个元素的值。否则返回 undefined。
19.some() 判断是否有一个满足条件 ,返回布尔值
20.fill() 填充数组
21.flat() 数组扁平化
能影响原来数组的方法 splice() push() pop() shift() unshift() reverse() sort() ...
不会影响原来数组的方法
concat() slice() forEach() toSting() join() reduce() map() filter() slice() findIndex() ...
无返回值的:
forEach()
返回值为新数组的(js哪些数组方法会返回新内存空间--换一种问法):
splice()、concat()、slice()、sort()、reverse()、map()、filter()
23.说一下hasOwnProperty、instanceof方法
hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
instanceof 运算符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
24.什么是原型对象,说说对它的理解
构造函数的内部的 prototype 属性指向的对象,就是构造函数的原型对象。
原型对象包含了可以由该构造函数的所有实例共享的属性和方法。当使用构造函数新建一个实例对象后,在这个对象的内部将包含一个指针(*proto*),这个指针指向构造函数的 原型对象,在 ES5 中这个指针被称为对象的原型。
25.什么是原型链
原型链是一种查找规则
当访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,这种链式查找过程称之为原型链
26. 原型链的终点是什么?
原型链的尽头是null。也就是Object.prototype.*proto*
27.实现继承的方法
1.原型链继承
关键:子类构造函数的原型为父类构造函数的实例对象
缺点:1、子类构造函数无法向父类构造函数传参。
2、所有的子类实例共享着一个原型对象,一旦原型对象的属性发生改变,所有子类的实例对象都会收影响
3、如果要给子类的原型上添加方法,必须放在Son.prototype = new Father()语句后面
2.借用构造函数继承
关键:用 .call() 和 .apply()方法,在子类构造函数中,调用父类构造函数
缺点:1、只继承了父类构造函数的属性,没有继承父类原型的属性。
2、无法实现函数复用,如果父类构造函数里面有一个方法,会导致每一个子类实例上面都有相同的方法。
3.组合继承
关键:原型链继承+借用构造函数继承
缺点:1、使用组合继承时,父类构造函数会被调用两次,子类实例对象与子类的原型上会有相同的方法与属性,浪费内存。
4.原型式继承
关键:创建一个函数,将要继承的对象通过参数传递给这个函数,最终返回一个对象,它的隐式原型指向传入的对象。 (Object.create()方法的底层就是原型式继承)
缺点:只能继承父类函数原型对象上的属性和方法,无法给父类构造函数传参
5.寄生式继承
关键:在原型式继承的函数里,给继承的对象上添加属性和方法,增强这个对象
缺点:只能继承父类函数原型对象上的属性和方法,无法给父类构造函数传参
6.寄生组合继承
关键:原型式继承 + 构造函数继承
Js最佳的继承方式,只调用了一次父类构造函数
7.混入继承
关键:利用Object.assign的方法多个父类函数的原型拷贝给子类原型
- class继承
关键:class里的extends和super关键字,继承效果与寄生组合继承一样
28. 什么是回调地狱?回调地狱会带来什么问题?
回调函数的层层嵌套,就叫做回调地狱。回调地狱会造成代码可复用性不强,可阅读性差,可维护性(迭代性差),扩展性差等等问题。
29. Promise是什么
Promise是异步编程的一种解决方案,它是一个对象,可以获取异步操作的消息,他的出现大大改善了异步编程的困境,避免了地狱回调,它比传统的解决方案回调函数和事件更合理和更强大。
promise本身只是一个容器,真正异步的是它的两个回调resolve()和reject()
promise本质 不是控制 异步代码的执行顺序(无法控制) , 而是控制异步代码结果处理的顺序
30. promise实例有哪些状态,怎么改变状态
(1)Promise的实例有三个状态:
- Pending(进行中)
- Resolved(已完成)
- Rejected(已拒绝)
当把一件事情交给promise时,它的状态就是Pending,任务完成了状态就变成了Resolved、没有完成失败了就变成了Rejected。
如何改变 promise 的状态
- resolve(value): 如果当前是 pending 就会变为 resolved
- reject(error): 如果当前是 pending 就会变为 rejected
- 抛出异常: 如果当前是 pending 就会变为 rejected
注意:一旦从进行状态变成为其他状态就永远不能更改状态了。
😛es6
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
😋Vue2
1. vue生命周期,父组件和子组件生命周期钩子执行顺序是什么??
vue生命周期就是`vue实例从创建到销毁的整个过程`我们称之为vue的生命周期,通过vue的生命周期我们可以在不同的阶段进行不同的逻辑操作. vue生命周期常用的钩子函数一共有8个,分别对应的钩子函数为beforeCreate 创建前、 created创建后、beforeMount 挂载前、mounted挂载后、beforeUpdate 更新前、updated更新后、beforeDestory 销毁前、 destoryed销毁后, `页面一开始加载的时候就会触发创建前后和挂载前后的钩子函数`, 而更新的钩子函数需要当我们改变data的时候才能触发,销毁的钩子函数必须得当组件进行切换的时候就会进行销毁.在项目开发过程中,我经常使用到的钩子函数有created,我们经常`在created进行数据请求,或者获取本地存储的数据`,还有一些其他的操作. 除了created还有mounted,我们经常`在mounted里面获取dom元素` (有时候也存在获取不到dom元素的情况,这个时候我们一般用$nextTick方法来解决).
每个生命周期钩子具体发生的事情:
⑴beforeCreate(创建前):在此生命周期函数执行的时候,data 和 methods 中的数据都还没有初始化。
⑵created(创建后):在此生命周期函数中,data 和 methods 都已经被初始化好了,如果要调用 methods 中的方法,或者操作 data 中的数据,最早只能在 created 中操作。
⑶beforeMount(载入前):在此生命周期函数执行的时候,模板已经在内存中编译好了,但是尚未挂载到页面中去,此时页面还是旧的。
⑷mounted(载入后):此时页面和内存中都是最新的数据,这个钩子函数是最早可以操作 dom 节点的方法。
⑸beforeUpdate(更新前):此时页面中显示的数据还是旧的,但是 data 中的数据是最新的,且页面并未和最新的数据同步。
⑹Updated(更新后):此时页面显示数据和最新的 data 数据同步。
⑺beforeDestroy(销毁前):当执行该生命周期函数的时候,实例身上所有的 data,所有的 methods 以及过滤器…等都处于可用状态,并没有真正执行销毁。
⑻destroyed(销毁后):此时组件以及被完全销毁,实例中的所有的数据、方法、属性、过滤器…等都已经不可用了。
//下面两个钩子函数一般配合使用
⑼activated(组件激活时):和上面的 beforeDestroy 和 destroyed 用法差不多,但是如果我们需要一个实例,在销毁后再次出现的话,用 beforeDestroy 和 destroyed 的话,就太浪费性能了。实例被激活时使用,用于重复激活一个实例的时候
⑽deactivated(组件未激活时):实例没有被激活时。
⑾errorCaptured(错误调用):当捕获一个来自后代组件的错误时被调用
结合实践:
**beforeCreate**:通常用于插件开发中执行一些初始化任务
**created**:组件初始化完毕,可以访问各种数据,获取接口数据等
**mounted**:dom已创建,可用于获取访问数据和dom元素;访问子组件等。
**beforeUpdate**:此时`view`层还未更新,可用于获取更新前各种状态
**updated**:完成`view`层的更新,更新后,所有状态已是最新
**beforeDestroy**:实例被销毁前调用,可用于一些定时器或订阅的取消
**destroyed**:销毁一个实例。可清理它与其它实例的连接,解绑它的全部指令及事件监听器
补充:
父组件和子组件生命周期钩子执行顺序:
`加载渲染过程`:
父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
`更新过程`:
父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
`销毁过程`:
父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
父组件可以监听到子组件的生命周期,使用$emit或者使用@hook:
2. vue路由守卫?
所谓的路由守卫就是当我们`进行页面跳转的时候会触发的钩子函数`,我们把它称之为vue路由守卫. vue一共给我们提供了三种路由守卫,第一种`全局路由守卫`,第二种是`组件内路由守卫`,第三种`路由独享守卫`,这个是写在路由里面. 全局路由守卫包含:beforeEach 前置守卫,beforeResolve 路由解析守卫,afterEach 后置守卫 ,组件内路由守卫:beforeRouteEnter 路由进入之前,beforeRouteUpdate 路由更新之前,beforeRouteLeave 路由离开之前,路由独享守卫:beforEnter 路由进入之前,这几个钩子函数里面都有一个回调函数,这个回调函数里面会有三个参数,分别是`to,from,next`,分别对应的是要进入的路由、离开之前的路由,以及进入写一个路由在项目中我们经常使用路由守卫实现页面的鉴权.
比如:当用户登录之后,我们会把后台返回的token以及用户信息保存到vuex和本地,当页面进行跳转的时候,我们会在路由守卫里面获取vuex里面的token,如果token存在的话,我们则使用next让他进入要跳转的页面,如果token不存在的话我们使用next方法让他回到登录页
3. v-if与v-show的区别?
v-if和v-show都是控制元素的显示与隐藏,
不过v-if控制元素的显示和隐藏的时候会删除对用的dom元素,当每一个显示的时候,都会重新创建dom和渲染.
而v-show则是通过css的`display`:none和display:block来控制元素的显示与隐藏.v-if比较耗费性能,
所以我们涉及到`频繁的显示隐藏操作`我们建议使用`v-show`,如果不是频繁操作的话,我们可以v-if在项目中我会经常使用v-if和v-show,比如我们在搜索功能的时候,他有一个历史记录,这个时候我们根据是否有搜索的结果来判断历史记录的显示与隐藏,这块我就可以使用v-if ,当然用v-show也可以.
4. v-for与v-if的优先级那个高?如果同时使用v-for和v-if怎么解决?
v-for的优先级高. 因为v-for的时候我们才开始渲染dom元素,这个v-if还无法进行判断.v-for和v-if不能同时使用,我们可以通过标签,比如div或者template标签来进行包裹,把v-if写到包裹的标签上面(写到v-for外面)
5. methods、computed和watch的区别?
首先呢,methods是`用来定义方法的区域`,methods定义的方法需要调用才能触发. 不具备缓存,
- computed是计算属性;watch是监听,监听data中的数据变化。
- computed`支持缓存`,当其依赖的属性的值发生变化时,计算属性会重新计算,反之,则使用缓存中的属性值; watch`不支持缓存`,当对应属性发生变化的时候,响应执行。
- computed`不支持异步`,有异步操作时无法监听数据变化;watch`支持异步操作`。
- computed`第一次加载时就监听`;watch默认第一次加载时不监听(immediate 组件创建时刻执行与否,
immediate: true,第一次加载时监听(默认为false),deep 深度监听 不推荐使用(非常的消耗性能))
- computed中的函数必须调用return;watch不是。
使用场景:
- computed:一个属性受到多个属性影响,如:购物车商品结算。
- watch:一个数据影响多条数据,如:搜索数据。
- 数据变化响应,执行异步操作,或高性能消耗的操作,watch为最佳选择
6. vue组件通信方式
1.`父传子` 在子组件的标签上定义属性 子组件通过props来进行接受,可以通过数组的方式进行接受,也可以通过对象的方式来进行接收,如果父组件没有传递属性,子组件可以default来设置默认值
2.`子传父` 子组件通过this.$emit("自定义的事件",要传给父组件的数据), 父组件通过子组件的标签监听自定义的事件,通过方法来接收传递的数据
3.`非父子组件通信` 通过中央事件总线,我们也称之为eventBus,
我们需要创建一个空的js文件,导出这个空的vue实例
传数据的时候 this.$bus.$emit 传
接数据的时候是在 钩子函数 created 中 this.$bus.$on 接收 第一个参数是事件名称 第二个参数是一个回调函数 包含了要接受的数据,以上就是非父子组件通信的方式
7. $nextTick方法有什么作用?
$nextTick也叫做异步更新队列方法,而$nextTick方法的主要作用就是等待dom元素加载完毕之后才会执行的回调函数,我们经常会在$nextTick方法里面`获取dom元素`
比如:beforeCreated获取DOM元素、获取最新的滚动列表
8. 说一下什么是mvvm模式?
MVVM 是Model代表数据模型,数据和业务逻辑都在Model层中定义;View代表UI视图,负责数据的展示;ViewModel负责监听Model中数据的改变并且控制视图的更新,处理用户交互操作;
View 的变化会自动更新到 ViewModel , ViewModel 的变化也会自动同步到 View 上显示。这种自动同步是因为 ViewModel 中的属性实现了 Observer ,当属性变更时都能触发对应的操作。
9. vue常用的指令有哪些?
v-if
v-show
v-html
v-text
v-on
v-bind
v-model
v-for
10. vue常用的修饰符有哪些?
.trim 去除首尾多余的空格
.stop 阻止事件冒泡
.once 只渲染一次
.self 事件只作用在元素本身
.number 将值转化为number类型
.capter 组件之间捕获
.prevent 阻止元素的默认行为
.native 事件穿透,让我们可以在自定义组件上定义事件和方法
11. 说一下你对keep-alive的理解?以及在项目中如何使用?
keep-alive是vue内置的一个组件,而这个组件的作用就是能够缓存不活动的组件,我们能够知道,一般情况下,组件进行切换的时候,默认会进行销毁,如果有需求,某个组件切换后不进行销毁,而是保存之前的状态,比如:说刚刚填好的表单数据。那么就可以利用keep-alive来实现。在搭建 vue 项目时,有某些路由组件没必要多次渲染,所以需要将组件在内存中进行‘持久化’,此时在router-view上使用keep-alive。 keep-alive可以使被包含的路由组件状态维持不变,即便是组件切换了,其内的状态依旧维持在内存之中。在下一次显示时,也不会重新渲染。
include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
max-数字最多可以缓存多少组件。
12. 说一下什么是vue过滤器? 有几种?项目中如何使用,请举例说明?
所谓的vue过滤器就是将数据进行二次处理,得到我们想要的结果数据
vue的过滤器分为两种,第一种是全局过滤器,通过vue.filter来进行定义,第二种是局部过滤器,需要定义在组件内部项目中我们通过过滤器将后台返回的状态0 和1 转化为支付或者未支付
13. 说一下你对slot插槽的理解?
首先呢,所谓的插槽就是一个占位符,`将自定义组件的内容展示出来`.我们知道自定义的组件里面如果写内容的话,页面是不会显示出来的,如果我们想让自定义组件里面的内容显示出来,我们就需要使用slot的插槽.
●`默认插槽`:又名匿名插槽,当slot没有指定name属性值的时候一个默认显示插槽,一个组件内只有有一个匿名插槽。
●`具名插槽`:带有具体名字的插槽,也就是带有name属性的slot,一个组件可以出现多个具名插槽。
●`作用域插槽`:默认插槽、具名插槽的一个变体,可以是匿名插槽,也可以是具名插槽,该插槽的不同点是在子组件渲染作用域插槽时,可以将子组件内部的数据传递给父组件,让父组件根据子组件的传递过来的数据决定如何渲染该插槽。
14. vue中data发生变化,视图不更新如何解决?
因为Vue实例中的数据是响应式的而我们新增的属性并不是响应式的,由于受现在JavaScript的限制,Vue无法检测到属性的新增或删除。所以有时无法实时的更新到视图上。所以我在项目中遇到这类问题的时候一般是通过this.$set方法去解决. this.$set方法一共有三个参数,分别是目前属性,新增属性,新增的值
补充:
this.$set可以使得新添加的属性也是响应式的,并触发视图更新,解决数据改变视图不更新问题
15. 为什么vue中data必须是一个函数?
如果data是一个函数的话,这样每复用一次组件,就会返回一份新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。所以说vue组件的data必须是函数。这都是因为js的特性带来的,跟vue本身设计无关。
16. props设置默认值? props自定义验证函数? default设置对象和普通字符串写法有啥区别
设置默认值:default
自定义验证函数:validator
设置普通字符串: default:String,
设置对象: default:()=>{}
17. vue路由传参方式?
一、`params传参`
this.$router.push({
name:"admin",
//这里的params是一个对象,id是属性名,item.id是值(可以从当前组件或者Vue实例上直接取)
params:{id:item.id}
})
//这个组件对应的路由配置
{
//组件路径
path: '/admin',
//组件别名
name: 'admin',
//组件名
component: Admin,
}
通过params传递参数,如果我们想获取 id 的参数值,可以通过this.$route.params.id这种方式来打印出来就可以得到了;
注意:获取参数的时候是 $route,跳转和传参的时候是 $router
二、`路由属性配置传参:`
this.$router.push({
name:"/admin/${item.id}",
})
//这个组件对应的路由配置
{
//组件路径
path: '/admin:id',
//组件别名
name: 'admin',
//组件名
component: Admin,
}
通过路由属性配置传参我们可以用this.$route.params.id来获取到 id 的值,
注意 this. $router.push 方法里面路径带的是值,路由配置项那里带的是变量名(属性名)来实现的对应;
以上两种传参方式基本上可以理解为 ajax 中的 post 请求方式,参数都是不可见的,但是上面两种方法都有一个弊端,就是当页面刷新了是获取不到参数值的,那么有没有一种方法是页面刷新之后参数依然存在呢?
三、`query传参`
this.$router.push({
name:"/admin",
query:{id:item.id}
})
//这个组件对应的路由配置
{
//组件路径
path: '/admin',
//组件别名
name: 'admin',
//组件名
component: Admin,
}
第三种方式是用 query 来传参,这种方式是可以解决页面刷新参数消失问题的,这种方式可以理解为是 ajax 中的 get 方法,参数是直接在 url 后面添加的,参数是可见的,所以解决页面刷新参数消失问题建议使用此方法来解决;
`区别`
(1)params传参
只能用 name,不能用 path。
地址栏不显示参数名称 id,但是有参数的值。
(2)query传参
name 和 path 都能用。用 path 的时候,提供的 path 值必须是相对于根路径的相对路径,而不是相对于父路由的相对路径,否则无法成功访问。
地址栏显示参数格式为?id=0&code=1
18. Vue事件穿透的方法?
.native 事件穿透,让我们可以在⾃定义组件上定义事件和⽅法
19. v-text v-html 区别?
1.v-text:会把标签转义输出。即原模原样输出,h1标签没效果
2.v-html:会直接输出结果。即h1标签会出效果
20. vue中路由跳转方式?
1.`router-link`
<router-link :to="{path:'/home'}"> 不带参数
<router-link :to="{name:'home', params: {id:1}}"> 带参数
<router-link :to="{name:'home', query: {id:1}}"> 带参数
2.`this.$router.push()` (函数里面调用)
this.$router.push('/home') 不带参数
this.$router.push({path:'/home'})不带参数
this.$router.push({path:'/home',query: {id:'1'}})带参数
this.$router.push({name:'home',params: {id:'1'}}) 带参数
3.`this.$router.replace()` (用法同push)
4.`this.$router.go(n)`
向前或者向后跳转n个页面,n可为正整数或负整数
区别:
this.$router.push 跳转到指定url路径,并向history栈中添加一个记录,点击后退会返回到上一个页面
this.$router.replace 跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)
this.$router.go(n) 向前或者向后跳转n个页面,n可为正整数或负整数
😋Vue3
setup类似于react hooks用法
- setup函数围绕着beforeCreate和created生命周期运行,所以无法使用data和methods重的数据和方法
- setup函数是CompositionAPI(组合API)的入口
- 在setup函数重定义的数据和方法,最后都要return出去,否则无法在模板中使用。
setup注意点:
- setup函数优于created生命周期,所以无法使用data和methods的变量和方法
- 不能在setup函数中使用data和methods,vue官方为了避免此类问题,setup函数中的this修改为undefined
- setup函数,只能是同步,不能是异步!!
示例: 结合ref使用
<template>
<div>
<h1>{{name}}</h1>
<h1>{{age}}</h1>
<button @click="add()">按钮</button>
</div>
</template>
// 引入核心的ref定义数据
import {ref} from 'vue'
export default {
data(){
return{// 现在已经不需要再data中定义数据了}
},
// setup才是我们的重点
setup(){
// ref方法中定义的就是name的值
const name = ref('张三');
// 定义age的值
const age = ref(18);
// 值得注意的是 事件函数也是定义在setup中的
function add(){
// 注意!!要修改变量的值,必须使用.value修改
age.value++
}
// 最后一定要将方法和变量暴露出去
return {
name,age,add
}
}
}
- 复杂数据结构定义和操作
<template>
<div>
<h1 @click="del(i)" v-for="(item,i) in list.list" :key="i">
{{ item }}
<h1>
</div>
</template>
import {reactive} from "vue"
setup(){
// ref只能定义简单数据结构,reactive专门用来定义复杂数据
const list = reactive({
list: [
{id: 1,name: '张三'}
]
})
// 删除数组中的数据
let del = (i)=>{
list.list.splice(i,1)
}
return {
list,del
}
}
- 总结: ref是用于vue3中的基本数据类型的双向绑定
reactive是用于处理对象的双向绑定
- 在setup函数中 传递2个数据 props和context
- props数据格式是响应式,但是注意这里不能使用ES6的解构,否则会让响应式消除
setup(props){
const {a} = props;
// 接受外部传递的数据
console.log(a);
}
- 生命周期
- x和2.x生命周期发生了不同
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
} from "vue";
setup(){
// 在setup中使用生命周期即可
onMounted(){
console.log("组件挂载后阶段")
}
}
- 计算属性和监听器
import {ref,computed,watch} from "vue"
export default {
setup(){
const age = ref(18); //定义数据
// watch监听器
// 第一个参数是监听的值,age.value 表示当 age.value 发生变化就会触发监听器的回调函数,即第二个参数,第二个参数可以执行监听时候的回调
watch(()=> age.value, val=>{
console.log("监听到的val其实就是age.value")
})
// computed计算属性
// 计算属性 computed 是一个方法,里面需要包含一个回调函数,当我们访问计算属性返回结果时,会自动获取回调函数的值:
const doubleCount = computed(() => age.value * 2)
return {
age,
doubleCount
}
}
}
- 获取路由
Vue 3.0 中通过 getCurrentInstance 方法获取当前组件的实例,然后通过 ctx 属性获得当前上下文, ctx.$router 是 Vue Router 实例,里面包含了 currentRoute 可以获取到当前的路由信息
import {getCurrentInstance } from "vue"
setup(){
// ctx获取上下文
const {ctx} = getCurrentInstance()
console.log(ctx.$router.currentRoute.value)
// 扩展 如果要获取仓库store数据 一样是在计算属性中
const a = computed(() => ctx.$store.state.test.a)
return {
a
}
}
- 组件的引入没有变化
<template>
<div id="app">
<Stu />
</div>
</template>
import Stu from '@/components/Stu.vue'
export default {
name: 'App',
components: {
Stu
}
}
- Vue2和Vue3双向绑定
- v2 是Object.defineProperty
- v3 是proxy
Vue2.0
Object.defineProperty(target, key, {
get() {
return value;
},
set(newValue) {
//深度监听
observer(value);
if (newValue !== value) {
value = newValue;
updateView();
}
}
});
Vue3.0
const proxyData = new Proxy(data, {
get(target,key,receive){
// 只处理本身(非原型)的属性
const ownKeys = Reflect.ownKeys(target)
if(ownKeys.includes(key)){
console.log('get',key) // 监听
}
const result = Reflect.get(target,key,receive)
return result
},
set(target, key, val, reveive){
// 重复的数据,不处理
const oldVal = target[key]
if(val == oldVal){
return true
}
const result = Reflect.set(target, key, val,reveive)
console.log('set', key, val)
return result
},
deleteProperty(target, key){
const result = Reflect.deleteProperty(target,key)
console.log('delete property', key)
console.log('result',result)
return result
}
})
- vue2和vue3的不同
- 弃用全局API new Vue,使用createApp const app = Vue.createApp({})
- 弃用Vue.prototype,在vue3中定义使用如下: const app = Vue.createApp({}) app.config.globalProperties.$http=() => {}
- 不再使用this.$nextTick 方法 import {nextTick} from 'vue' nextTick(()=>{ // 执行的操作 })
- filter被移除
😋React
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
😋Uniapp
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
😋App
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃
- 客官先别急,作者正在飞速收录中~😃