web前端 Vue 框架面试120题(四)
面试题 61 . 简述Vue computed 和 watch 的区别和运用的场景 ?
参考回答:
一、Computed
在Vue.js,computed 是一个非常有用的属性,它允许声明计算属性,这些属性会根据其依赖的数据进行自动更新,而无需手动触发。computed 属性常用于根据现有的响应式数据进行计算,以生成派生的数据,而这些数据的值会随着依赖数据的变化而自动更新。
computed 的使用场景和方法如下:
计算属性的缓存: computed 属性会在其依赖的数据发生变化时进行重新计算,但是它会缓存计算结果。这意味着,只有在依赖数据变化时,才会触发计算函数重新执行,如果依赖数据没有变化,会直接返回之前缓存的计算结果,这有助于性能优化。
依赖关系管理: 当有一些数据需要根据其他数据进行计算,而这些数据之间存在依赖关系时,使用 computed 可以更清晰地管理这些依赖关系,而不需要手动跟踪它们的变化。
模板中的使用: 在模板中,可以像使用普通属性一样使用计算属性。这使得能够将复杂的计算逻辑从模板中分离出来,让模板更加简洁和易读。
<br />
<template><br />
<div><br />
<p>原始价格: { {<br />
originalPrice<br />
}<br />
}<br />
</p><br />
<p>折扣后价格: { {<br />
discountedPrice<br />
}<br />
}<br />
</p><br />
</div><br />
</template><br />
<script><br />
export default {<br />
data() {<br />
return {<br />
price: 100,<br />
discount: 0.2,<br />
}<br />
;<br />
}<br />
,<br />
computed: {<br />
originalPrice() {<br />
return this.price;<br />
}<br />
,<br />
discountedPrice() {<br />
return this.price * (1 - this.discount);<br />
}<br />
,<br />
}<br />
,<br />
}<br />
;<br />
</script><br />
在这个例子中,discountedPrice 计算属性依赖于 price 和 discount 数据,当它们发生变化时,discountedPrice 会自动更新。
总之,computed 在Vue中用于将响应式数据的计算逻辑从模板中分离出来,提高代码可读性和维护性,并自动处理依赖关系和缓存计算结果,从而帮助优化性能。
二、与watch的区别
虽然 computed 和 watch 在某些情况下可以实现相似的功能,但它们的用途和工作方式有一些重要区别。以下是它们之间的主要区别:
1. 计算属性 (computed):
用途: 用于派生出一些新的响应式属性,这些属性的值基于其他已有的响应式属性计算得出。
自动缓存: computed 属性会自动缓存计算结果,只有在依赖属性变化时才会重新计算。这对于频繁访问和计算的情况非常有用,以避免不必要的重复计算。
同步: 计算属性是同步的,它们会立即返回计算结果。
2. 观察属性 (watch):
用途: 用于监听一个特定的数据变化,并在变化发生时执行自定义的操作,如异步操作、API 请求等。
无缓存: watch 不会缓存旧值或计算结果,每当被监听的属性发生变化时,都会触发对应的回调函数。
异步支持: watch 回调函数可以执行异步操作,例如发送网络请求或执行定时器等。
适用于复杂逻辑: 当需要在属性变化时执行一些复杂逻辑、副作用或异步操作时,watch 更合适。
要根据你的具体需求来选择使用 computed 还是 watch:
如果你需要根据已有的响应式属性进行计算,而且这个计算是同步的,那么使用 computed 更合适。
如果你需要监听特定属性的变化,并在变化时执行一些异步操作或复杂逻辑,那么使用 watch 更合适。
绝大多数情况下,computed 能够满足计算和派生数据的需求,而 watch 则更适用于监听属性的变化并执行副作用操作。
三、频繁访问和计算的情况,有哪些
当涉及到频繁访问和计算的情况时,使用计算属性可以避免不必要的重复计算,提高性能。以下是一些具体的例子:
价格计算: 在电子商务应用中,商品价格可能会受到多个因素的影响,如折扣、运费等。如果你需要在页面中显示折扣后的价格、总价等,这些计算可以使用计算属性来处理。
过滤和搜索: 在一个列表中过滤和搜索数据时,你可能需要根据搜索关键字或选定的过滤条件来计算显示在页面上的数据集。这些过滤和搜索逻辑可以放在计算属性中,以保持页面的简洁性和性能。
图表数据生成: 当你在应用中显示图表或图形时,图表的数据通常是从原始数据集中进行聚合、统计等计算得出的。使用计算属性可以确保图表数据在依赖数据变化时自动更新,而无需手动处理。
动态样式: 如果你需要根据一些条件来设置元素的样式,例如根据用户的输入改变按钮的颜色,可以使用计算属性来根据条件动态计算样式。
排序和排名: 在显示排行榜、评分列表或任何需要排序的列表时,你可能需要根据某些规则对数据进行排序和排名。使用计算属性可以将排序逻辑与模板分离,并确保排序是响应式的。
数据格式化: 当你需要在页面上显示格式化的数据,比如日期、货币、百分比等,你可以使用计算属性来处理数据格式化逻辑。
四、关于同步和异步
computed 和 watch 的最大区别在于缓存和同步/异步的处理。下面进一步解释一下关于同步和异步方面的内容,特别是在 watch 中。
同步和异步:
同步(Sync): 同步操作意味着操作会立即执行,直到操作完成后才继续执行下一个操作。在 Vue 的上下文中,这表示计算会立即发生,没有阻塞,没有延迟。
异步(Async): 异步操作允许在操作开始后,不必等待其完成,而是可以继续执行其他操作。在 Vue 中,异步操作通常涉及到网络请求、定时器等,这些操作不会阻塞 JavaScript 的主线程,而是在后台进行。
在 watch 中的异步行为:
watch 提供了一种监视数据变化并采取响应行动的机制。当被监视的数据发生变化时,watch 的回调函数将被调用。这个回调函数可以包含异步操作,比如发送网络请求、执行定时器等。这些异步操作不会阻塞主线程,而是在后台执行,这样页面仍然可以继续响应用户操作。
为什么说发送网络请求或执行定时器是异步请求?
异步操作的概念:
异步操作是指在代码执行过程中,不需要等待某个操作完成就可以继续执行其他操作的方式。在异步操作中,你通常会指定一个回调函数,以便在操作完成时得到通知。这允许你同时执行多个操作而不会阻塞整个程序或页面。
发送网络请求的异步性质:
当你发送一个网络请求时(例如使用 XMLHttpRequest、Fetch API 或 Axios),你的程序并不会等待服务器响应返回。相反,它会继续执行后续代码。当服务器响应到达时,会触发你提供的回调函数。这样,你可以在等待响应的同时继续执行其他操作,从而实现并发性。
执行定时器的异步性质:
当你设置一个定时器(例如使用 setTimeout 或 setInterval)时,你指定了一个时间,经过该时间后,指定的回调函数会被触发。在等待这段时间内,JavaScript 不会阻塞程序的其他部分。这使得你可以同时处理其他任务,而不必等待定时器时间结束。
总之,异步操作允许你在某些操作等待完成时继续执行其他操作,而不会阻塞程序的执行。这与同步操作不同,后者会在操作完成之前阻塞代码的执行。在 Vue 的 watch 中,你可以使用异步操作,如发送网络请求或执行定时器,而不会影响整个应用的响应性。
五、回调函数
回调函数是一种在某个事件发生或者某个条件满足时被调用的函数。它是一种常见的编程概念,用于实现异步操作、事件处理、以及在特定情况下执行代码等。
在 JavaScript 中,函数可以作为值传递,因此你可以将一个函数传递给另一个函数,以便在适当的时机调用它。这就是回调函数的基本概念。
回调函数的特点和用途:
异步操作: 回调函数常用于处理异步操作,如网络请求、文件读取等。你可以在异步操作完成后执行回调函数,以响应操作的结果。
事件处理: 在事件驱动的编程中,你可以指定某个事件发生时应该执行的回调函数。比如,在点击按钮时触发的点击事件,就可以关联一个回调函数来响应按钮点击。
参数传递: 回调函数可以接受参数,这使得你可以将数据传递给回调函数,让它在执行时使用这些数据。
动态逻辑: 通过回调函数,你可以在不同的情况下执行不同的逻辑。根据不同的参数或条件,可以传递不同的回调函数来实现动态逻辑。
示例:
function doSomething(callback) {
console.log("Doing something...");
// 模拟操作的延迟
setTimeout(function() {
console.log("Operation completed!");
// 执行回调函数
callback();
}
, 1000);
}
function onOperationComplete() {
console.log("Callback executed: Operation complete!");
}
// 调用 doSomething 并传递回调函数
doSomething(onOperationComplete);
在这个例子中,doSomething 函数模拟了一个异步操作,然后在操作完成后调用传递的回调函数 onOperationComplete。这种方式可以在异步操作完成后执行额外的逻辑。
总之,回调函数是一种灵活的方式,允许你在特定事件发生或条件满足时执行一些代码,从而实现异步操作和动态逻辑。
对于上面的例子,doSomething(onOperationComplete);
首先打印了console.log("Doing something...");
然后打印了 console.log("Operation completed!");
最后打印了 console.log("Callback executed: Operation complete!");
doSomething(onOperationComplete) 的执行顺序如下:
首先,doSomething 函数被调用,并在控制台打印出 "Doing something..."。
然后,通过 setTimeout 模拟了一个异步操作,等待了1秒(1000毫秒)。
在异步操作完成后,"Operation completed!" 被打印到控制台。
接着,传递的回调函数 onOperationComplete 被执行,控制台打印 "Callback executed: Operation complete!"。
也就是说,对于doSomething,需要用到onOperationComplete的结果,但是doSomething本身也有需要执行的逻辑,使用回调函数可以在不影响doSomething的情况下异步拿到onOperationComplete
回调函数的一个重要用途是在处理异步操作时,可以在操作完成后执行额外的逻辑,同时不影响原始操作本身的流程。在上面提到的例子中,doSomething 函数本身可能有一些需要执行的逻辑,但也需要在操作完成后得到 onOperationComplete 函数的结果或执行一些额外操作。
使用回调函数,可以将 onOperationComplete 作为参数传递给 doSomething,然后 doSomething 在异步操作完成后调用这个回调函数,以确保操作完成后执行相关的逻辑。这种方式使得代码结构更清晰,并且能够处理异步操作的结果。
这种模式在处理异步操作、事件处理以及动态逻辑等方面非常有用。
vue中我们通常在哪里使用回调函数?
在Vue中,回调函数通常在以下几个场景中使用:
生命周期钩子函数: Vue组件有一系列的生命周期钩子函数,它们会在组件生命周期中的不同阶段被自动调用。你可以在这些钩子函数中传递回调函数来执行在特定生命周期阶段执行的逻辑。例如,在 created、mounted 或 updated 钩子函数中。
异步操作: 当涉及到异步操作,例如网络请求、定时器等,你可以将回调函数作为异步操作完成后的处理逻辑。这样可以确保在操作完成后执行额外的操作,比如更新组件的状态。
事件处理: Vue中的事件处理也经常使用回调函数。你可以在模板中绑定事件,并在事件触发时执行指定的回调函数。例如,监听按钮的点击事件。
Watch 监听器: 在Vue的组件中,你可以使用 watch 监听器来监听数据的变化。当被监听的数据发生变化时,你可以指定一个回调函数来执行相应的操作,如执行异步请求、更新视图等。
父子组件通信: 当你在父子组件之间进行通信时,可以通过 props 和自定义事件($emit)来传递回调函数。子组件可以通过调用传递的回调函数来将数据传递回父组件。
路由导航守卫: 在Vue的路由中,你可以使用导航守卫来在路由导航发生时执行特定的逻辑。这些守卫可以接受回调函数,用于在不同的导航阶段执行代码。
总之,回调函数在Vue中用于处理生命周期事件、异步操作、事件处理、数据变化监听等多个场景。它们是一种实现灵活、动态逻辑的重要方式,帮助你编写更加响应式和交互性的应用程序。
生命周期使用回调函数:
几乎所有生命周期都可以使用回调函数,但并不是所有生命周期都适合使用回调函数。每个生命周期阶段都有其特定的用途和适用情况。以下是一些常见的生命周期阶段以及是否适合使用回调函数的简要说明:
beforeCreate: 在实例初始化之后,数据观测 (data observation) 和事件配置之前被调用。这个阶段适合执行一些初始化设置,但不太适合使用回调函数,因为此时组件还没有被完全创建。
created: 在实例已经创建完成之后被调用。在这个阶段,组件的数据和方法已经初始化,你可以在这里执行一些异步操作,或者执行一些需要在组件实例创建后才能执行的逻辑。回调函数可以用于执行初始化数据加载等操作。
beforeMount: 在挂载开始之前被调用。这个阶段是在模板编译成渲染函数之后,但在渲染函数首次调用之前。在这个阶段使用回调函数的情况较少,通常用于一些底层操作。
mounted: 在挂载结束后被调用,即 DOM 元素已经被插入页面中。在这个阶段,你可以执行与 DOM 相关的操作,如初始化第三方库、操作 DOM 元素等。这个阶段较为适合使用回调函数。
beforeUpdate: 在数据更新之前被调用,发生在虚拟 DOM 重新渲染和打补丁之前。这个阶段通常不适合使用回调函数,而是用于一些底层操作。
updated: 在数据更改导致虚拟 DOM 重新渲染和打补丁后被调用。这个阶段适合执行与数据更改有关的操作,但通常情况下不是最适合使用回调函数的地方。
beforeDestroy: 在实例销毁之前调用。在这个阶段,实例还完全可用,你可以执行一些清理工作。回调函数可以用于执行销毁前的一些操作。
destroyed: 在实例销毁之后被调用。这个阶段适合执行一些最终清理工作,如解绑事件监听器、清理定时器等。回调函数可以用于执行销毁后的操作。
综上所述,虽然几乎所有生命周期都可以使用回调函数,但回调函数的使用会根据生命周期的特性和目的而有所不同。要根据具体的需求来决定是否在特定的生命周期中使用回调函数。
六、再讲一下this.$nextTick
当在 Vue 中更新数据时,Vue 实际上并不会立即更新 DOM。相反,它会将更新放入一个队列中,然后在适当的时机进行更新。这种方式可以优化性能,避免不必要的 DOM 操作。但有时候你可能需要在 DOM 更新后执行一些操作,这时就可以使用 this.$nextTick。
this.$nextTick 是 Vue 提供的一个实例方法,它接受一个回调函数作为参数,并会在下次 DOM 更新循环结束之后调用这个回调函数。这意味着你可以确保在 DOM 更新完成后执行一些操作,例如读取更新后的 DOM 元素属性、执行某些操作等。
使用场景:
DOM 更新后的操作: 如果你想在更新后访问更新后的 DOM 元素,比如获取元素的高度或宽度,可以将这些操作放在 this.$nextTick 的回调函数中。
保证更新完成后执行: 有时候你可能需要在数据更新后执行一些操作,但又不想在每次数据更新时都执行,而是确保在整个更新周期完成后执行。
面试题 62 . 简述 v-if 和 v-for 为什么不建议一起使用 ?
参考回答:
v-for和v-if不要在同一标签中使用,因为解析时先解析v-for在解析v-if。如果遇到需要同时使用时可以考虑写成计算属性的方式。
永远不要把 v-if 和 v-for 同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)
如果避免出现这种情况,则在外层嵌套template(页面渲染不生成dom节点),在这一层进行v-if判断,然后在内部进行v-for循环
如果条件出现在循环内部,可通过计算属性computed提前过滤掉那些不需要显示的项
computed: {
items: function() {
return this.list.filter(function (item) {
return item.isShow
})
}
}
面试题 63 . 请解释Vue的父子组件生命周期钩子函数执行顺序 ?
参考回答:
加载渲染过程
父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created
-> 子 beforeMount -> 子 mounted -> 父 mounted
子组件更新过程
父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
父组件更新过程
父 beforeUpdate -> 父 updated
销毁过程 父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
总结: 父组件先开始 子组件先结束
面试题 64 . 简述vue-router 路由钩子函数是什么?执行顺序是什么?
参考回答:
路由钩子的执行流程,钩子函数种类有:全局守卫、路由守卫、组件守卫。
完整的导航解析流程:
1 、导航被触发。
2 、在失活的组件里调用 beforeRouterLeave 守卫。
3 、调用全局的 beforeEach 守卫。
4 、在重用的组件调用 beforeRouterUpdate 守卫( 2.2+ )。
5 、在路由配置里面 beforeEnter 。
6 、解析异步路由组件。
7 、在被激活的组件里调用 beforeRouterEnter 。
8 、调用全局的 beforeResolve 守卫( 2.5+ )。
9 、导航被确认。
10 、调用全局的 afterEach 钩子。
11 、触发 DOM 更新。
12 、调用 beforeRouterEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回
调函数的参数传入。
面试题 65 . 简述vue-router 组件复用导致路由参数失效怎么办?
参考回答:
解决方案 :
通 过 watch 监听 路由参数再发请求
watch:{
"router":function(){
this.getData(this.$router.params.xxx)
}
}
面试题 66 . 简述vue.mixin的使用场景和原理?
参考回答:
Mixin是面向对象程序设计语言中的类,提供了方法的实现。
其他类可以访问mixin类的方法而不必成为其子类
当一段代码非常相似的时候就可以抽离成一个mixin
mixins是一个js对象,它可以包含我们组件中script项中的任意功能选项,如data、components、methods 、created、computed等等。只要将共用的功能以对象的方式传入 mixins选项中,当组件使用 mixins对象时所有mixins对象的选项都将被混入该组件本身的选项中来,这样就可以提高代码的重用性,使你的代码保持干净和易于维护。
使用场景
当存在多个组件中的数据或者功能很相近时,就可以利用mixins将公共部分提取出来,通过 mixins封装的函数,组件调用他们是不会改变函数作用域外部的。
mixins和vuex的区别
vuex公共状态管理,在一个组件被引入后,如果该组件改变了vuex里面的数据状态,其他引入vuex数据的组件也会对应修改,所有的vue组件应用的都z是同一份vuex数据。(在js中,有点类似于浅拷贝)
vue引入mixins数据,mixins数据或方法,在每一个组件中都是独立的,互不干扰的,都属于vue组件自身。(在js中,有点类似于深度拷贝)
mixins和组件的区别
组件:在父组件中引入组件,相当于在父组件中给出一片独立的空间供子组件使用,然后根据props来传值,但本质上两者是相对独立的。
Mixins:则是在引入组件之后与组件中的对象和方法进行合并,相当于扩展了父组件的对象与方法,可以理解为形成了一个新的组件。
(装饰器模式)
mixin的使用
定义一个mixin名字为myMixins
export default {
data () {
return {
num:1
}
}
,
methods: {
mymixin() {
console.log(this.num);
}
,
}
}
在组件中使用
import {
myMixins
}
from './myMixins';
export default {
mixins: [myMixins],
data() {
return {
}
}
,
created() {
//使用mixin可以直接用,但是组件就得传值
this.num++
}
,
}
面试题 67 . 解释Vue中transition的理解?
参考回答:
1)定义transition时需要设置对应的name,具体语法为:需要动画的内容或者组件或者页面
2)过渡动画主要包含6个class,分别为:
v-enter:定义元素进入过渡的初始状态,在元素插入前生效,插入后一帧删除,
v-enter-active:在元素插入前生效,在动画完成后删除,
v-enter-to:在元素插入后一帧生效,在动画完成后删除,
v-leave:离开过渡的初始状态,在元素离开时生效,下一帧删除
v-leave-active:在离开过渡时生效,在动画完成后删除
v-leave-to:离开过渡结束状态,在离开过渡下一帧生效,在动画完成后删除
⚠️:v会转化为对应的transition的name值
3)当然我们也可以自定义这六个class 可以直接在transition中设置对应的属性为对应的class名称,属性有:enter-class,enter-active-class,enter-to-class,leave-class,leave-active-class,leave-to-class
4)在同时使用过渡和css动画的时候 可以设置type属性来制定vue内部机制监听transitioned或者animationed事件来完成过渡还是动画的监听
5)如果需要设置对应的过渡时间,可以直接设置属性duration,可以直接接收一个数字(单位为毫秒),也可以接收一个对象{enter:1000,leave:300}
6)也可以设置过渡的钩子函数,具体有:before-enter,enter,after-enter,enter-cancelled,before-leave,leave,after-leave,leave-cancelled
面试题 68 . 请简述 Vue组件的通信(父子组件和非父子组件)?
参考回答:
父子组件通信
传递参数可以使用props,传递函数可以直接在调用子组件的时候传递自定义事件,并使用$emit来调用,例如:
//父组件
export default {
data(){
return {
usermessage:'我是父亲'
}
},
methods:{
myname(name){
console.log('我的名字叫'+name)
}
}
}
//子组件
来源:{{userinfo}}
export default {
props:['userinfo'],
methods:{
getname(){
this.$emit('getinfo','bilibili')
}
}
}
面试题 69 . 请简述 Vue组件的通信( 兄弟组件通信 )?
参考回答:
首先建立一个vue实例空白页(js文件)
import Vue from 'vue'
export default new Vue()
组件a(数据发送方)通过使用 $emit 自定义事件把数据带过去
组件b(数据接收方)使用而通过 $on监听自定义事件的callback接收数据
面试题 70 . 请简述Vue 的性能优化可以从哪几个方面去思考设计 ?
参考回答:
这里只列举针对 Vue 的性能优化,整个项目的性能优化是一个大工程。
对象层级不要过深,否则性能就会差。
不需要响应式的数据不要放在 data 中(可以使用 Object.freeze() 冻结数据)
v-if 和 v-show 区分使用场景
computed 和 watch 区分场景使用
v-for 遍历必须加 key,key最好是id值,且避免同时使用 v-if
大数据列表和表格性能优化 - 虚拟列表 / 虚拟表格
防止内部泄露,组件销毁后把全局变量和时间销毁
图片懒加载
路由懒加载
异步路由
第三方插件的按需加载
适当采用 keep-alive 缓存组件
防抖、节流的运用
服务端渲染 SSR or 预渲染
面试题 71 . 简述nextTick 的作用是什么?他的实现原理是什么 ?
参考回答:
作用 :vue 更新 DOM 是异步更新的,数据变化,DOM 的更新不会马上完成, nextTick的回调是在下次 DOM 更新循环结束之后执行的延迟回调 。
实现原理 :nextTick 主要使用了 宏任务和微任务 。根据执行环境分别尝试采用
Promise:可以将函数延迟到当前函数调用栈最末端
MutationObserver :是 H5 新加的一个功能,其功能是监听 DOM 节点的变动,在所有 DOM 变动完成后,执行回调函数setImmediate:用于中断长时间运行的操作,并在浏览器完成其他操作(如事件和显 示更新)后立即运行回调函数
如果以上都不行则采用 setTimeout 把函数延迟到 DOM 更新之后再使用,原因是宏任务消耗大于微任务,优先使用微任务,最后使用消耗最大的宏任务。
面试题 72 . keep-alive 使用场景和原理 ?
参考回答:
keep-alive 组件是 vue 的内置组件 ,用于 缓存内部组件 实例。这样做的目的在于,keep
alive 内部的组件切回时, 不用重新创建 组件实例,而直接使用缓存中的实例,一方面能够
避免创建组件带来的开销,另一方面可以保留组件的状态 。
keep-alive 具有 include 和 exclude 属性,通过它们可以控制哪些组件进入缓存。另外它 还提供了 max 属性,通过它可以设置最大缓存数,当缓存的实例超过该数时,vue 会移除最久没有使用的组件缓存。
受keep-alive的影响,其内部所有嵌套的组件都具有两个生命周期钩子函数,分别是activated 和 deactivated,它们分别在组件激活和失活时触发。第一次 activated 触发是在 mounted 之后
在具体的实现上,keep-alive 在内部维护了一个 key 数组和一个缓存对象
// keep-alive 内部的声明周期函数
created () {
this.cache = Object.create(null)
this.keys = []
}
key 数组记录目前缓存的组件 key 值,如果组件没有指定 key 值,则会为其自动生成一个
唯一的 key 值 cache 对象以 key 值为键,vnode 为值,用于缓存组件对应的虚拟 DOM
在 keep-alive 的渲染函数中,其基本逻辑是判断当前渲染的 vnode 是否有对应的缓存,
如果有,从缓存中读取到对应的组件实例;如果没有则将其缓存。、
当缓存数量超过 max 数值时,keep-alive 会移除掉 key 数组的第一个元素
面试题 73 . 简述Vue.set 方法原理 ?
参考回答:
Vue 响应式原理的同学都知道在两种情况下修改 Vue 是不会触发视图更新的。
在实例创建之后添加新的属性到实例上(给响应式对象新增属性)
直接更改数组下标来修改数组的值。
Vue.set 或者说是 $set 原理如下
因为响应式数据 我们给对象和数组本身新增了 __ob__ 属性,代表的是 Observer 实例。
当给对象新增不存在的属性,首先会把新的属性进行响应式跟踪 然后会触发对象 __ob__
的 dep 收集到的 watcher 去更新,当修改数组索引时我们调用数组本身的 splice 方法去
更新数组。
面试题 74 . Vue的组件data为什么必须是一个函数?
参考回答:
new Vue是一个单例模式,不会有任何的合并操作,所以根实例不必校验data一定是一个函数。 组件的data必须是一个函数,是为了防止两个组件的数据产生污染。 如果都是对象的话,会在合并的时候,指向同一个地址。 而如果是函数的时候,合并的时候调用,会产生两个空间。
面试题 75 . 请解释Vue为什么要用虚拟Dom ,详细解释原理 ?
参考回答:
什么是虚拟DOM?
在Vue.js 2.0版本中引入了 Virtual DOM 的概念,Virtual DOM 其实就是一个以JavaScript对象(VNode节点)作为基础来模拟DOM结构的树形结构,这个树形结构包含了整个DOM结构的信息。简单来说,可以把Virtual DOM理解为一个简单的JS对象,并且最少包含标签名(tag)、属性(attrs)和子元素对象(children)三个属性。不同的框架对这三个属性的命名会有所差别。
虚拟DOM的作用?
虚拟DOM的最终目标是将虚拟节点渲染到视图上。但是如果直接使用虚拟节点覆盖旧节点的话,会有很多不必要的DOM操作。例如,一个ul标签下有很多个li标签,其中只有一个li标签有变化,这种情况下如果使用新的ul去替代旧的ul,会因为这些不必要的DOM操作而造成性能上的浪费。
为了避免不必要的DOM操作,虚拟DOM在虚拟节点映射到视图的过程中,将虚拟节点与上一次渲染视图所使用的旧虚拟节点做对比,找出真正需要更新的节点来进行DOM操作,从而避免操作其他不需要改动的DOM元素。
其实,虚拟DOM在Vue.js中主要做了两件事情:
1、提供与真实DOM节点所对应的虚拟节点VNode
2、将虚拟节点VNode和旧虚拟节点oldVNode进行对比,然后更新视图
具备跨平台优势,由于Virtual DOM 是以JavaScript对象为基础而不依赖真实平台环境,所以使它具有了跨平台的能力,比如说浏览器平台、Weex、Node等。
操作DOM慢,JS运行效率高,可以将DOM对比操作放在JS层,提高效率。因为DOM操作的执行速度远不如JavaScript运算速度快,因此,把大量的DOM操作搬运到JavaScript中,运用patching算法来计算出真正需要更新的节点,最大限度地减少DOM操作,从而显著提高性能。Vritual DOM本质上就是在JS和DOM之间做了一个缓存,JS只操作Virtual DOM,最后把变更写入到真实DOM。
提高渲染性能,Virtual DOM的优势不在于单次的操作,而是在大量、频繁的数据更新下,能够对视图进行合理、高效的更新。
面试题 76 . vue通过数据劫持可以精准的探测数据变化,为什么还要进行diff检测差异?
参考回答:
现代前端框架有两种方式侦测变化,一种是pull一种是push
pull: 其代表为React,我们可以回忆一下React是如何侦测到变化的,我们通常会用setStateAPI显式更新,然后React会进行一层层的Virtual Dom Diff操作找出差异,然后Patch到DOM上,React从一开始就不知道到底是哪发生了变化,只是知道「有变化了」,然后再进行比较暴力的Diff操作查找「哪发生变化了」,另外一个代表就是Angular的脏检查操作。
push: Vue的响应式系统则是push的代表,当Vue程序初始化的时候就会对数据data进行依赖的收集,一但数据发生变化,响应式系统就会立刻得知,因此Vue是一开始就知道是「在哪发生变化了」,但是这又会产生一个问题,如果你熟悉Vue的响应式系统就知道,通常一个绑定一个数据就需要一个Watcher,一但我们的绑定细粒度过高就会产生大量的Watcher,这会带来内存以及依赖追踪的开销,而细粒度过低会无法精准侦测变化,因此Vue的设计是选择中等细粒度的方案,在组件级别进行push侦测的方式,也就是那套响应式系统,通常我们会第一时间侦测到发生变化的组件,然后在组件内部进行Virtual Dom Diff获取更加具体的差异,而Virtual Dom Diff则是pull操作,Vue是push+pull结合的方式进行变化侦测的.
面试题 77 . Vie3.0 Proxy 相比 defineProperty 的优势在哪里?
参考回答:
Vue3.x 改用 Proxy 替代 Object.defineProperty
原因在于 Object.defineProperty 本身存在的一 些问题 :
Object.defineProperty 只能劫持对象属性的 getter 和 setter 方法。
Object.definedProperty 不支持数组(可以监听数组,不过数组方法无法监听自己重写),更准确的说是不支持数组的各种 API(所以 Vue 重写了数组方法。
而相比 Object.defineProperty,Proxy 的优点在于:
Proxy 是直接代理劫持整个对象。
Proxy 可以直接监听对象和数组的变化,并且有多达 13 种拦截方法。
目前,Object.definedProperty 唯一比 Proxy 好的一点就是兼容性,不过 Proxy 新标准
也受到浏览器厂商重点持续的性能优化当中
面试题 78 . Vue首屏白屏如何解决?
参考回答:
1)路由懒加载
2)vue-cli开启打包压缩 和后台配合 gzip访问
3)进行cdn加速
4)开启vue服务渲染模式
5)用webpack的externals属性把不需要打包的库文件分离出去,减少打包后文件的大小
6)在生产环境中删除掉不必要的console.log
plugins: [
new webpack.optimize.UglifyJsPlugin({ //添加-删除console.log
compress: {
warnings: false,
drop_debugger: true,
drop_console: true
},
sourceMap: true
})
7)开启nginx的gzip ,在nginx.conf配置文件中配置
http { //在 http中配置如下代码,
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 8; #压缩级别
gzip_buffers 16 8k;
#gzip_http_version 1.1;
gzip_min_length 100; #不压缩临界值
gzip_types text/plain application/javascript application/x-javascript text/css
application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
}
8)添加loading效果,给用户一种进度感受
面试题 79 . 请简述Vue中的v-cloak的理解 ?
参考回答:
使用 v-cloak 指令设置样式,这些样式会在 Vue 实例编译结束时,从绑定的 HTML 元素上被移除。
一般用于解决网页闪屏的问题,在对一个的标签中使用v-cloak,然后在样式中设置[v-cloak]样式,[v-cloak]需写在 link 引入的css中,或者写一个内联css样式,写在import引入的css中不起作用
面试题 80 . 简述Vue单页面和传统的多页面区别?
参考回答:
单页面应用(SPA)
通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。但在写的时候,还是会分开写(页面片段),然后在交互的时候由路由程序动态载入,单页面的页面跳转,仅刷新局部资源。多应用于pc端。
多页面(MPA)
指一个应用中有多个页面,页面跳转时是整页刷新
单页面的优点:
用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小;前后端分离;页面效果会比较炫酷(比如切换页面内容时的专场动画)。
单页面缺点:
不利于seo;导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理);初次加载时耗时多;页面复杂度提高很多
原文地址:https://blog.csdn.net/p445098355/article/details/140524914
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!