Vue.js面试题
1、MVVM框架
MVVM是Model-View-ViewModel的简写。MVVM分为三层,M层,V层,VM层。
- M层即模型层,一般代表服务端的业务逻辑处理,负责与数据库交互。
- V层即视图层,一般代表前端展示的页面,用于数据的展示。
- VM层即视图模型层,负责通过接口从服务端(M层)获取数据,并将数据绑定到页面(V层);
VM层是连通M层和V层的桥梁。在Vue中,V层相当于vue实例的视图模板;VM层相当于vue实例本身,拥有向后台请求数据和操作数据的能力。
MVVM最主要的优点在于实现前后端分离,提高开发效率。
2、Vue的响应式原理
响应式原理:Vue 遍历data选项对象所有的 property,并使用 Object.defineProperty(obj, propName, value)
把这些 property 全部转为 getter/setter
。
每个组件实例都对应一个 watcher 实例,监听数据更改并更新页面。

对于对象
Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data
对象上存在才能让 Vue 将它转换为响应式的。例如:
var vm = new Vue({
data:{
a:1
}
})
// vm.a 是响应式的
vm.b = 2
// vm.b 是非响应式的
如何添加响应式属性?
可以使用 Vue.set(object, propertyName, value)
方法向嵌套对象添加响应式 property。
还可以使用 vm.$set
实例方法,这也是全局 Vue.set
方法的别名
赋值多个新 property:
// 代替Object.assign(this.someObject, { a: 1, b: 2 })
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
对于数组
Vue 不能检测以下数组的变动:
-
利用索引设置数组项,例如:
vm.items[indexOfItem] = newValue
-
修改数组长度,例如:
vm.items.length = newLength
举个例子:
var vm = new Vue({ data: { items: ['a', 'b', 'c'] } }) vm.items[1] = 'x' // 不是响应性的 vm.items.length = 2 // 不是响应性的
如何响应式变动数组?
对于第1种情况:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)// 删除再添加
// vm.$set
vm.$set(vm.items, indexOfItem, newValue)
对于第2种情况:
// Array.prototype.splice
vm.items.splice(newLength)// 从指定索引处开始删除数组后的所有元素,若该数大于数组长度则不进行任何操作
3、Vue生命周期
常用钩子函数:
beforeCreate:
此时组件的选项对象还未创建,el 和 data 并未初始化,因此无法访问methods, data, computed等上的方法和数据。
created:
实例已完成以下配置:数据观测、属性和方法的运算,watch/event事件回调,完成了data 数据的初始化,$el还没有准备好。
beforeMount:
编译模板,把data里面的数据和模板生成html,完成了el和data 初始化,但并未挂载。
mounted:
挂载完成,也就是模板中的HTML渲染到HTML页面中,此时一般可以做一些ajax操作,mounted只会执行一次。
beforeUpdate:
发生在虚拟DOM重新渲染和打补丁之前
updated:
在由于数据更改导致的虚拟DOM重新渲染和打补丁完成时调用,调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作,在大多是情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。
beforeDestroy:
这一步还可以用this来获取实例,一般在这一步做一些重置的操作,比如清除掉组件中的定时器和 监听的dom事件。
destroyed:
在实例销毁之后调用,调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。
4、v-for与key
在v-for中使用key属性的目的:
-
高效地更新虚拟DOM
-
完整地触发组件的生命周期钩子
-
触发过渡
5、路由的hash模式和history模式
1、hash
hash模式也就是锚点(#), 本质上是改变window.location的hash属性。我们可以通过直接赋值location.hash来改变href, 但是页面不刷新
2、history
history接口是HTML5新增的, 它有五种模式改变URL而不刷新页面.
history.pushState()
history.replaceState()
history.go(num)
history.back()
history.forward()
在发起http请求时的不同:
1:hash 模式下,仅hash符号之前的内容会被包含在请求中,即使没有做到对路由的全覆盖,也不会返回404错误。
2:history模式下,前端的URL必须和实际向后端发起请求的URL一致。否则将返回404错误 ,必须在服务器进行配置避免错误。
6、插槽
默认插槽、具名插槽、作用域插槽。
<!-- 子组件 -->
template: '
<slot></slot>// 默认插槽
<slot name='name'></slot>// 具名
<slot name="name" :prop="data"></slot>// 作用域
'
<!-- 父组件 -->
1、默认插槽
<childComponent>
<!-- 任何HTML内容 -->
</childComponent>
2、具名插槽
<childComponent>
<template v-slot:name></template>
</childComponent>
3、作用域插槽
<childComponent>
<template v-slot:name="propsObj">
{{ propsObj.prop }}
</template>
</childComponent>
7、vue的内置组件
1、<component :is="conponentName"></component>
2、<transition></transition>
3、<keep-alive></keep-alive>// 使组件实例能够在它们第一次被创建的时候被缓存下来
4、<slot name="name"></slot>
8、组件传值
1、父=>子
通过子组件的props:
// 子组件
<template>
<div>
{{ prop1 }}
</div>
</template>
<script>
export defualt {
props: ['prop1','prop2', ...]
}
</script>
// 父组件
<template>
<div>
<child-component :prop1="parentData" />
</div>
</template>
<script>
export default {
data() {
return {
parentData: 'value';
}
}
}
</script>
2、子=>父
通过$emit(eventType, data)
// 子组件
<template>
<div>
<button @click="handler"></button>
</div>
</template>
<script>
export default {
data() {
return {
childData: 'value',
}
},
methods: {
handler() {
this.$emit('event-name', childData)
}
}
}
</script>
//父组件
<template>
<div>
<child-component @event-name="getData" />
{{ parentData }}
</div>
</template>
<script>
export default {
data() {
return {
parentData: ''
}
},
methods: {
getData(value) {
this.parentData = value;
}
}
}
</script>
3、非父子组件间
通过vuex
// store/index.js
let store = Vuex.Store({
state: {
storeData: 'data',
},
mutations: {
changeData(state, newValue) {
state.storeData = newValue;
}
}
})
export default store;
// component-a
<template>
<div>
<input v-model="dataA">
<button @click="change(dataA)">改变component-a的数据</button>
</div>
</template>
<script>
import { mapMutations} from 'vuex'
export default {
name: 'Home',
data() {
return {
dataA: 'value'
}
},
methods: {
...mapMutations(['changeData']),
change(newValue) {
//this.changeData(newValue)
this.$store.commit('changeData', newValue)
}
}
}
</script>
// component-b
<template>
<div>
component-b的数据将改变:{{ dataB }}
</div>
</template>
<script>
export default {
computed: {
dataB() {
return this.$store.state.storeData;
}
}
}
</script>
9、v-if和v-show的区别
1、v-if值为假时直接将元素从DOM中删除
2、v-show是切换元素的css属性display
/* v-show值为真时 */
display: block;
/* v-show值为假时 */
display: none;
10、为什么要避免v-if和v-for一起使用在同一个元素上
因为v-for比v-if有更高的优先级,所以每次渲染时都会遍历整个数据列表,无论数据列表是否发生了变化,这在数据列表庞大时会造成一定性能问题。可以通过将数据列表换成计算属性来解决,以此避免使用v-if。
11、异步更新队列
1、Vue在观察到数据变化时并不是直接更新DOM,而是开启一个队列,并缓冲同一事件循环中发生的所有数据改变。
2、在缓冲时会去除重复的(未改变的)数据,以此减少DOM
操作,
3、vue是数据驱动的,所以我们应避免直接操作DOM,因为可能达不到我们想要的效果。我们可以把依赖于更新后的DOM的操作放到一个特殊的函数nextTick中,该函数的回调会在下次DOM更新完成后执行:
this.$nextTick(function() {
...
})
// 或
Vue.nextTick(function() {
...
})
12、Vuex
Vuex用于Vue的状态管理。
通过new Vuex.Store()
并传入一个选项对象来创建一个主仓库store实例。
一个store实例一般有几个属性:
1、state, 存放数据,这些数据是响应式的。
2、getters, 相当于数据的计算属性。
3、mutations, 存放更改state数据的一些方法,更改state的唯一方式就是调用store实例的commit方法。
4、actions, 存放提交mutation的一些方法,可以是异步的。
// 主仓库
5、modules, 用于声明子仓库
6、plugins, 声明插件
// 子仓库
7、namespaced, 开启命名空间
vuex的辅助函数:
mapState, mapGetters, mapMutations, mapActions。
13、 r o u t e r 和 router和 router和route的区别
$router是通过VueRouter构造函数创建的router实例,常用方法有:push,replace, beforeEach, afterEach
。
$route表示当前路由对象,存储当前路由的一些信息,比如path、fullpath、hash、query等。
14、路由传参的方式和区别
1、方式:params 和 query
2、区别:
1)params配合name,传递的参数在地址栏不会显示,类似于post
2)query配合path,传递的参数会在地址栏显示出来,类似于get
3、举例说明:
1)params 传参
传: this.$router.push({
name: 'particulars',
params: {
id: id
}
})
接:this.$route.params.id
2)query传参
传:this.$router.push({
path: '/particulars',
query: {
id: id
}
})
接:this.$route.query.id
15、导航守卫
导航守卫函数一般都需传入三个参数:to, from, next
,后置守卫一般不用传入next方法。
守卫种类包括:
1、全局守卫
beforeEach
afterEach
2、路由独享守卫
beforeEnter, ...
3、组件内守卫
beforeRouteEnter// 不能直接访问组件实例,可通过传入next()的回调的第一个参数vm访问
beforeRouteUpdate// 在路由改变但组件被复用时调用
beforeRouteLeave
16、请求/响应拦截器
1、请求拦截器
axios.interceptors.request.use((config) => {
// 给请求头设置一个token或其他操作
return config;
}, (error) => {
// 对请求错误做些什么
return Promise.reject(error);
});
2、响应拦截器
axios.interceptors.response.use((response) => {
// 对响应数据做一些预处理
return response.data;
}, (error) => {
// 对响应错误做点什么
return Promise.reject(error);
});
17、Vue有哪些优点?
1、简单易学
2、双向数据绑定
3、组件化
4、数据驱动
5、虚拟DOM
18、单页应用程序(SPA)的优缺点
SPA即整个应用只有一个HTML页面,HTML内容的变化是由路由跳转机制实现的,不会重新加载整个页面。
优点:局部更新,性能良好,用户体验好。
缺点:首屏加载耗时多,且不利于SEO(搜索引擎优化)。
19、SPA首屏加载缓慢怎么解决?
// 1、路由懒加载
routes = [
{
path: '',
name: '',
component: () => import('./A.vue')
}
]
// 2、静态资源本地缓存(怎么实现?)
// 3、UI组件库按需加载
// 4、图片资源压缩
// 5、使用SSR(服务端渲染)
// 6、开启gzip