Vue高频面试

一、 生命周期

1.1 说一下生命周期

1.2页面(组件)一加载会执行哪些生命周期

追问:执行这些生命周期的时候什么时候有节点和数据? 节点:this.$el 数据:this.$data created以及created之后有节点 mounted以及mounted之后有数据
不使用keep-alive的前提下,执行
beforeCreate 没有节点,没有数据
created 没有节点,有数据
beforeMount 没有节点,有数据
mounted 有节点,有数据

1.3 生命周期都怎么用?,你用哪些?

我一般这样子用:
created 发生请求,获取数据
mounted 去操作dom相关的行为,例如:写入插件better-scroll,获取某个元素的高度等等
追问: 你为什么不去beforeCreate 、beforeMount、mounted 发生请求? 在beforeCreate中发送请求,没有节点,也没有数据,一般不做什么操作,把请求放到mounted中,可能会出现页面闪动的情况(此时页面dom结构已经渲染完成,之后获取到数据,又得挂载数据),放到created中可以避免这种情况发生,因此建议在created发送请求。 如果在beforemount中发送请求,获取数据,那么就可能造成阻塞。

二、路由

路由
路由就是SPA(单页应用)的路径管理器,适合用于构建单页面应用。vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。传统的页面应用,是用一些超链接来实现页面切换和跳转的。在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。路由模块的本质 就是建立起url和页面之间的映射关系

2.1 路由传值(显式,隐式)

显式:query:{},接收参数:this. r o u t e . q u e r y 隐式: p a r a m s : , 必须有 n a m e 属性因为 p a r a m s 只能用 n a m e 来引入路由,如果这里写成了 p a t h ,接收参数页面会是 u n d e f i n e d !!!,接收参数: t h i s . route.query 隐式:params:{},必须有name属性因为params只能用name来引入路由,如果这里写成了path,接收参数页面会是undefined!!!,接收参数:this. route.query隐式:params,必须有name属性因为params只能用name来引入路由,如果这里写成了path,接收参数页面会是undefined!!!,接收参数:this.route.params,
区别:
二者还有点区别,直白的来说query相当于get请求,页面跳转的时候,可以在地址栏看到请求参数,刷新页面还在,而params相当于post请求,参数不会再地址栏中显示,刷新页面不在了.

2.2、this. r o u t e r 和 t h i s . router 和this. routerthis.route有何区别?

  • 1. r o u t e r 为 V u e R o u t e r 实例,想要导航到不同 U R L ,则使用 router为VueRouter实例,想要导航到不同URL,则使用 routerVueRouter实例,想要导航到不同URL,则使用router.push方法
  • 2.$route为当前router跳转对象,里面可以获取name、path、query、params等
  • 在这里插入图片描述

2.3路由的模式和区别

hash
hash 模式的路由中带有 # 号
hash 模式通过 window.onhashchange 方法监听路由的修改
hash 模式在页面刷新的时候,发送的请求 url 是不带 # 后面的内容的
hash 模式可以兼容部分低版本的浏览器
hash 模式打包后,直接在浏览器中打开 /dist/index.html 可以正常的访问(原因是第 3 条指出的原因

history
history 模式是使用正常的 url 路径显示
history 模式通过 pushState 和 replaceState 方式修改路由改变
history 模式在页面刷新的时候,会请求当前地址栏中完成的 url,这时需要服务器对这个 url 有处理,如果没有对应的文件,需要返回 index.html
history 模式因为是使用的 HTML5 的新规范,所以不能兼容低版本的浏览器
history 模式打包后,直接在浏览器中打开 /dist/index.html 会报错(原因是第 3 条指出的原因)

在这里插入图片描述

2.4路由导航守卫(拦截)

路由相关面试题
含义是:跳转到某个路由之前做检查及操作,比如说:在订单页面需要先登录后才能访问,
分为:

  • 全局路由守卫 beforeEach
    • 前置守卫:一般会用来判断用户是否登录,未登录则跳转登录页
    • 后置守卫:afterEach
    • 和beforeEach不同的是afterEach不接收第三个参数 next 函数,也不会改变导航本身
  • 全局解析守卫: router.beforeResolve
    • 在 2.5.0+ 你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是:在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
  • 组件路由守卫
    • 在组件中使用路由
beforeRouteEnter (to, from, next) {
   		 // 在渲染该组件的对应路由被 confirm 前调用
	    // 不!能!获取组件实例 `this`
	    // 因为当守卫执行前,组件实例还没被创建
	  },
	  beforeRouteUpdate (to, from, next) {
	    // 在当前路由改变,但是该组件被复用时调用
	    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
	    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
	    // 可以访问组件实例 `this`
	  },
	  beforeRouteLeave (to, from, next) {
	    // 导航离开该组件的对应路由时调用
	    // 可以访问组件实例 `this`
	  }
  • 路由独享守卫 beforeEnter
    • 这个守卫是写在路由里面的,只有当进入这个路由时才会调用的,这些守卫与全局前置守卫的方法参数是一样的

路由三个参数:

  • to 要去那个路由
  • from 来自那个路由
  • next 是一个方法,它接受参数。这个方法必须调用要不就跳不过去了,你可以把它看做保安。必须给它打个招呼,要不然不让你过。
    • next()。这就是告诉保安我要过去,去哪里呢? 就是to了。放行
    • next(false) 中断跳转
    • next({‘path’: ‘/’}) 跳转到某个路由
    • next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError()注册过的回调。

2.5 动态路由和子路由

  • 动态路由:名称/:id 应用场景:新闻页、多商品详情页
  • 子路由 某个路由下添加children属性,方便维护,统一管理路由
  • 路由配置:
    1. 安装路由
    2. router.js中配置路由规则,通过new VueRouter () 传入路由规则routes,路由模式等参数并返回路由对象,然后到处对象
    3. 在mian.js导入路由,注册路由,然后再Vue实例挂载路由
    4. 可以通过路由链接或者push、replace等方式来实现路由跳转,之后就会去路由规则进行路径的匹配,如果匹配成功,就跳转到对应的组件,通过路由占位符显示出组件的内容。

2.6 路由导航守卫-如何实现登录跳转限制

在这里插入图片描述
白名单:那些不需要token就可以直接访问的页面

三、keep-alive

是什么?缓存组件中的数据,将组件的状态保存在内存中,防止重复渲染DOM。
应用场景:单页面应用keep-alive,比如:对tabber导航栏使用keep-alive,当登录用户的时候,发现数据并没有更新,还是上一个用户登录的数据,但是又得对tabbar导航栏使用keep-alive。可以使用vuex来进行动态得控制keep-alive得缓存,当登录成功,就移除掉tabbar导航栏的缓存,当进入到tabbar的导航栏的时候再将缓存添加上去。
多出两个生命周期 activated和deactivated
第一次进入:created => … => activated =>
离开触发 deactivated
后续进入:activated
离开触发 deactivated

参数:

  • include - 名称匹配的组件会被缓存。
  • exclude - 名称匹配的组件都不会被缓存。
  • max 最多可以缓存多个组件实例

四、ref

4.1 是什么?vuet通过ref操作dom
4.2 使用: <h1 ref="xxx></h1> this.$refs.xxx
4.3 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例

五、nextTick

5.1 是什么?

在数据变化后要执行的某个操作,而这个操作需要使用随数据的变化而变化的Dom结构,这个操作就要放进vue.nextTick()的回调函数中,比如:在data中定义一个数据message,在页面中使用这个message,在对message进行多次相同的修改,再打印message数据,结果会发现打印出来的结果不是最新的数据,而是之前的数据,这是因为vue的更新时异步的更新,它不会立即做dom更新,而是把数据变化存入到异步队列当中,对于相同的操作,异步队列会去重,当所有的数据变化后,再去执行队列的任务,更新dom。nextTick本质是一种优化策略。

5.2 为什么要有nextTick?
例如:使用1万次的循环修改data的message数据,那它将进行1万次的视图更新,非常消耗性能,如果有nextTick的话,它只需要更新1次就可以了,nextTick本质是一种优化策略。
原理

在这里插入图片描述
接着说, 降级方案:
1 promise方案,如果说支持promise的话,就把回调函数加入到队列中2. MutationObserver方案,是HTML5中的新API,是个用来监视DOM变动的接口。他能监听一个DOM对象上发生的子节点删除、属性修改、文本内容修改等等。如果说支持MutationObserver的话,通过new MutationObserver()把回调函数当作构造函数的参数,并返回一个对象,通过这个对象调用observe方法,去监听你需要监听的节点,以及节点相关的操作,比如: 删除,修改等操作,监听到dom更新后,就会触发回调函数,
3. setTimeout方案,也是最后没有办法的方案 ,执行setTimeout,把回调函数传入到队列中

为什么要使用异步队列?能够确保dom更新和nextTick回调函数先后执行,nextTick拿到的是dom更新后的内容,所以dom更新要先执行完成。
源码分析

六、axios二次封装

6.1 为什么要封装:

  • 每次请求数据都会加载中有数据后就关闭加载中
  • 有些请求需要携带token去验证
  • 总结:方便维护,后面使用简单,复用性高

七、vuex

7.1 vuex是什么?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理插件
把多个组件共享的变量全部存储到一个对象中

7.2 哪些属性?

state,getters,mutations,actions,modules

7.3 Vuex解决了什么问题?

多个组件依赖于同一状态时
来自不同组件的行为需要变更同一状态。

7.4 vuex在什么情况下用?,为什么使用vuex?

单页面应用、组件之间共享同一个的状态,用户信息,购物车信息,登录状态
管理数据方便,响应式的,状态改变,使用状态的地方也发生改变,但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在 vuex 里数据改变的时候把数据拷贝一份保存到 localStorage 里面,刷新之后,如果 localStorage 里有保存的数据,取出来再替换 store 里的 state。或者npm安装vuex-persistedstate:
什么要用modules?
当state数据比较多的时候,不好管理,也不知道那个属性是在那个地方用到的?

7.5 Vuex中状态储存在哪里,怎么改变它?

存储在state中,改变Vuex中的状态的唯一途径就是显式>地提交 (commit) mutation.

7.6 Vuex中状态是对象时,使用时要注意什么?

对象是引用类型,复制后改变属性还是会影响原始数据,这样会改变state里面的状态,是不允许,所以先用深度克隆复制对象,再修改。

7.7 怎么在组件中批量使用Vuex的state状态?

使用mapState辅助函数, 利用对象展开运算符将state混入computed对象中

7.8 Vue.js中ajax请求代码应该写在组件的methods中还是vuex的actions中?

如果请求来的数据是不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入vuex 的state里。
如果被其他地方复用,这个很大几率上是需要的,如果需要,请将请求放入action里,方便复用。

7.9 mutations、actions区别

使用的不同:mutations是可以直接修改state的数据的,Action 提交的是 mutation,而不是直接改变state状态。
mutations必须是同步的,actions可以包含任何异步操作。
上诉是为什么?
在mutations中使用异步操作,是不可能完成的,因为当 mutation 触发的时候,回调函数还没有被调用,devtools 是不清楚回调函数什么时候被调用。而在actions中则不会出现
acitons比mutations调试更加方便

八、computed和mothods和watch区别

8.1 computed

有缓存,进入组件会执行一次,当值发生变化再执行,依赖它的值改变,computed也随着改变

8.2 methods

进入组件会执行一次,调用多次就执行多次,值没有改变也会执行多次

8.3 watch

监听data已定义的数据,第一次进入组件不会执行,当值发生变化才会执行,但是有时候初始化的时候也执行函数,那就要设置immediate为true,

总结:如果说computed、methods、watch都能实现效果,尽量使用computed

九 、slot是什么?有什么作用?原理是什么?应用场景?

slot又名插槽,是Vue的内容分发机制,组件内部的模板引擎使用slot元素作为承载分发内容的出口。插槽slot是子组件的一个模板标签元素,而这一个标签元素是否显示,以及怎么显示是由父组件决定的。
slot又分三类,默认插槽,具名插槽和作用域插槽。
默认插槽:又名匿名查抄,当slot没有指定name属性值的时候一个默认显示插槽,一个组件内只有有一个匿名插槽。
具名插槽:带有具体名字的插槽,也就是带有name属性的slot,一个组件可以出现多个具名插槽。
作用域插槽:默认插槽、具名插槽的一个变体,可以是匿名插槽,也可以是具名插槽,该插槽的不同点是在子组件渲染作用域插槽时,可以将子组件内部的数据传递给父组件,让父组件根据子组件的传递过来的数据决定如何渲染该插槽。
实现原理:当子组件vm实例化时,获取到父组件传入的slot标签的内容,存放在 vm. s l o t 中,默认插槽为 v m . slot 中,默认插槽为 vm. slot中,默认插槽为vm.slot.default ,具名插槽为 vm.$slot.xxx ,xxx 为插槽名,当组件执行渲染函数时候,遇到slot标签,使用 $slot 中的内容进行替换,此时可以为插槽传递数据,若存在数据,则可称该插槽为作用域插槽。
最常用用于组件的封装

9.1 slot怎么传值

父传子:父组件通过在标签上定义属性,子组件通过props接受这个数据
子传父:子组件在slot中定义属性,父组件通过slote-scope接收参数。
在这里插入图片描述

十、组件传值

  • 父传子:父组件通过在标签上定义属性,子组件通过props接受这个数据

  • 子传父 : 子组件通过this.$emit 发送一个事件,父组件通过一个回调函数来接收

  • 同级页面传参:通过事件总线,this. b u s . bus. bus.emit,this. b u s . bus. bus.on
    A页面通过this. e m i t 发送事件, t h i s . emit发送事件, this. emit发送事件,this.bus. e m i t ( " s e n d " , t h i s . u s e r N a m e ) B 组件通过 t h i s . emit("send",this.userName) B组件通过this. emit("send",this.userName)B组件通过this.on 来接受这个事件 this. b u s . bus. bus.on(“send”,(val)=>{console.log(val)})

  • p a r e n t / parent / parent/children 与ref
    ref: 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
    $parent / $children:访问父 / 子实例

  • 通过Vuex状态管理传值

十一、v-model双向绑定原理,v-model是什么?有什么用?

  • 双向数据绑定是什么?

  • 当数据发生变化时,相应的视图会进行更新,当视图更新时,数据也会跟着变化

  • 原理

  • vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的,利用了Object.defineProperty() 这个方法重新定义了对象获取属性值(get)和设置属性值(set)。

  • 在数据变动时,会触发set方法,会对数据进行比较,如果数据发生了变化,调用dep的notify方法发布通知,通知订阅者进行更新,订阅者就会调用update方法,更新视图。

  • 视图变化后,例如:input输入框的改变,给这个节点添加input的事件监听函数,当输入框变化,把最新的值复制给data对象对应的数据,当这个数据变化后,就会触发这个数据set方法,对这个数据重新赋值。数据就更新了。 observe会对data的每个数据通过defineReative变成响应式数据,并为每个数据添加一个dep(发布者),complier将解析指令模板,解析指令,将模板指令替换对应的数据,然后模板中每个非事件指令或表达式都对应一个watcher对象订阅者,在创建订阅者的时候,会把订阅者添加dep中,收集依赖。之后把fragment对象添加到el中,展示到页面中,
    在这里插入图片描述
    双向数据绑定原理

  • v-model是v-bind:value和@input的语法糖,意思是绑定了一个value属性,子组件可以对value属性监听,通过this.$emit(‘input’,xxx)方式给父组件通讯。

十二、v-if和v-show区别

  • 共同点
    • 都是显示和隐藏元素
    • 当表达式为true的时候,都会占据页面的位置
    • 当表达式为false的时候,都不会占据页面的位置
  • 不同点
  • v-show是通过display:none来隐藏元素的,虽然隐藏了,但是它是存在的,一般用于频繁交换(为什么?v-show是通过css的display来实现隐藏和显示元素的,不会触发组件的生命周期,不会引起重绘和回流,性能方面开销不大。)
    v-if是添加、删除页面元素,为false时,它是隐藏的,是不会渲染的,是不存在的,一般不用与频繁交换(为什么?v-if本质是添加/删除元素,添加/删除dom元素会引起重绘和回流,而且触发组件的生命周期,false–true 触发组件的beforeCreate、created、beforeMounte、mounted钩子,true—false,触发组件的beforeDestory、destoryed,性能开销很大)

十三、MVVM

13.1 为什么有MVVM?

mvvm是一种模式,是程序历史演变而来的,
在web1.0的时代,前后端不分离,基本没有前端概念
问题:代码写在一起难以维护
在web2.0年代,出现了ajax,这个时候前端和后端可以分离
前端:调用接口,渲染数据
后端: 写接口,供前端调用
问题:但是一个网页可能特别大,维护起来可能也会出现难以维护的情况,
解决:出现了前端的框架ng,vue,react,整个网页分为不同的组件,这样维护就很方便了

13.2 mvvm是什么?

m (数据层,通过模块来定义内容,让view层展示出来)
v  (视图层,就是我们看到的页面,各类ui组件)
vm(业务逻辑层,框架封装的核心,它负责将数据和视图关联起来)
vm的主要职责:
数据变化后更新视图
视图变化后更新数据
他还有两个主要部分组成:
监听器:对所有数据的属性进行监听
解析器: 对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
在这里插入图片描述

十四、为什么vue中的v-if和v-for不建议一起用?

14.1 作用
v-if用于条件渲染,只有当条件为true的时候,才会渲染
v-for用于列表渲染,通常渲染数组的数据,语法是item in 数组,在v-for的时候,建议添加key值并保证每个key的值是唯一的,这便于diff算法优化。
14.2 优先级
v-for的优先级高于v-if,怎么证明?
可以编写一个例子来证明,在tempale中,写出v-for和v-if在用一个标签内,v-for循环data的数组数据,v-if使用computed的属性并返回true/false,之后打印render函数(vm.$options.render),在控制台输出render函数的代码,在render函数中可以看到_l,_l是列表循环的函数,在循环里可以看到条件判断,所以是先循环再判断,v-for的优先级高于v-if,也可以在compiler\codegen\index.js中可以看到v-for判断先于v-if的判断。
14.3 优化措施:

  1. v-for和v-if放到同一个标签上,会造成性能浪费,如果避免出现这种情况,则在外层嵌套template,然后在template中进行v-if判断,最后再进行内部循环。
  2. 如果条件出现在循环内部,可以通过计算属性computed过滤掉不需要显示的元素。

十五 为什么组件的data必须是一个函数?

vue实例的data可以是一个对象,也可以是一个函数。为什么?因为vue实例只有一个(根实例只能有一个,不会造成数据污染的情况)
组件的data如果是一个对象,多个实例共用同一个内存地址,(为什么共用同一个内存地址? 在initData中,对data进行了判断,如果data不是一个函数,则返回data或者一个空对象,创建多个实例,返回同一个data,所以共用同一个内存内存地址)修改一个实例的数据会影响其他实例的数据,造成数据污染,
如果data是一个函数,则不会出现这种情况,initData会将函数作为工厂函数都会返回全新的data对象。每个实例的数据互不影响,不会造成数据的污染。所以data必须是一个函数。

十六、你了解哪些vue性能优化方法

  • 路由懒加载
  • keep-alive缓存页面
  • 使用v-show复用dom
  • v-for遍历避免同时使用v-if (v-for和v-if尽量不写在用一个标签上)
  • object.freeze() 冻结 ,对于某些数据只是展示,不会发生任何变化,就不需要做响应变化,就可以使用object.freeze()
  • 如果有大数据长列表,可采用虚拟滚动,只渲染少部分区域的内容,例如:第三方 vue-virtual-scroller
  • 图片懒加载 vue-lazyload <img v-lazy="/ss/ss/x.img" />
  • 第三方插件按需引入 例如: 像element-ui这样的第三方组件库
  • 无状态的组件标记为函数式组件
    • 没有vue实例,没有数据,只是展示
      在这里插入图片描述
  • 子组件分割

十七、了解vue3.0的特性?

vue3.0特性

Vue 通过Object.defineProperty来将对象的key转换成getter/setter的形式来追踪变化,但getter/setter只能追踪一个数据是否被修改,无法追踪新增属性和删除属性。如果是删除属性,我们可以用vm.$delete实现,那如果是新增属性,该怎么办呢?
1)可以使用 Vue.set(location, a, 1) 方法向嵌套对象添加响应式属性;
2)也可以给这个对象重新赋值,object.assign()可以对对象添加属性,但它不会去更新视图,需要原对象,新对象,新增的属性合在一起重新赋值给原对象或者使用解构赋值,解构原来的对象再添加新的属性再赋值给原来的对象,比如data.location = {…data.location,a:1}
3)使用$forceRefresh强制刷新,重新渲染页面。

十八、vue响应式数据原理是什么?

在 new Vue() 后, Vue 会调用_init 函数进行初始化,也就是init 过程,在 这个过程Data通过Observer转换成了getter/setter的形式,来对数据追踪变化,当被设置的对象被读取的时候会执行getter 函数,而在当被赋值的时候会执行 setter函数。
当外界通过Watcher读取数据时,会触发getter从而将Watcher添加到依赖中。
在修改对象的值的时候,会触发对应的setter, setter通知之前依赖收集得到的 Dep 中的每一个 Watcher,告诉它们自己的值改变了,需要重新渲染视图。这时候这些 Watcher就会开始调用 update 来更新视图。
在这里插入图片描述
对data对象通过obsever设置成getter和setter形式,当watcher获取数据的时候,会触发getter函数,将watcher添加到dep中,收集依赖,当数据发生变化后,会触发setter函数,通知dep,dep会通知watch,watch接收到通知,就会向外界发送通知,通知外界进行更新。

追问:
vue如何监测数组中的变化
vue如何监测数组中的变化
Object.defineProperty 没有监听数组的变化,而是进行数组方法的重写,具体代码如下:`

function render() {
  console.log('模拟视图渲染')
}
let obj = [1, 2, 3]
let methods = ['pop', 'shift', 'unshift', 'sort', 'reverse', 'splice', 'push']
// 先获取到原来的原型上的方法
let arrayProto = Array.prototype
// 创建一个自己的原型 并且重写methods这些方法
let proto = Object.create(arrayProto)
methods.forEach(method => {
  proto[method] = function() {
    // AOP
    arrayProto[method].call(this, ...arguments)
    render()
  }
})
function observer(obj) {
  // 把所有的属性定义成set/get的方式
  if (Array.isArray(obj)) {
    obj.__proto__ = proto
    return
  }
  if (typeof obj == 'object') {
    for (let key in obj) {
      defineReactive(obj, key, obj[key])
    }
  }
}
function defineReactive(data, key, value) {
  observer(value)
  Object.defineProperty(data, key, {
    get() {
      return value
    },
    set(newValue) {
      observer(newValue)
      if (newValue !== value) {
        render()
        value = newValue
      }
    }
  })
}
observer(obj)
function $set(data, key, value) {
  defineReactive(data, key, value)
}
obj.push(123, 55)
console.log(obj) //[1, 2, 3, 123,  55]

这种方法将数组的常用方法进行重写,进而覆盖掉原生的数组方法,重写之后的数组方法需要能够被拦截。但有些数组操作Vue时拦截不到的,当然也就没办法响应,比如:

obj.length-- // 不支持数组的长度变化
obj[0]=1  // 修改数组中第一个元素,也无法侦测数组的变化

如何答:Object.defineProperty 没有监听数组的变化,而是进行数组方法的重写, 为什么?这是因为非常消耗性能。
原理:
vue在数据初始化时调用initData方法,然后通过new Observer对数据进行监测,然后对数据进行判断,如果是数组并且支持原型链就会执行protoAugment让数组的原型指向重写数组方法的原型上,之后调用arrayMethods方法,arrayMethods用来重写数组的原型方法。内部会采用函数劫持的方式,当用户调用这些方法(push,pop,shift,unshift,sort,splice,reverse)之后,还会调用原数组的方法进行更新数组。用户如何调用这些方法?用户调方法时使用的时mutator函数,这个函数还是会调用数组原有的方法,重写的mutator函数中会调用原生的方法,数组的push,unshift,splice方法会新增数组的数据,对插入的数据使用observeArray再次进行监测,最后通过dep.notify发布通知,更新视图。

在vue中,给对象添加属性的时候,页面不刷新怎么办?

原因:
Vue 通过Object.defineProperty来将对象的key转换成getter/setter的形式来追踪变化,但getter/setter只能追踪一个数据是否被修改,无法追踪新增属性和删除属性。如果是删除属性,我们可以用vm.$delete实现,那如果是新增属性,该怎么办呢?
措施:
1)可以使用 Vue.set(location, a, 1) 方法向嵌套对象添加响应式属性;
2)也可以给这个对象重新赋值,object.assign()可以对对象添加属性,但它不会去更新视图,需要原对象,新对象,新增的属性合在一起重新赋值给原对象或者使用解构赋值,解构原来的对象再添加新的属性再赋值给原来的对象,比如data.location = {…data.location,a:1}
3)使用$forceRefresh强制刷新,重新渲染页面。

十九、为何vue采用异步渲染

vue采用异步渲染

二十 数据请求放到那个生命周期中?

  • 在 created 的时候,有数据没有节点,视图中的dom并没有渲染出来,所以此时如果直接去操作dom节点,无法找到相关的元素

  • 在 mounted 中,有数据,有节点,由于此时dom 已经渲染出来了,所以可以直接操作dom节点。

  • 把请求放到mounted中,可能会出现页面闪动的情况(此时页面dom结构已经渲染完成,之后获取到数据,又得挂载数据),放到created中可以避免这种情况发生,因此建议在created发送请求。

二十一、何时需要使用beforeDestroy

  • 可能在当前页面中使用了 $on 方法,需要在组件销毁前解绑。
  • 清除自己定义的定时器
  • 解除事件的绑定 scoll mousemove …

二十二、vue父子组件生命周期调用顺序

vue父子组件生命周期调用顺序

二十三 computed的实现原理或特点

computed的实现原理或特点

二十四 Watch中的deep:true 是如何实现的

Watch中的deep:true 是如何实现的

二十五 vue中的事件绑定原理

vue中的事件绑定原理

二十六、vue中v-html会导致哪些问题?

vue中v-html会导致哪些问题?

二十七、为什么v-for和v-if不能连用

v-for的优先级是高于v-if,v-for和v-if在用一个标签内,在render函数中,先有循环,在每一个循环里都有判断,如果把v-if放到循环外面,在render函数中,先有判断,判断正确才有循环,v-for和v-if不要放到同一个元素上,会带来性能方面的浪费,一般情况下,把v-if放到v-for循环的外层,如果v-if和v-for必须出现在用一个标签,可以通过computed提前过滤掉不符合条件元素。

二十八、Vue组件如何通信?

Vue组件如何通信?

二十九、什么是作用域插槽?插槽与作用域插槽的区别

什么是作用域插槽?插槽与作用域插槽的区别

三十、用vnode来描述一个dom结构

用vnode来描述一个dom结构

三十一、diff算法

diff算法
取下图1
取下图2
3. 当数据发生变化之后,回触发set方法,在set方法中调用dep(发布者)的notify()方法,发送通知,所有的watcher(订阅者)接收到通知,就会调用patch(打补丁)给真实的dom打补丁。
4. 进入patch函数的时候 ,

  • 如果没有新节点,通过destory钩子函数删除旧节点,
  • 如果没有旧节点,说明页面刚开始初始化的时候,就不需要进行比较了,直接调用createElm方法创建新节点,
  • 往下就通过showVnode判断两个节点是否相同,
  • 如果不相同就删除旧节点,创建新节点,
  • 如果相同就调用patchVnode方法,
    • 进入到该方法后,会有以下几种情况,先判断两个节点是否是文本节点
    • 如果是文本节点,将新的节点的文本内容替换掉el的文本内容
    • 如果不是文本节点,判断两个节点是否存在子节点
    • 只有新节点存在子节点时,则将新节点的子节点真实化后添加到el中
    • 只有旧节点存在子节点,删除掉旧节点的子节点
    • 如果都有子节点,并且两个子节点不相同,调用updateChildren方法
      • 该方法主要是
      • 设置新旧Vnode的头尾指针
      • 新旧指针进行比较,循环向中间靠拢,根据情况去调用patchVnode方法进行patch重复流程、调用createElem创建一个新的节点、从哈希表中根据key找到一致的Vnode节点应用到真实dom上。
        在这里插入图片描述
        diff算法中的sameVnode方法比较当前标签上的key还有它当前的标签名,如果key和标签名都一样的话,就代表着这两个节点是相同的,就会调用patchVnode方法去处理这两个节点。

三十二、v-for中为什么要用key

v-for中为什么要用key

三十三、描述组件渲染和更新过程

描述组件渲染和更新过程

三十四、vue中模板编译原理

vue中模板编译原理

三十五、vue中常见的性能优化

vue中常见的性能优化

三十六、Vue中相同逻辑如何抽离?

Vue中相同逻辑如何抽离?

三十七、为什么要使用异步组件?

为什么要使用异步组件?

三十八、vue-router有哪几种导航钩子( 导航守卫 )?

vue-router有哪几种导航钩子( 导航守卫 )?

三十九、actions和mutations的区别

actions和mutations的区别

四十、简述vuex的工作原理

简述vuex的工作原理

四十一 、vue3.0有哪些改进

vue3.0有哪些改进
vue3.0面试总结

四十二 指令

什么是指令?

四十三 说说vue中,key的作用和原理

  1. key是什么?作用?
    可以唯一的确定一个dom元素,,也是diff算法的一种优化策略,从而使diff算法更加高效,可以根据key,更准确、更快的找到对应的vnode节点。key的主要作用是为了高效的更新虚拟DOM

  2. 原理:在vue进行patch过程当中,会执行patchVnode,在执行patchVnode的过程中,会调用updateChildren,updateChildren方法中会对新旧vnode进行diff,然后将对比的结果用来更新真实的DOM,如果不加key的情况下,永远把两个节点当做是同一个节点,就无法避免频繁更新过程,就会额外的多做dom更新,不加key的话性能就很差,如果加上key的话,就会使用一系列的内部优化算法,比如:首尾结构的相似性。这样的话,大部分元素都不会发生位置的变化,减少大量的dom更新操作,提高了性能,另外,若不设置key还可以在列表更新时引发一些隐藏的bug,比如没有更新的发生了更新,应该变化的没有发生变化。

在这里插入图片描述

四十四 说一下vue的组件化的理解

在这里插入图片描述
组件的定义:
在程序中,有独立单独的模块可以抽离出来,把模块切分成更小的块,这些块有独立的逻辑,有更好的复用性。
优点:
组件化可以增加代码的复用性 可维护性和可预测性,高内聚 低耦合
使用场景:什么时候使用组件?以下分类可作为参考:

  • 通用组件:实现最基本的功能,具有通用性、复用性,例如按钮组件、输入框组件、布局组件等。
    • 业务组件:它们完成具体业务,具有一定的复用性,例如登录组件、轮播图组件。
    • 页面组件:组织应用各部分独立内容,需要时在不同页面组件间切换,例如列表页、详情页组件

组件有全局注册Vue.component和局部注册components
在这里插入图片描述

34道面试题

在这里插入图片描述
34

兼容

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

性能优化

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

x

前端面试题总结

2020前端面试宝典

更多面试题

vue核心面试题(零碎题)

移动端面试题

移动端面试题

小程序面试题

小程序面试题
前端面试手册

.h5底部输入框被键盘遮挡问题,或者底部导航栏被弹起问题的解决办法

解决

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 根据引用,这份面试题涵盖了Vue.js的基础知识和高级概念,包括组件、生命周期、异步操作和跨域请求,以及Vue框架的优势、vue-router和服务端渲染(SSR)等。对于准备Vue.js面试的人来说,这份问题列表应该是非常有用的资源。 在回答你提到的其他问题之前,让我先回答第32和第33个问题。根据引用,GET请求和POST请求在底层都是基于TCP/IP协议实现的,都可以实现客户端和服务器端的双向交互。最本质的区别在于约定和规范。GET请求用于获取资源,进行查询操作,而POST请求用于传输实体对象,用于增删改操作。约定上,GET请求将参数拼接到URL上进行参数传递,而POST请求将参数写入请求正文中传递。这样的约定和规范有助于保持请求的语义和一致性。 关于第33个问题,数组去重,可以使用不同的方法来实现。一种常见的方法是使用Set数据结构,因为Set只会存储不重复的值。可以将数组转换为Set,然后再将Set转换回数组即可。另一种方法是使用循环和条件判断,遍历数组,将不重复的元素添加到一个新数组中。这样可以去除数组中的重复元素。 希望这些回答对你有帮助。如果你还有其他问题,请随时提问。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [2023前端vue高频面试题](https://blog.csdn.net/qq_27244301/article/details/129619172)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [2023最新高频前端面试题总结(附答案)](https://blog.csdn.net/jewels_w/article/details/125899379)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值