VUe面试题[部分一]
1、v-show和v-if的区别?
v-if 会导致组件销毁【也就是删除DOM节点】;
v-show 只会隐藏组件,其中v-show是通过display属性来修改是否显示;
如果是经常显示隐藏可以用v-show
2、为何在v-for中使用key?
(1)必须用key,且不能是index和random
(2)diff算法中通过tag和key来判断,是否是sameName
(3)减少渲染次数,提升渲染性能
vue中列表循环需加:key=“唯一标识” 唯一标识尽量是item里面id等,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM。
key主要用来做dom diff算法用的,diff算法是同级比较,比较当前标签上的key还有它当前的标签名,如果key和标签名都一样时只是做了一个移动的操作,不会重新创建元素和删除元素。
没有key的时候默认使用的是“就地复用”策略。如果数据项的顺序被改变,Vue不是移动Dom元素来匹配数据项的改变,而是简单复用原来位置的每个元素。如果删除第一个元素,在进行比较时发现标签一样值不一样时,就会复用之前的位置,将新值直接放到该位置,以此类推,最后多出一个就会把最后一个删除掉。
尽量不要使用索引值index作key值,一定要用唯一标识的值,如id等。因为若用数组索引index为key,当向数组中指定位置插入一个新元素后,因为这时候会重新更新index索引,对应着后面的虚拟DOM的key值全部更新了,这个时候还是会做不必要的更新,就像没有加key一样,因此index虽然能够解决key不冲突的问题,但是并不能解决复用的情况。如果是静态数据,用索引号index做key值是没有问题的。
标签名一样,key一样这时候就会就地复用,如果标签名不一样,key一样不会复用。
3、描述Vue组件生命周期(父子组件)?
(1)单组件生命周期图
(2)父组件生命周期关系
(1)挂载阶段(初始化相关属性)
【1】beforeCreate:此阶段为实例初始化之后,此时数据观察和事件机制还没有形成,不能获取到dom节点;
【2】created:此阶段的vue实例已经创建,仍不能获取DOM 节点.把vue 的一个实例给初始化了,只是存在于 js 内存的一个变量而已,这个时候并没有开始渲染;
【3】beforeMount:在这一阶段,我们虽然还不能获取到具体 DOM 元素,但 vue 挂载的根节点已经创建,下面 vue 对DOM 的操作将围绕这个根元素继续进行,beforeMount 这个阶段是过渡性的,一般一个项目只能用到一两次;
【3】mounted:组件真正绘制完成了,页面已经渲染完了,数据和DOM 都已被渲染出来,一般我们的异步请求都写在这里)
(2)更新阶段(初始化相关属性)
【1】beforeUpdate: 这一阶段,vue遵循数据驱动DOM 的原则,beforeUpdate函数在数据更新后没有立即更新数据,但是DOM 数据会改变,这是双向数据绑定的作用;
【2】 updated:这一阶段,DOM
会和更改过的内容同步
(3)销毁阶段(销毁相关属性)
【1】beforeDestroy:在上一阶段vue已经成功通过数据驱动DOM 的修改,当我们不再需要 vue 操纵 DOM 时,就要销毁 vue,也就是清除vue 实例与 DOM 的关联,调用destroy方法可以销毁当前组件。在销毁前,会触发beforeDestroy 钩子函数;
【2】destroyed:在销毁后,会触发destroyed 钩子函数
生命周期图
结合父子组件之后
一个完整生命周期:
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted->父beforeUpdate->子beforeUpdate->子updated->父updated->父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
什么是生命周期?
简而言之:从生到死的过程,从Vue实例创建-运行-销毁的过程
Vue实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、销毁等一系列过程
生命周期方法?
Vue从生到死的过程中伴随着各种各样的事件,这些事件会自动触发一些方法.这些方法我们统称为生命周期方法
生命周期钩子 = 生命周期函数 = 生命周期事件
创建期间生命周期方法 beforeCreate: created: beforeMount mounted
运行期间生命周期方法?
beforeUpdate updated
销毁期间的生命周期方法?
beforeDestroy destroyed
注:create页面还没有渲染但vue的实例已经初始化了 mounted页面已经渲染完了
4、Vue组件如何通讯(常见)?
(1)父子组件props和this.$emit
(2)自定义事件event. n o 、 e v e n t . no、event. no、event.off、event.$emit
//event.vue
import Vue from 'vue'
export default new Vue()
//this.$emit 常用于触发父子组件的通讯
//event.$emit 常用语触发跨组件的自定义事件
(3)vuex
代码实例如下:
index.vue 父组件(最外层):
<template>
<div>
<Input @add="addHandler"/>
<List :list="list" @delete="deleteHandler"/>
</div>
</template>
<script>
import Input from './Input'
import List from './List'
export default {
components: {
Input,
List
},
data() {
return {
list: [
{
id: 'id-1',
title: '标题1'
},
{
id: 'id-2',
title: '标题2'
}
]
}
},
methods: {
addHandler(title) {
this.list.push({
id: `id-${Date.now()}`,
title
})
},
deleteHandler(id) {
this.list = this.list.filter(item => item.id !== id)
}
},
created() {
// eslint-disable-next-line
console.log('index created')
},
mounted() {
// eslint-disable-next-line
console.log('index mounted')
},
beforeUpdate() {
// eslint-disable-next-line
console.log('index before update')
},
updated() {
// eslint-disable-next-line
console.log('index updated')
},
}
</script>
input.vue 子组件:
<template>
<div>
<input type="text" v-model="title"/>
<button @click="addTitle">add</button>
</div>
</template>
<script>
import event from './event'
export default {
data() {
return {
title: ''
}
},
methods: {
addTitle() {
// 调用父组件的事件
this.$emit('add', this.title)
// 调用自定义事件
event.$emit('onAddTitle', this.title)
this.title = ''
}
}
}
</script>
list.vue 子组件:
<template>
<div>
<ul>
<li v-for="item in list" :key="item.id">
{{item.title}}
<button @click="deleteItem(item.id)">删除</button>
</li>
</ul>
</div>
</template>
<script>
import event from './event'
export default {
// props: ['list']
props: {
// prop 类型和默认值
list: {
type: Array,
default() {
return []
}
}
},
data() {
return {
}
},
methods: {
deleteItem(id) {
this.$emit('delete', id)
},
addTitleHandler(title) {
// eslint-disable-next-line
console.log('on add title', title)
}
},
created() {
// eslint-disable-next-line
console.log('list created')
},
mounted() {
// eslint-disable-next-line
console.log('list mounted')
// 绑定自定义事件
event.$on('onAddTitle', this.addTitleHandler)
},
beforeUpdate() {
// eslint-disable-next-line
console.log('list before update')
},
updated() {
// eslint-disable-next-line
console.log('list updated')
},
beforeDestroy() {
// 及时销毁,否则可能造成内存泄露
event.$off('onAddTitle', this.addTitleHandler)
}
}
</script>
父组件到子组件传递数据
子组件到父组件触发事件
5、描述组件渲染和更新过程?(和上面的生命周期一样)
6、双向绑定v-model的实现原理?
(1)input元素的value = this.name
(2)绑定input事件this.name = $event.target.value
(3)data更新触发re-render
7、对MVVM的理解?
数据驱动视图(MVVM,setState)-Vue MVVM
M - model 数据 ===》 data(){return{}}
V - view 视图 ===》 template模板
VM - viewModel 视图模型
MVVM 数据驱动视图模型 ===》 视图与数据的连接层;视图的点击事件通过VM来操作数据;反之,VM也可以通过改变数据来修改视图。
View-视图
Model – vue组件中的data
ViewModel - 让数据的更改同步到DOM的渲染,视图上监听事件将变化同步到数据中
View和Model两者之间通过ViewModel进行关联像监听事件、监听指令,总之通过ViewModel这一层,当Model修改时就会立刻执行到View的渲染,view这层中有各种点击事件啊DOM事件等,监听的时候就可以去修改model的数据。
8、computed有何特点,computed和watch的区别?
(1)computed有缓存,data不变则不会重新计算
(2)提高性能
<template>
<div>
<p>num {{num}}</p>
<p>double1 {{double1}}</p>
<input v-model="double2"/>
</div>
</template>
<script>
export default {
data() {
return {
num: 20
}
},
computed: {
double1() {
return this.num * 2
},
double2: {
get() {
return this.num * 2
},
set(val) {
this.num = val/2
}
}
}
}
</script>
这里的v-model是双向数据类型绑定,所有必须要有get和set两个不然会报错
computed 和 watch的区别:computed 默认只要 getter,不过需要时也可以提供 setter;watch 侦听器,当需要在数据变化时执行异步或开销较大的操作时,watch是最有用的,使用 watch选项允许执行异步操作(访问一个API),限制我们执行该操作的频率,并在得到最终结果前,设置中间状态,这些都是计算属性无法做到的
computed 是属性
当需要根据已有数据产生一些派生数据的时候,可使用计算属性
注意:计算属性不支持异步操作,因为计算属性一般要绑定到模板中;
计算属性会缓存调用的结果,提高性能;计算属性必须有返回值,没有返回值就没有意义;
watch 是一个功能:
watch不需要返回值,根据某个数据变化执行某些逻辑
watch可以执行异步操作
9、为何组件data必须是一个函数?
防止组件重用的时候导致数据相互影响。如果data是一个函数的话,这样每复用一次组件,就会返回一份新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。
所以说vue组件的data必须是函数。这都是因为js的特性带来的,跟vue本身设计无关。
js本身的面向对象编程也是基于原型链和构造函数,应该会注意原型链上添加一般都是一个函数方法而不会去添加一个对象了。
10、ajax请求应该放在哪个生命周期?
(1)mounted
(2)JS是单线程的,ajax异步获取数据
(3)放在mounted之前没用,只会让逻辑更混乱
11、如何将组件所有props传给子组件?
如果想将一个对象的所有属性都作为prop传入,可以使用不带参数的v-bind。例如
<blog-post v-bind="post"></blog-post>
等价于
<blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>
(1)$props
(2)user v-bind = “$props”