VUE面试题集锦(修改版)
一、vue核心知识–理论篇
1、对vue是一套渐进式框架的理解
渐进式代表的含义是:主张最少。
Vue可能有些方面是不如React,不如Angular,但它是渐进的,没有强主张,你可以在原有大系统的上面,把一两个组件改用它实现,当jQuery用;
也可以整个用它全家桶开发,当Angular用;
还可以用它的视图,搭配你自己设计的整个下层用。
你可以在底层数据逻辑的地方用OO和设计模式的那套理念,也可以函数式,都可以,
它只是个轻量视图而已,只做了自己该做的事,没有做不该做的事,仅此而已。
渐进式的含义,我的理解是:没有多做职责之外的事。
2、vue.js的两个核心
数据驱动和组件化。
数据驱动:
在vue中,数据的改变能驱动视图的更新。而在传统的做法是手动改变DOM来更新视图,vue只需要改变数据。
组件化:
组件化开发,可以很好的降低数据之间的耦合度。将常用的代码封装成组件之后(vue组件封装方法),就能高度的复用,提高代码的可重用性。一个页面/模块可以由多个组件组成。
3、vue生命周期钩子函数有哪些?
总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。具体执行流程查看下图。
1、beforeCreate(创建前)
此阶段为实例初始化之后,在此阶段data、methods、computed、watch上的数据和方法都不能被访问。不能获得DOM节点。
2、created(创建后)
在实例创建完成后发生,此阶段可以使用数据,更改数据,不会触发updated函数。仍然不能获取DOM元素
3、beforeMount(挂载前)
此阶段依然不能获取DOM元素,当前虚拟DOM已经创建完成,即将开始渲染。
4、mounted(挂载后)
在此阶段真实的Dom挂载完毕,数据完成双向绑定,可以访问到DOM节点,使用$refs对DOM进行操作。一般异步请求写在这里。
5、beforeUpdate(更新前)
此阶段之针对视图层,发生在更新之前,在当前阶段更改数据不会,不会导致重渲染。
6、updated (更新后)
发生在更新完成之后,此阶段组件DOM已经更新完成。要注意在此阶段更改数据,这可能会导致无限循环的更新。
7、beforeDestroy(销毁前)
在vue实例销毁之前调用,此阶段所有实例完全可用。
8、destroy (销毁后)
在vue实例销毁之后调用,此阶段vue实例指示的所有东西都会解绑,所有的事件监听器都会被移除,所有的子实例也会被销毁。
4、vue的双向绑定的原理是什么?
vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。
具体实现过程:
我们已经知道实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。因此接下去我们执行以下3个步骤,实现数据的双向绑定:
1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。
流程图如下:
5、vue中methods、computed、watch的区别?
computed:
是计算属性。会更加依赖的数据的显示结果。计算结果会被缓存,只有在依赖的数据发生改变时才会重新调用getter来计算。
如果一个数据依赖其他数据,那么就把这个数据设计为computed。
watch:
数据的监听回调。当依赖的数据发生变化时,执行回调。
如果需要在某个数据变化时做一些事情,就使用watch来观察这个数据的变化。
methods:
是通过事件驱动来执行函数的,运算是没有缓存的。
6、vue实现流程?
解析模板,生成render函数(渲染函数)
模板中用到的data中的属性,变成JS变量。v-model、v-for、v-on都变成了JS逻辑。render函数返回VNode(虚拟节点)
响应式开始监听
使用object.defineProperty将data属性代理到vm上。
开始渲染,显示页面,绑定依赖
首先,初次渲染模板会访问到data里的数据,触发get方法,创建订阅者加入订阅者中心。
然后执行render函数,将VNode渲染成DOM,初次渲染完成。
data属性变化,触发render
修改属性,被响应式的set监听到,set执行updateComponent,触发render函数,生成VNode和prevNode对象(新旧VNode对象),通过patch算法进行对比,修改原有的DOM结构,更新页面。
7、什么时候用到vue.nextTick()?
定义:
在下次DOM更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM
简单理解:
当数据更新了,在DOM渲染后,自动执行该函数。
使用:
在created钩子函数中,进行DOM操作,要放在vue.nextTick()回调函数中。
原因:created钩子函数执行的时候,DOM其实并为进行任何渲染。
在数据变化后要执行某个操作,而这个操作要随着数据改变而改变DOM结构时,这个操作是需要放在vue.nextTick()回调函数中。
原因:vue是异步执行DOM更新的,设置数据改变,vm.someData = ‘new value’, dom并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时,才会进行必要的dom更新。此时如果想要根据更新的dom状态去做某些事情时,就会出现问题。
二、vue核心知识–语法篇
1、问v-if和v-show有什么区别?
相同点:
两者都是在判断DOM节点是否要显示。
不同点:
a.实现方式: v-if是根据后面数据的真假值判断直接从Dom树上删除或重建元素节点。 v-show只是在修改元素的css样式,也就是display的属性值,元素始终在Dom树上。
b.编译过程:v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件; v-show只是简单的基于css切换;
c.编译条件:v-if是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译; v-show是在任何条件下(首次条件是否为真)都被编译,然后被缓存,而且DOM元素始终被保留;
d.性能消耗:v-if有更高的切换消耗,不适合做频繁的切换; v-show有更高的初始渲染消耗,适合做频繁的额切换;
2、vue常用的修饰符
a、按键修饰符
如:
.delete(捕获“删除”和”退格“键) 用法上和事件修饰符一样,挂载在v-on:后面,
语法:
v-on:keyup.xxx=’yyy’ <inputclass = 'aaa' v-model="inputValue" @keyup.delete="onKey"/>
b、系统修饰符
可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器
- .ctrl
- .alt
- .shift
- .meta
c、鼠标按钮修饰符
- .left
- .right
- .middle
这些修饰符会限制处理函数仅响应特定的鼠标按钮。如:<button @click.middle ="onClick">A</button>
鼠标滚轮单击触发 Click默认是鼠标左键单击
d、其他修饰符
.lazy
在默认情况下,v-model
在每次input
事件触发后将输入框的值与数据进行同步 ,我们可以添加lazy
修饰符,从而转变为使用change
事件进行同步:
<inputv-model.lazy="msg" >
.number
如果想自动将用户的输入值转为数值类型,可以给v-model
添加.number
修饰符:
<input v-model.number="age" type="number">
这通常很有用,因为即使在
type="number"
时,HTML 输入元素的值也总会返回字符串。如果这个值无法被parseFloat()
解析,则会返回原始的值。
- .trim
如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:
<inputv-model.trim="msg">
3、v-on可以监听多个方法吗?
当然可以
例如:
<p v-on="{click:clickDo,mousemove:MouseDo}"></p>
一个事件绑定多个函数:
<p @click="one(),two()">点击</p>
4、vue中key值得作用
使用key来给每个节点做一个唯一标识
key的作用主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,
否则vue只会替换其内部属性而不会触发过渡效果。
5、vue组件中的data为什么必须是函数?
在
new Vue()
中,data
是可以作为一个对象进行操作的,然而在component
中,data
只能以函数的形式存在,不能直接将对象赋值给它。当data选项是一个函数的时候,每个实例可以维护一份被返回对象的独立的拷贝,这样各个实例中的data不会相互影响,是独立的。
6、v-for与v-if的优先级
v-for和v-if同时使用,有一个先后运行的优先级,v-for比v-if优先级更高,
这就说明在v-for每次的循环赋值中每一次调用v-if的判断,所以不推荐v-if和v-for在同一个标签中同时使用。
解决方法:1、ul和li搭配使用,或者是渲染父级标签下的子标签。
<ul v-if = "state">
<li v-for = "(item,id) in list" :key="id"></li>
</ul>
2、使用过滤器将v-if中的判断转移到computed的计算属性中。
<ul>
<li v-for"(item,id) in formList" :key = "id"></li>
</ul>
computed: {
formList: function(){
return this.list.filter(function(item){
return item.state;
})
}
}
7.说出至少 4 种 vue 当中的指令和它的用法
v-if(判断是否隐藏)
v-for(把数据遍历出来)
v-bind(绑定属性)
v-model(实现双向绑定)
8、vue-loader是什么?使用它的用途有哪些?
定义:
vue-loader是webpack的一个loader,用于处理.vue 文件,是解析.vue文件的一个加载器。
作用:
js可以写成es6,css可以写成less、scss,template可以写成jade。
三、vue核心知识–组件篇
1、vue中子组件调用父组件的方法
第一种方法:直接在子组件中通过this.$parent.event来调用父组件的方法
父组件
<template>
<div>
<child></child>
</div>
</template>
<script>
import child from '~/components/dam/child';
export default {
components: {
child
},
methods: {
fatherMethod() {
console.log('测试');
}
}
};
</script>
子组件
<template>
<div>
<button @click="childMethod()">点击</button>
</div>
</template>
<script>
export default {
methods: {
childMethod() {
this.$parent.fatherMethod();
}
}
};
</script>
第二种方法:在子组件里用$emit
向父组件触发一个事件,父组件监听这个事件就行了。
父组件
<template>
<div>
<child @fatherMethod="fatherMethod"></child>
</div>
</template>
<script>
import child from '~/components/dam/child';
export default {
components: {
child
},
methods: {
fatherMethod() {
console.log('测试');
}
}
};
</script>
子组件
<template>
<div>
<button @click="childMethod()">点击</button>
</div>
</template>
<script>
export default {
methods: {
childMethod() {
this.$emit('fatherMethod');
}
}
};
</script>
第三种:父组件把方法传入子组件中,在子组件里直接调用这个方法
父组件
<template>
<div>
<child :fatherMethod="fatherMethod"></child>
</div>
</template>
<script>
import child from '~/components/dam/child';
export default {
components: {
child
},
methods: {
fatherMethod() {
console.log('测试');
}
}
};
</script>
子组件
<template>
<div>
<button @click="childMethod()">点击</button>
</div>
</template>
<script>
export default {
props: {
fatherMethod: {
type: Function,
default: null
}
},
methods: {
childMethod() {
if (this.fatherMethod) {
this.fatherMethod();
}
}
}
};
</script>
2、vue中父组件调用子组件的方法
a、父组件文件,使用子组件时,声明ref属性:
<v-header ref="header"></v-header>
b、父组件函数中,用this.$refs调子组件的数据或方法:如:
this.$refs.header.属性名 this.$refs.header.方法名
但要注意:ref 被用来给元素或子组件注册引用信息,引用信息将会注册在父组件的 $refs 对象上。(可以在控制台打印出来看看)
如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。
所以重点来了,如果子组件是一个弹出框,只有在触发某个点击事件时弹出框才能出现(也就是说在父组件中的子组件使用上用了v-if),那在父组件上如果不点击弹出框是不能获取到$ref的。
原因就是:引用指向的是子组件创建的实例,可以理解为绑在了DOM结构上
那如果我偏偏想调用的是这个子组件(弹出框)中的方法,但又不想要弹出框显示,怎么办呢?
解决方法:把v-if换成v-show,这样DOM元素会一直存在于父组件中,子组件的方法也就能调用了
c、子组件调用父组件属性和方法
this.$parent.属性名 this.$parent.方法名
3、vue组件之间传值
(1)父组件给子组件传值:
1.父组件调用子组件的时候动态绑定属性 <parent :dataList='dataList'></parent> 2.子组件定义props接收动态绑定的属性props: ['dataList'] 3.子组件使用数据
(2)子组件主动获取父子间的属性和方法:
在子组件中使用this. p a r e n t . 属 性 / t h i s . parent.属性/this. parent.属性/this.parent.方法。
(3)子组件给父组件传值:
一、使用ref属性
1.父组件调用子组件时绑定属性ref
2.在父组件中使用this. r e f s . p a r e n t . 属 性 / t h i s . refs.parent.属性/this. refs.parent.属性/this.refs.parent.方法二、使用$emit方法
1.子组件调用this.$emit('方法名‘,传值)
2.父组件通过子组件绑定的’方法名’获取传值。
(4)vue页面级组件之间传值
1.使用vue-router通过跳转链接带参数传参。
2.使用本地缓存localStorge。
3.使用vuex数据管理传值。
(5)说说vue的动态组件。
多个组件通过同一个挂载点进行组件的切换,is的值是哪个组件的名称,那么页面就会显示哪个组件。
主要考查面试这 component的 is属性。
(6)keep-alive内置组件的作用
可以让当前组件或者路由不经历创建和销毁,而是进行缓存,凡是被keep-alive组件包裹的组件,除了第一次以外。不会经历创建和销毁阶段的。第一次创建后就会缓存到缓存当中
(7)递归组件的用法
组件是可以在它们自己的模板中调用自身的。不过它们只能通过 name 选项来做这件事。
首先我们要知道,既然是递归组件,那么一定要有一个结束的条件,否则就会使用组件循环引用,最终出现“max stack size exceeded”的错误,也就是栈溢出。那么,我们可以使用v-if="false"作为递归组件的结束条件。当遇到v-if为false时,组件将不会再进行渲染。
四、vue核心知识–路由
1、怎么定义vue-router的动态路由?怎么获取传过来的值?
动态路由的创建,主要是使用path属性过程中,使用动态路径参数,以冒号开头,如下:
{ path: '/details/:id' name: 'Details' components: Details }
访问details目录下的所有文件,如果details/a,details/b等,都会映射到Details组件上。
当匹配到/details下的路由时,参数值会被设置到this.$route.params下,所以通过这个属性可以获取动态参数
this.$route.params.id
2、vue-router有哪几种路由守卫?
路由守卫为:
全局守卫:前置守卫->beforeEach, 后置守卫->afterEach
局部守卫(组件内的钩子):beforeRouterEnter、beforRouterUpdate、beforRouterLeave
路由独享守卫:beforeEnter
3、$route和 $router的区别是什么?
$router为VueRouter的实例,是一个全局路由对象,包含了路由跳转的方法、钩子函数等。
$route 是路由信息对象||跳转的路由对象,每一个路由都会有一个route对象,是一个局部对象,包含path,params,hash,query,fullPath,matched,name等路由信息参数。
4、vue-router响应路由参数的变化
a、用watch检测
b、组件内导航钩子函数
5、vue-router 传参
a、使用Params:
- 只能使用name,不能使用path
- 参数不会显示在路径上
- 浏览器强制刷新参数会被清空
b、使用Query:
- 参数会显示在路径上,刷新不会被清空
- name 可以使用path路径
6、<keep-alive></keep-alive>
的作用是什么?
官方:包裹组件时,会缓存不活动的组件实例,主要是用于保留状态和避免重渲染。
理解:比如有一个列表页和详情页,用户操作时会详情页=>列表页=>详情页。。。这样的话列表页和详情页都是高频率的页面,那么对组件使用缓存,每次用户返回列表的时候,都能从缓存中快速渲染,而不是重新渲染。
7、active-class是哪个组件的属性?
active-class是vue-router的样式方法,当router-link标签被点击时将会应用这个样式。
五、vue核心知识–vuex
1、不用vuex会带来什么问题?
a、可维护性会下降,你要想修改数据,你得维护三个地方
b、可读性会下降,因为一个组件里的数据,你根本就看不出来是从哪来的
c、增加耦合,大量的上传派发,会让耦合性大大的增加,本来Vue用Component就是为了减少耦合,现在这么用,和组件化的初衷相背。
2、vuex有哪几种属性?
有五种,分别是 State、 Getter、Mutation 、Action、 Module。
3、vuex的State特性是?
a、Vuex就是一个仓库,仓库里面放了很多对象。其中state就是数据源存放地,对应于与一般Vue对象里面的data
b、state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新
c、它通过mapState把全局的 state 和 getters 映射到当前组件的 computed 计算属性中
4、vuex的Getter特性是?
a、getters 可以对State进行计算操作,它就是Store的计算属性
b、 虽然在组件内也可以做计算属性,但是getters 可以在多组件之间复用
c、 如果一个状态只在一个组件内使用,是可以不用getters
5、Vue.js中ajax请求代码应该写在组件的methods中还是vuex的actions中?
a、如果请求来的数据是不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入vuex 的state里。
b、如果被其他地方复用,这个很大几率上是需要的,如果需要,请将请求放入action里,方便复用,并包装成promise返回,在调用处用async await处理返回的数据。如果不要复用这个请求,那么直接写在vue文件里很方便。