前端面试题(附答案) · 学习笔记

258 阅读15分钟

vue面试题

1、什么是 vue 生命周期

通俗的说就是vue实例从创建到销毁的过程。一个vue实例的生命周期是从开始创建、数据初始化、编译模板、挂载DOM、渲染→更新→渲染、卸载等一系列过程,我们称这是vue的生命周期。

2、vue生命周期的作用是什么

在vue生命周期的不同阶段通过调用对应的勾子函数来实现组件的数据管理和DOM渲染两大重要功能。

3、第一次页面加载会触发哪几个钩子

依次触发beforeCreate、created、beforeMount、mounted。

4、简述每个周期具体适合哪些场景

1).beforeCreate:vue实例还未创建,数据观测和事件初始化还未开始。

2).create:vue实例已创建,实例配置options:data、methods、watch、computed等配置初始化完成,但此阶段渲染的节点还未挂载到DOM上,所以不能访问el属性。在此阶段可以完成数据初始化,对初始化的数据绑定事件的相关操作以及发送ajax请求等。

3).beforeMount:在这个阶段已经编译好模板,并将data里的数据和模板生成html,但还未挂载html到页面上。

4).mounted:完成将模板中的html渲染到页面上,在此阶段可以操作DOM元素,获取渲染元素对应的DOM节点。

5).beforeUpdate:监听到数据更新,此时已经拿到最新的数据但还未渲染到视图上。

6).Update:此阶段已经将更新后的数据渲染到页面上,在页面上可以看到最新的数据。

7).beforeDestory:在实例销毁前调用,此时vue实例以及其配置项都还是可用状态,这个阶段可以做最后的移除定时器和事件绑定操作。

8).destoryed:该阶段vue实例已经完全销毁,vue所指示的所有东西都会解除绑定,事件监听器被移除,所有的vue实例也会被销毁。

5、mvvm 框架是什么?

MVVM是Model-View-ViewModel的简称,即模型、视图、视图模型。模型指的是数据,视图指的是看到的页面,视图模型是MVVM模式的核心,它是连接model和view的桥梁。

MVVM模式有两个数据传递的方式:

一是将模型转化为视图,即将数据转化为所看到的页面,实现的方式是数据绑定。 二是将视图转化为模型,即将所看到的页面转化为数据,实现的方式是DOM事件监听。

总结:在MVVM的框架下视图和模型是不能直接通信的。它们通过ViewModel来通信,ViewModel通常要实现一个observer观察者,当数据发生变化,ViewModel能够监听到数据的这种变化,然后通知到对应的视图做自动更新,而当用户操作视图,ViewModel也能监听到视图的变化,然后通知数据做改动,这实际上就实现了数据的双向绑定。并且MVVM中的View 和 ViewModel可以互相通信。

6、vue-router 是什么?它有哪些组件

vue-router是vue官方的路由管理器。它是vue的核心深度集合,让构建单页面应用易如反掌。

它有 <router-link>、<router-view />两个组件。

7.怎么定义 vue-router 的动态路由? 怎么获取传过来的值

配置路由路径时使用动态路由参数,以冒号(:)开头。通过route.params来获取参数。

8、route 和 router 的区别是什么?

route是路由元对象,包括path,params,hash,query,fullPath,matched,name等路由信息参数。

router是vue-router的实例,相当一个全局的路由器对象,里面包含有很多子对象,例如history对象。使用router.push方法就可以往history对象里加入一条新记录,返回上一条history则是使用router.go方法。

9、query 和 params 之间的区别是什么?

1).query需要在path上添加,params则是在params对象里添加。

2).接收参数时是route.query和route.params。

3).query类似ajax请求中的get,params类似于post,query参数可以在url中看到,params则看不到。

4).params传值刷新页面就没了,query传值刷新还在。

10、vue-router 有哪几种导航钩子?

全局前置守卫beforeEach

全局解析守卫beforeResole

全局后置守卫afterEach

路由独享守卫beforeEnter

组件内守卫:

1).beforeRouterEnter因为守卫在导航确认前被调用,无法访问this。可以通过传一个回调给next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

2).beforeRouteUpdate在当前路由改变,但是该组件被复用时调用,可以访问组件实例this

3).beforeRouteLeave导航离开该组件的对应路由时调用,通常用来禁止用户在还未保存修改前突然离开。可以通过next(false)来取消。

11、vue-router如何响应路由参数的变化?

1).通过watch监听route对象

2).通过beforeRouteUpdate监听

12、vue-router传参

1).通过router-link标签传参,取参方式:route.params。

2).通过path传参,取参方式:route.params。

3).通过query传参,取参方式:route.query。

4).通过params传参,刷新页面传参会丢失,取参方式:route.params。

13、vue-router的两种模式

Vue-Router有两个路由模式,分别是哈希hash模式和历史history模式,然后默认的是哈希hash模式。

hash模式是开发中默认的模式,它的URL带着一个#,例如:www.baidu.com/#/vue,它的has…

hash的值会出现再URL里面的,但是不会出现再HTTP请求之中的,也就是说,它并没有向后端发起请求,对后端是没有影响的。

history模式的URL中没有#,它使用传统的路由分发模式,就是说用户在输入一个URL时,服务器会接收这个请求,并解析这个URL,然后做出相应的处理,也就是它向服务器发起了一个请求。

14、vue-router实现路由懒加载( 动态加载路由 )

1).使用异步组件:resolve => require(['@components/home'], resolve);

2).使用import:

const Home = () => import('@components/home');

加魔法注释:const Home = () => import(/* webpakeChunkName: 'improtFuncDemo' */ '@/components/home') 指定相同chunkName可以打包到同一个js文件。

15、vue的优点

轻量级、高性能、易上手。

vue可以进行组件化开发,组件可复用,减少代码量,提高了开发效率。数据与结构分离,易于理解。vue是单页面应用,页面局部刷新,不用每次跳转都请求数据,加快了访问速度,提升了用户体验。

16、vue传递数据的几种方式

1). 父子组件:

① props & emit:父组件通过v-bind往子组件上绑定数据,子组件可通过props来接收数据。

子组件通过emit(name, params)方法传递数据。

② ref属性:给子组件设置ref属性后,父组件可通过ref来获取子组件实例来调用子组件的方法来进行传值。

2).兄弟组件:

① 通过eventBus总线来进行传值。传输方通过$emit来传输,接收方通过$.on来接收。

② vue3则需安装mitt插件来进行兄弟组件传值。传输方通过emitter.emit()来传值,接收方通过emitter.on()来接收。 3).祖孙组件: ① provide/inject:在组件中提供一个provide数据,此组件内的所有组件都可以通过inject来访问该数据。

17、v-show和v-if指令的共同点和不同点

共同点:都可以实现元素的显示和隐藏。

不同点: 1).实现的本质不同。v-show是通过控制css来实现显示隐藏,v-if则是通过在DOM树上添加或删除来实现。

2).渲染方式不同。v-show不管true还是false都会渲染到DOM树上,v-if为false的话则不会渲染。

3).性能比较。v-show只需要编译一次,而v-if需要不停的销毁创建,故v-show的性能更好一些。

18、如何让CSS只在当前组件中起作用

在vue组件中,只需要给<style>加上scoped属性即可<style scoped>。vue给所有的组件都生成了data-v-xxx的属性,加上scoped后生成css文件的时候会自动把这个属性加到样式表里,从而使得css样式只对当前组件生效。

19、<keep-alive></keep-alive>的作用是什么?

可以实现缓存组件,保留不活动组件的渲染状态,避免组件被重新渲染。

组件可接收3个属性:

include:需要缓存的组件名。

exclude:不需要缓存的组件名。

max:最多缓存的组件数量。

20、vue如何获取DOM?

通过给元素添加ref来获取当前元素。

21、说出几种vue当中的指令和它的用法?

v-show:通过控制元素的display来控制元素显示隐藏,一般用于需要频繁切换显示隐藏的元素。

v-if:往DOM树上添加或销毁元素来控制元素显示隐藏,一般用于判断元素是否渲染。

v-else、v-else-if:必须搭配v-if来使用,在v-if条件不满足时执行。

v-model:双向绑定指令,一般搭配表单元素使用。

v-on:给元素绑定事件,例如v-on:click,v-on:change等,简写符号为@。

v-bind:给元素绑定属性。

v-cloak:防抖指令,用于页面元素防抖,需搭配css使用。

22、vue-loader是什么,它的用途有哪些?

loader用于对模块的源代码进行转化,解析和转换.vue文件。script里可以写es6,style里可以写sass,less。

23、vue为什么要使用key?

需要用key来标识每个节点,这样diff算法才能正确识别此节点。添加key是为了更高效的更新虚拟DOM。

24、axios如何解决跨越?

配置代理可以解决axios不能直接进行跨越的问题。代理:在本地创建一个虚拟的服务端来发送请求,服务端与服务端之间的通信就不会有跨域问题。

25、v-model的使用

v-model双向绑定其实是一个语法糖,它背后的本质是两个操作:v-bind绑定一个value属性,v-on绑定一个input事件。

26、简述computed和watch。

computed:一个数据受多个数据影响。computed有缓存属性,当依赖数据发生变化时,关联的数据才会变化,适用于计算或格式化的场景,例如购物车金额等。

watch:一个数据影响多个数据。当一条数据影响多条数据的时候就使用watch。

27、 nextTick的使用

用于数据动态变化后,DOM树还没及时更新的问题。由于vue的异步更新队列,数据更新后并没有时时渲染到DOM树,这时候操作DOM会出现问题,使用nextTick就可以获取数据变化后最新的DOM。

28、vue组件中data为什么必须是一个函数

因为vue组件是可复用的。而对象是引用类型,里面保存的是内存地址,如果用对象的话就变成所有组件使用同一份data,会造成一个变全部变的影响。而data是函数的话每次复用都会返回一份新的data,各个组件之间的data才会不会互相影响(类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据)。

29、单页面与多页面的区别以及优缺点。

查找资料过程中发现一个博主总结的很好,拿到这里借鉴一下: 6cf8a143424eb5c7c6392f87956e7f02.png

30、v-if和v-for的优先级

在vue2中v-for的优先级比v-if高,两个不能放在一起用,会造成性能浪费,一般利用computed属性过滤一下需要循环的数据再进行循环渲染。

vue3更新了v-for和v-if的优先级,使得v-if的优先级高于v-for。

31、Vue中常用的修饰符有哪些?

1).表单修饰符

lazy:当光标离开标签时,才会将值赋值给value。

trim:过滤掉两边的空格。

number:自动将用户的输入值转为数值类型,但如果这个值无法被parseFloat解析,则会返回原来的值。

2).事件修饰符

stop:阻止事件的冒泡,相当于调用了event.preventPropagation方法。

prevent:阻止了事件的默认行为,相当于调用了event.preventDefault方法。

self:只当在 event.target 是当前元素自身时触发处理函数。

once:绑定了事件以后只能触发一次,第二次就不会触发。

cupture:事件捕获,从顶层往下触发。

passive:用于提升移动端scroll事件的性能。在移动端,当我们在监听元素滚动事件的时候,会一直触发onscroll事件会让我们的网页变卡,因此我们使用这个修饰符的时候,相当于给onscroll事件整了一个.lazy修饰符。

native:如果在自定义组件标签上绑定原生事件,则需要加上.native。

js面试题

1、js的数据类型有哪些

number、string、object、undefined、null、boolean、symbol、bigint

2、let、const、var的区别

var声明的变量具有全局作用域和变量提升特性(可先调用后声明,拿到的值为undefined),且可以重复声明同一变量名(覆盖掉原先的值)。

let和const声明的变量具有块级作用域,不可提前调用,不可重复声明同一变量名。const声明的变量为常量,不能重新赋值。

3、修改原数组的方法

  • push(a,b,c):往数组末尾插入一条或多条数据,并返回更新后数组长度。
  • unshift(a,b,c):往数组开头插入一条或多条数据,并返回更新后数组长度。
  • pop():删除数组最后一项,并返回被删除的元素。
  • shift():删除数组第一个元素,并返回被删除的元素。
  • sort((num1,num2)=>{}):数组排序,按字符ASCII码值进行排序,并返回排序后的数组。
  • reverse():数组反转,并返回反转后的数组。
  • splice(index,howmany,a,b,c):第一个参数为要删除元素的起始索引,第二个参数为从起始索引开始删除的数量,第三个参数为要插入的元素(从起始索引的位置插入),并返回被删除的元素。
  • copyWithin():将指定位置元素复制到指定位置,并返回改变后的数组。
  • fill(data, start, end):自定义数据替换数组指定位置数据,第一个参数为自定义数据,第二个参数为替换起始索引,第三个参数为替截至索引,并返回改变后的数组。

4、遍历数组的方法

for循环:

for(let i=0; i<arr.length; i++){
    arr - 要遍历的数组
    arr[i] - 数组元素
    i - 数组元素下标
}

for of循环:

for(let item of arr){
    item - 数组元素
}

forEach遍历:

arr.forEach((item, index, self)=>{
    item - 数组元素
    index - 对应元素下标
    self - 数组本身
    无返回值
})

map映射:

arr.map((item, index, self)=>{
    item - 数组元素
    index - 对应元素下标
    self - 数组本身
    
    有返回值 - 必返(返回和原数组长度一致的数组,可自定义返回元素表达式)
})

filter过滤:

arr.filter((item, index, self)=>{
    item - 数组元素
    index - 对应元素下标
    self - 数组本身
    
    有返回值 - 选返(可自定义返回条件,返回满足条件元素构成的数组)
})

every检测:

arr.every((item, index, self){
    item - 数组元素
    index - 对应元素下标
    self - 数组本身
    
    有返回值 - 自定义检测条件,有一项不满足条件则返回false并终止检测,
               全部满足则返回true。
})

some检测:

arr.some((item, index, self){
    item - 数组元素
    index - 对应元素下标
    self - 数组本身
    
    有返回值 - 自定义检测条件,有一项满足条件则返回true并终止检测,
               全部不满足则返回false。
})

reduce高阶函数:

arr.reduce(callback, initiaValue)// callback-回调函数,initiaValue-total初始值
let arr = [1,2,3,4]
let num = arr.reduce((total, item, index, self)=>{
    return total + item
})// 无初始值initiaValue,total一开始为数组第一项:1
let num1 = arr.reduce((total, item, index, self)=>{
    return total + item
}, 10)// 有初始值则total一开始为:10
console.log(num)// 10
console.log(num1)// 20

5、遍历对象的方法

for..in..任意顺序遍历对象本身和原型链上可枚举属性(symbol属性除外)。

for(let key in obj){
    // key - 键名
    // obj[key] - 键值
    // obj - 对象本身
    if(obj.hasOwnProperty(key)){
        // 可用hasOwnProperty判断是否属于对象自身属性
    }
}

Object.keys()、Object.values()、Object.entries()可遍历对象自身可枚举属性(不包括symbol)。

let obj = {
    name: 'hxy',
    age: 26,
    sex: 'man'
}
let a = Object.keys(obj) /* ['name', 'age','sex'] */
let b = Object.values(obj) /* ['hxy', 26, 'man'] */
let c = Object.entries(obj) / * [['name', 'hxy'],['age',26],['sex', 'man']] */

Object.getOwnPropertyNames()可遍历对象自身可枚举属性和不可枚举属性(不包括symbol),返回一个由属性名组成的数组,无属性则返回空数组。

let obj = { name: 'hxy', age: 26, sex: 'man' }

/* 设置不可枚举属性 */
Object.defineProperty(obj, 'key1', {
    value: 'no-enumerable',
    writable: true,
    enumerable: false
})

let a = Object.getOwnPropertyNames(obj) /* ['name', 'age', 'sex', 'key'] */

Object.getOwnPropertySymbols()可遍历对象自身symbol属性,返回一个由属性名组成的数组,不存在symbol则返回空数组。

Reflect.ownKeys()可遍历对象自身可枚举属性和不可枚举属性包括symbol属性。返回一个由属性名组成的数组。

6、浏览器缓存数据的方法有哪些

sessionStorage

优点:可以临时储存,关闭页面标签自动回收,不支持跨页面交互。

缺点:只能做为临时储存,不能储存持久化。

localStorage

优点:可以永久储存数据,需手动删除,同一域名下可跨标签交互。

缺点:同一域名下不同标签数据不能独立,互相干扰。

不能被爬虫读取。

在浏览器隐私模式下不能读取。

写入数据量大会卡(FF是将localstorage写入内存中的)。

cookie

优点:兼容性最好,几乎所有的浏览器都支持。

确定:有大小限制,每次请求都会随http一起发送,浪费宽带。