2022最新前端面试题(vue方向)

前言:又到了跳槽旺季,经过几天的收集,整理出了2022年后最新的面试题及答案,坐标武汉,期望薪资15k+的。持续更新,也欢迎各位大佬的评论区补充

1.vue的运行机制

1.初始化

调用vue原型上的_init()初始化,会初始化vue的生命周期,data,methods,props,watch,computed,利用object.definepropty对data里面的属性设置getter和setter函数,来实现响应式和依赖收集。

2.挂载组件

调用$mount挂载组件

3.编译

parse(解析):利用正则将模板转换成抽象语法树;

optimize:标记静态节点,以后update的时候,diff算法会跳过静态节点

generate:将抽象语法树转换成字符串,供render去渲染dom

经过以上步骤可以得到render function

4.响应式

利用object.definepropty设置data所返回的对象后,在进行render function渲染的时候,会对data对象进行数据读取,触发getter函数,从而把data里面的属性进行依赖收集,依赖收集的目的就是将这些属性放到观察者watcher的观察队列中,一旦我们对data里面的数据进行修改时,就会触发setter函数,setter告诉观察者数据变化,需要重新渲染视图,观察者调用update更新视图

5.虚拟dom

render function会被转换成虚拟dom,虚拟dom实际上就是一个js对象,从顶层dom层层描述dom。

6.更新视图

当数据发生变化时,会经历 setter–watcher–update,在update的时候会执行patch,将旧的vnode传进去,通过diff算法算出差异,局部更新视图

2.vue是如何实现数据的双向绑定的,v-model的原理

vue的数据双向绑定主要是指数据变化更新视图,视图变化更新数据

1.对数据对象进行遍历,利用object.defineproperty()对属性都加上getter和setter,这样的话给某个对象赋值都会触发setter,就能检测到数据的变化。

2.结合订阅者发布者模式,订阅者监视数据,一旦数据发生变动,收到通知,调用更新函数进行数据更新。

v-model在内部为不同的输入元素使用不同的属性并抛出不同的事件,例如

text,textarea使用value属性和input事件

check和radio使用checked属性和change事件

select将value作为prop并将change作为事件

3.怎样理解 Vue 的单向数据流?

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。

这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。

4.vue中的data为什么必须是一个函数?为什么需要return返回?

​ vue中data必须是函数是为了保证组件的独立性和可复用性,data是一个函数,组件实例化的时候这个函数将会被调用,返回一个对象,计算机会给这个对象分配一个内存地址,你实例化几次,就分配几个内存地址,他们的地址都不一样,所以每个组件中的数据不会相互干扰,改变其中一个组件的状态,其它组件不变。

​ 不使用return包裹的数据会在项目的全局可见,会造成变量污染;使用return包裹后数据中变量只在当前组件中生效,不会影响其他组件

5.computed 和 watch 的区别和运用的场景?

computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;

watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;

运用场景:

  • 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
  • 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

6.由于js的限制,直接给一个数组项赋值,Vue 能不能检测到变化?

不能,用索引直接设置一个数组项时 或者 当你修改数组的长度时,Vue 不能检测到数组的变动。

为了解决他们,Vue 也提供了操作方法:
Vue.set
vm.$set(Vue.set的一个别名)
Array.prototype.splice
vm.items.splice(修改数组的长度)

7.vue组件传值(父子,隔代,兄弟)

1.父–子传值

子组件通过props接收

2.子传父

1).子组件绑定一个事件,通过$emit()来触发

2).父组件通过callback函数,并把callback函数传给子组件,子组件通过props接收

3).通过 p a r e n t 和 parent和 parentchildren或者$refs访问组件的实例

4). 父组件通过provide提供变量,子孙组件通过inject注入变量。

5).vuex

8.vuex的理解

vuex是vue的状态管理模式,每个vuex的核心就是store(仓库),包含着应用中大部分的状态(state)。

vuex的状态存储是响应式的,当组件从store里获取状态时,当state里面的状态发生改变,相应的组件也会更新。

改变store里的状态唯一的途径就是显式的提交mutation。

主要包括几大模块:

state:单一数据源,定义应用状态的数据结构,设置初始状态。

getters:允许组件从store中获取数据,是store的计算属性。

mutations:是store的methods,保存着更改数据的回调函数,是同步操作,第一个参数state,第二个是自定义参数。

actions:是异步的操作方法,第一个参数是context,是一个与store具有相同的属性和对象,使用commit异步提交mutations里的方法。

modules:模块化,将store分割成模块,是每个模块都有自己的state,getters,mutations,actions

9.Vue 的父组件和子组件生命周期钩子函数执行顺序?

页面加载时:父beforeCreate–父Created-父beforeMount-子beforeCreate-子Create-子beforeMount-子Mounted-父Mounted

子组件更新:父beforeUpdate-子beforeUpdate-子Updated-父Updated

父组件更新:父beforeUpdate-父Updated

组件销毁:父beforedestroy-子beforedestroy-子destroyed-父destroyed

10.在哪个生命周期内调用异步请求?

可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值,推荐created

11.在什么阶段才能访问操作DOM?

mounted

12.Vue 框架怎么实现对象和数组的监听?

通过遍历数组 和递归遍历对象,从而达到利用 Object.defineProperty() 也能对对象和数组(部分方法的操作)进行监听。

observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])  // observe 功能为监测数据的变化
    }
  }

13.Proxy 与 Object.defineProperty 优劣对比。vue2和3的区别

Proxy 可以直接监听对象而非属性;

Proxy 可以直接监听数组的变化;

Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;

Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;

14.vue路由

常用的有hash和history模式

hash:是用的location.hash()获取#后面的为路由地址,在地址栏会带有难看的#。js对location.hash进行赋值,改变url的hash值

history:依赖html5的historyApi和服务器配置,pushState新增历史记录和RepalceState替换当前的历史记录

abstract:支持所有的js运行环境,如果发现没有浏览器api,会自动强制进入这个模式

动态路由:在path后面跟上对应的值,

导航钩子:

全局守卫:

​ 全局前置钩子:router.beforeEach(to,from,next)

​ 全局后置钩子:router.afterEach(to,from),不接受next,也不会改变导航本身

路由独享守卫:beforeEnter(to,from,next)

组件内的守卫:beforeRouteEnter

离开守卫:beforeRouteLeave(to,from,next),路由离开时调用,防止用户未提交页面突然离开

router:vueRouter的对象,是一个全局的实例对象,包含了所有的路由的对象和属性;例如替换路由: r o u t e r . p u s h ( ) , 切 换 路 由 : router.push(),切换路由: router.push()router.replace()

route:跳转的路由对象,每个路由都会有一个route对象,用来获取路由的path,name,params,query

meta:路由元信息,可以在里面配置title和roles(路由的权限访问)

keep-alive:路由缓存,当组件切换时,会保存所有的组件状态,而不是切换页面后又要重新操作,默认情况下会缓存打开的所有组件,如果需要指定缓存哪些组件,需要用include["…","…"]指定。特有的生命周期:activated(组件激活状态),deactivated(组件失活状态)

当路由组件采用缓存后,created和mounted这两个生命周期函数,只会在第一次执行;并且destroyed这个生命周期函数不会执行。
这时候,通常都会配合activated(路由组件激活状态生命周期函数)和deactivated(路由组件失活状态生命周期函数)这两个生命周期钩子。
**注意:**只有当组件在 内被切换,才会有activated 和 deactivated 这两个钩子函数。

15.keep-alive的理解

keep-alive是vue的内置的一个组件,可以使被包含的组件保留状态,避免重新渲染

一般结合路由和动态组件一起使用,用于缓存组件

提供include和exclude属性,都支持字符串和正则表达式,include表示只有名称相匹配的组件会被缓存,exclude表示任何组件都不会被缓存,exclude优先级高于include

16.v-if和v-show

v-if:当初始条件为真的时候才会渲染,否则一直不会渲染

v-show:不管初始条件是什么,元素总会被渲染,基于css的display:none;进行切换

v-if优先级高于v-show

v-if适用于不需要频繁切换条件的场景

v-show适用于需要频繁切换条件的场景

17.webpack原理,构建流程,如何按需加载

根据文件之间的依赖关系对其进行静态分析,将这些模块按照指定规则生成静态资源,webpack处理程序时,会递归的构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将这些模块打包成一个或者多个包(bundle)

主要功能是将多个文件打包成一个文件,减少服务器压力,将预编译语言转换成浏览器识别的语言,性能优化。

特点:

代码拆分:

webpack有同步和异步两种组织模块的依赖方式

只能解析:

有一个智能解析库,几乎可以解析任何一个第三方库

快速运行:

使用异步I/O,多级缓存提高运行效率,使得它有非常快的速度快速增量编译

核心概念:

entry:一个可执行模块和库的入口文件

chunk:多个文件组成的一个代码块,例如一个可执行模块和他所有依赖的模块组成了一个chunk,体现了webpack的打包机制。

loader:文件转换器,例如es6转化成es5,scss转换成css

plugin:插件,扩展webpack的功能,在webpack构建生命周期的节点上加入扩展hook为webpack加入功能

构建流程:

1.解析webpack配置参数,合并从shell传入的和webpack.config.js文件里配置的参数,生产最后的配置结果。

2.注册所有配置的插件,让插件监听webpack构建生命周期的事件节点,并作出对应的反应

3.从配置的entry入口文件开始解析文件构建AST语法树,找出每个文件所依赖的文件,递归下去

4.在解析文件递归的过程中,根据文件类型和loader配置,找出合适的loader对文件进行转换

5.递归完后,得到每个文件最终的结果,根据entry配置生成代码块chunk。

6.输出所有chunk到文件系统

按需加载:

使用code splitting实现按需加载

使用场景:

在最开始使用webpack时,都是将所有的js文件全部打包到build.js里,但是在大型项目中,build.js可能过大,导致页面加载时间过长,这个时候就需要code splitting将文件分割成块(chunk),我们可以定义一些分割点,然后根据这些分割点分割成块,并进行按需加载

18.position 属性

  • static(元素默认的静态定位)
  • relative(相对定位,相对于正常未知进行定位)
  • fixed(固定定位,相对于窗口进行定位)
  • absolute(绝对定位,相对于最近的定位祖先元素进行定位)
  • sticky(粘性定位,根据用户滚动位置进行定位)

19.flex弹性布局

容器属性

  • flex-direction
  • flex-wrap
  • flex-flow
  • justify-content
  • align-items
  • align-content

项目属性

  • order
  • flex-grow
  • flex-shrink
  • flex-basis
  • flex
  • align-self

20.css预处理器

sass:允许定义变量,允许css代码嵌套,函数功能,继承等

定义变量:再有使用相同属性时,可以使用定义变量($)将属性存储到变量中,当需要统一修改相同属性时,直接修改变量即可。

代码嵌套:box>box1{}------> box{box1{}}

函数:复用的代码可以写成一个函数,在有需要的时候调用这个表达式即可,但是这个表达式不产生一个值

$mixin box{}

继承:如果一个样式与另一个样式几乎相同,只有少量的区别,就可以用继承,当使用继承时,可以使用@extend,用法同$mixin,在extend后面就是需要继承的代码

21.css性能问题如何优化

1.减少css嵌套

2.建立公共样式

3.巧用css的继承

4.雪碧图

22,promise

为了代码更加具有可读性和可维护性,我们需要将数据请求与数据处理明确的区分开来

三种状态:

pending: 等待中,或者进行中,表示还没有得到结果

resolved(Fulfilled): 已经完成,表示得到了我们想要的结果,可以继续往下执行

rejected: 也表示得到结果,但是由于结果并非我们所愿,因此拒绝执行

new Promise(function(resolve, reject) {
    
    
    if(true) {
    
     resolve() };
    if(false) {
    
     reject() };
}).then(function(){
    
    
    
}).catch(function(){
    
    
    
})

参数:resolve,reject,都是函数,将状态修改为resolved,rejected

Promise.all([promisea,promiseb,…]).then().catch()

当所有都是执行完成,并且结果都是成功的时候才会执行回调

Promise.race([promisea,promiseb,…]).then().catch()

谁先执行完谁先进入回调,无论结果是成功还是失败,后面的都不再执行

reject:是抛出异常,是promise的方法,当pending为reject的时候,会进入catch

catch:是处理异常,是promis实例的方法

23.js的事件循环机制,单线程,和宏任务微任务

宏任务:每次执行栈执行的代码就是一个宏任务,包括每次从消息队列里获取的一个事件回调放到执行栈执行的,包括script,setTimeout,setInterval等

微任务:在当前宏任务执行结束后立即执行的,在当前宏任务执行结束后,下一个宏任务执行之前,渲染之前的,包括promise.then(),object.observe等

事件循环执行机制: 执行一个宏任务(栈没有就从消息队列中获取)---->执行过程中遇到微任务,就将它添加到微任务的任务队列中---->宏任务执行完毕后,立即依次执行所有的微任务队列中的微任务---->当前宏任务执行完毕开始检查渲染,然后浏览器进行渲染----->渲染完毕后开始下一个宏任务

24.对es6的了解

闭包:在一个函数内部创建另一个函数,函数嵌套函数,函数内部可以引用函数外部的变量和参数,参数和变量不会被垃圾回收机制回收

let,const:let声明变量,const声明常量,两个都有块级作用域,var是功能范围的

解构赋值:允许按照一定模式,从对象和数组中提取值,对变量进行赋值

箭头函数:this不是指向window而是父级,不能用作构造函数,也就是不能使用new,不能够使用arguments对象

模板字符串:(``)加强版的字符串,使用${}可以将表达式与字符串进行拼接,也可以当普通字符串使用

forEach:一般用来遍历数组

for in:一般用来遍历对象或者json,遍历出来的是key

for of:数组对象都可以遍历,遍历出来的是value

import:导入

export:导出

set:用于数据重组,数据不能重复,只有键值,没有键名,可以遍历,方法有add,delete

map:用于数据存储,是键值对的集合,可以遍历,与各种数据格式转换

25.js的数据类型

基本类型(值类型):undefined,number,string,boolear

基本数据类型是按值访问的,我们可以操作保存在变量中的实际的值,基本类型的值是不可改变的,任何方法都不会改变一个基本数据类型的值。改变只是指针和指向的改变,基本数据类型是不能添加属性和方法的。

基本数据类型的值占用固定大小的内存空间,保存在栈中

引用类型:object,array,function,data

引用数据类型实际上是保存在堆内存中,栈内存中保存的是对象在堆内存中的引用地址,可以通过引用地址,可以快速查找到保存在堆内存中的对象

基本数据类型和引用数据类型的区别

声明变量时,不同的内存分配

基本数据类型存储在栈里,就是变量访问的位置。

引用数据类型存储在堆中,存储在变量处的值是一个指针。指向存储对象在堆内存中的地址

不同的访问机制

基本数据类型可以直接访问的到

访问引用数据类型时,先得到的是这个对象在堆内存中的地址,然后按照这个地址去获取这个对象的值,也就是按引用访问

复制变量时不同

基本数据类型是将原始值的副本赋值给新变量,此后两个变量是完全独立的,只是拥有两个相同的value

引用数据类型会将这个对象的内存地址赋值给新变量,两个内存地址指向的是堆内存同一个对象,他们之中任何一个作出改变都会反映到另一个身上

参数传递的不同

基本数据类型是把变量的值传给参数,之后这个参数和变量互不影响

引用数据类型是变量把他里面的值传递给了参数,这个参数也指向原对象,因此在函数内部给这个参数赋值另外一个对象时,这个参数就会更改他的值为新对象的内存地址指向新的对象,但原来的变量还是指向原来的对象

26.深拷贝,浅拷贝

浅拷贝:创建新的数据,这个数据有着原始数据属性值的一份精确拷贝。如果属性是基本类型,拷贝的是基本类型的值,如果属性是引用类型,拷贝的是内存地址。常见的浅拷贝:object.assign,slice(),concat()

深拷贝:开辟一个新的栈,两个对象属性完全相同,但是对应的是不同的地址,修改一个对象的属性,不会改变另一个对象的属性。常见的深拷贝:.cloneDeep(),jquery.extend(),JSON.stringify()

区别:复制对象属性的时候,行为不一样,浅拷贝只复制属性指向某个对象的指针,而不复制对象本身,新旧对象还是共用一块内存,修改对象属性会影响原对象,深拷贝是创造一个一模一样的对象,新对象和原对象不共享内存,修改新对象不会改到原对象

27.前端怎么解决跨域?

jsonP,cors跨域资源共享,nginx反向代理接口跨域,postMessage(data,origin)跨域

28.什么是幽灵节点?怎么解决?

图片下方出现的未知空白,将图片设置为 display:block;

29.说一下对async/await的理解

async await 是用来解决异步的,async函数是Generator函数的语法糖
使用关键字async来表示,在函数内部使用 await 来表示异步
async函数返回一个 Promise 对象,可以使用then方法添加回调函数
当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句

30.setTimeout、Promise、Async/Await 的区别

事件循环中分为宏任务队列和微任务队列

setTimeout的回调函数会被放到宏任务队列里,等到执行队列清空之后执行

promise.then()的回调函数会被放到对应的宏任务的微任务队列里,等宏任务里面的同步代码执行完之后执行

Async函数表示函数里可能会存在异步方法,Await后面跟一个表达式,Async执行时,遇到Await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让执行队列的同步代码先执行

31.原型,原型链,继承

原型:每一个函数都有一个prototype对象属性,指向另一个对象,prototype所有的属性和方法都会被构造函数的实例继承,我们就可以把公用的属性和方法定义在prototype上,这个prototype就是调用构造函数所创建的那个实例的原型。

原型链:实例对象与原型之间的连接。每个对象都有一个proto属性,原型链上的对象正式依靠这个连接在一起。

原型链继承:

将构造函数的原型设置为另一个构造函数的实例对象,这样就可以继承另一个原型对象的属性和方法。

32.等于和恒等于的区别

等于(双等号):先检查两个操作数数据类型,如果相等,再进行===比较,如果不相同,则会进行一次类型转换,转成相同类型再比较。

恒等于(===):数据类型不同,直接false

如果两个数一个是null一个是undefined,相同

33.this指向

1.如果一个函数中有this,但他没有被调用,this指向的是window,严格模式中,this默认指向的是undefined

2.如果一个函数中有this,这个函数有被上一级对象所调用,那么this指向的就是上一级对象

3.如果一个函数中有this,这个函数包含多个对象,尽管这个函数被最外层函数调用,那他也指向的是它的上一级对象

构造函数中的this指向:

当用变量创建了一个函数实例的时候,谁调用了这个函数实例,this指向的就是谁

34.call、apply、bind三者的用法和区别

call ,apply ,bind三者都是改变this指向的方法

call:当前实例通过原型链的查找机制,找到原型的call方法

当call方法执行的时候,首先要把操作的函数中的this关键字变成了call的第一个传递的参数,把call方法第二个及以后的实参获取到,然后把要操作的函数执行,把call方法的第二个及以后的实参传递给函数。

非严格模式下,call会吧第一个参数包装成一个对象,再指向包装后的对象,如果不传参数或者第一个参数传的是null或者undefined,this指向的都是window

严格模式下,第一个参数传的是谁,this就指向谁,包括null和undefined,如果没传,this指向的就是undefined。

apply:跟call基本一致,区别是传递参数的不同

apply需要把给函数传递的参数放到一个数组中传递过去,虽然写的是一个数组,但是也是给函数一个个的传过去

bind:跟call的语法一样,区别在于立即执行还是等待执行,bind不兼容ie6-8

bind会把函数中的this预处理为obj,但此时函数没有执行,当触发事件的时候函数才会执行

35.js防抖和节流

防抖:事件触发n秒后再执行回调,如果n秒内再次被触发,则重新计算时间。(在触发某个事件后,在下一次触发之前,中间的间隔时间如果超过设置的时间才会发送请求,一直触发就不会发送请求)

使用场景:滚动条滚动触发,搜索框输入验证,表单验证,按钮提交事件等

节流:如果连续触发某个事件,则每隔一段时间执行一次

使用场景:dom元素的拖拽功能,射击类游戏,计算鼠标移动距离,监听scroll事件

36.ajax

ajax是异步js和xml,是一种用于创建快速动态网页的技术。

工作原理:

客户端发送请求,请求交给xhr,xhr把请求交给服务,服务器进行业务处理,服务器响应数据交给xhr对象,xhr对象接收数据,由js把数据渲染在页面上。

基本步骤:

1.创建一个异步调用对象(XMLHttpRequest)。

2.创建一个新的http请求,并指定这个请求的方法,url及验证信息。

3.设置响应http请求状态变化的函数,

4.发送http请求。

5.获取异步调用返回的数据。

6.使用js对dom进行局部更新

37.事件冒泡,事件捕获,事件委托

事件冒泡:目标元素事件先触发,然后父元素事件再触发。

事件捕获:父元素事件先触发,然后目标元素事件触发。

事件执行顺序是先对事件进行捕获(window---->document依次往下),然后对事件进行处理,然后是事件冒泡。

阻止事件冒泡的方式:event.stopPropagation(),event.target

事件委托:利用事件冒泡原理,把事件绑定在元素的父级上,当元素被点击时,父级上绑定的事件就会被触发。

事件委托的优点:

减少事件注册,节省内存。

简化dom更新时,相应事件的更新。

缺点:事件委托基于冒泡,对不冒泡的事件不支持,层级过多,冒泡过程中可能被阻止掉

38.js阻塞页面

所有浏览器在下载js文件的时候,会阻塞页面上的其他活动,只有当js下载,执行,解析完才会执行后面的操作。

1.内嵌脚本阻塞

直接写在html中的js代码就是内嵌js,内嵌脚本无需加载,可以直接执行,当页面有内嵌脚本时,可以直接执行,导致阻塞其他资源的加载和页面的呈现

2.外联脚本阻塞

外联脚本只有当页面加载到

为了不阻塞页面,脚本的放置位置

1.尽量合并脚本,减少

2.尽量使用外联脚本,并将脚本放置在body底部

3.使用延迟脚本和异步脚本

4.内嵌脚本放置在window.onload中执行

39.http和https

http:是互联网上应用最广泛的网络协议,是客户端和服务端请求应答的标准,用于从www服务器传输超文本到本地浏览器的传输协议。

https:是以安全为目标的http通道,就是http的安全版,在http里加入的sll层。

https协议的作用主要分两种:一种是建立信息安全通道,保障数据传输的安全,一种是确认网站的真实性

区别:

https需要申请ca证书,

http是超文本传输协议,信息都是明文传输,https则具有安全的ssl加密传输协议。

http和https是完全不同的连接方式,端口号也不同,http是80,https是443

http的连接很简单,是无状态的,https的连接是由http和ssl构建的可进行身份验证,加密传输的协议,比http安全。

猜你喜欢

转载自blog.csdn.net/h5_since/article/details/122922756