Vue相关面试题
一、vue双向数据绑定原理
数据双向绑定 vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty() 来对Vue的数据进行数据劫持,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。
observer:会对data里所有属性进行数据劫持和数据代理,使数据拥有get和set方法
compile:对模板进行解析,将模板中的变量替换成数据,然后初始化渲染页面视图
watcher:负责数据监听,当数据发生改变通知订阅者,调用视图更新函数更新视图
Object.defineProperty()方法
语法:
Object.defineProperty(obj, prop, descriptor)
三个参数:
obj: 必需。目标对象;
prop: 必需。需定义或修改的属性的名字;
descriptor: 必需。目标属性所拥有的特性;
<input type="text" id="in"/>
输入的值为:<span id="out"></span>
<script>
var int = document.getElementById('in');
var out = document.getElementById('out');
var obj = {};
Object.defineProperty(obj, 'msg', {
enumerable: true,
configurable: true,
set (newVal) {
out.innerHTML = newVal;
}
})
int.addEventListener('input', function(e) {
obj.msg = e.target.value;
})
</script>
二、组件中data为什么是个函数
组件中定义data属性,只能是一个函数
<script>
export default {
data() {
return {
name:"只能是函数"
};
}
};
</script>
- 组件实例对象data必须为函数,目的是为了防止多个组件实例对象之间共用一个data,产生数据污染。
三、v-if和v-show的区别
共同点:v-if 和 v-show 都能实现元素的显示隐藏
区别:
v-if 是动态的向DOM树内添加或者删除DOM元素;
v-show 是通过设置DOM元素的display样式属性控制显隐;
v-if 切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;
v-show只是简单的基于css切换;
v-if更适合数据的筛选和初始渲染
v-show更适合元素的切换
四、v-if和v-for的优先级
首先:永远不要把 v-if 和 v-for 同时用在同一个元素上
其次:当 Vue 处理指令时,v-for 比 v-if 具有更高的优先级
v-for比v-if优先,如果每一次都需要遍历整个数组,将会影响速度
// 错误写法
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</li>
如上情况,即使100个user中之需要使用一个数据,也会循环整个数组。
// 正确写法
computed: {
activeUsers: function () {
return this.users.filter(function (user) {
return user.isActive
})
}
}
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
五、v-for中的key值的作用
key值代表唯一
当我们在使用v-for时,需要给单元加上key
<ul>
<li v-for="item in data" :key="item.id">...</li>
</ul>
key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确, 更快的找到对应的vnode节点
我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。
key的作用主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们
六、修改数据页面不更新的原因和解决方案
原因:对象是引用类型,直接改变对象的某属性但是指向地址没变,vue不一定能监控到,所以当我们新建一个对象,就直接改变了他的指向地址。
使用下面这些方法操作数组,其数据会被vue监测
push()、pop()、shift()、unshift()、splice()、sort()、reverse()可被vue监测到。
解决方案
Vue.set( target, propertyName/index, value )
参数:
target:要修改的对象或数组
propertyName/index:属性或下标
value:修改后的value值
Object.assign()
直接使用 Object.assign() 添加到对象的新属性不会触发更新
$forceUpdate
$forceUpdate迫使Vue 实例重新渲染
小结
-
如果为对象添加少量的新属性,可以直接采用Vue.set()
-
如果需要为新对象添加大量的新属性,则通过Object.assign()创建新对象
-
如果你需要进行强制刷新时,可采取$forceUpdate() (不建议)
七、$nextTick()
1. 什么是Vue.nextTick()??
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
理解:nextTick(),是将回调函数延迟在下一次dom更新数据后调用,
简单的理解是:当数据更新了,在dom中渲染后,自动执行该函数
<template>
<div class="hello">
<div>
<button id="firstBtn" @click="testClick()" ref="aa">{{testMsg}}</button>
</div>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
testMsg:"原始值",
}
},
methods:{
testClick:function(){
let that=this;
that.testMsg="修改后的值";
console.log(that.$refs.aa.innerText); //that.$refs.aa获取指定DOM,输出:原始值
}
}
}
</script>
2. 使用this.$nextTick()
methods:{
testClick:function(){
let that=this;
that.testMsg="修改后的值";
that.$nextTick(function(){
console.log(that.$refs.aa.innerText); //输出:修改后的值
});
}
}
注意:Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。$nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM
3. Vue.nextTick(callback) 使用原理:
原因是,Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环 (event loop) 当中观察到数据变化的 watcher 推送进这个队列。如果这个watcher被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOM操作。而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新。
当你设置 vm.someData = ‘new value’,DOM 并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM更新。如果此时你想要根据更新的 DOM 状态去做某些事情,就会出现问题。。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。
八、vue-router钩子函数和执行顺序
vue-router的导航守卫实际和组件的生命周期都是同一类型的钩子函数,在一个特定时间内会触发。
导航守卫有三个类型,分别是全局的钩子(针对整个路由器实例),单个路由对象的钩子和组件内的钩子。
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes,
});
//全局前置守卫
router.beforeEach((to, from, next) => {
});
// 全局解析守卫
router.beforeResolve((to, from, next) => {
});
// 全局后置钩子
router.afterEach((to, from) => {
});
以上三个都是全局钩子,无论是哪个路由对象被激活,这些钩子都会被触发,只是触发的时机不同。
单个路由对象的钩子:
{
path: "/",
name: "Home",
component: Home,
// 路由独享守卫
beforeEnter: (to, from, next) => {
},
}
组件内的钩子:
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 可以访问组件实例 `this`
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
而在Vue-Router中,导航守卫的执行顺序如下所示
九、Vuex的核心概念和运行机制
1. 什么是Vuex?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,它采用集中式存储和管理程序的所有组件的数据
2. Vuex核心概念 五部分组成
- state 所有的数据都存储在state中 state是一个对象
- mutations 可以直接操作state中的数据
- actions 只能调用mutations的方法
- getters 类似计算属性实现对state中的数据做一些逻辑性的操作
- modules 将仓库分模块存储
3. Vuex运行机制
在组件中通过dispatch来调用actions中的方法
在actions中通过commit来调用mutations中的方法
在mutations中可以直接操作state中的数据,state的数据只要一发生改变立马响应到组件中
十、axios的封装
一般我会在项目的src目录中,新建一个utils文件夹,然后在里面新建一个request.js。
request.js具体内容
//导入axios依赖包
import axios from "axios";
//2. 创建axios的实例化对象
const Server = axios.create({
baseURL:'',//后期请求接口的基地址
timeout: 5000,//5s超时时间
});
//3. 设置请求拦截器配置
Server.interceptors.request.use(function(config){//成功的时候
return config
},function(error){//失败
return Promise.reject(error);
})
//4. 配置相应拦截器的信息
Server.interceptors.response.use(function(response){
if(response.status == 200){
return response.data;
}
return response;
},function(error){//失败
return Promise.reject(error);
});
//5. 导出模块
export default Server;
在main.js中全局使用
// 引入axios
import axios from "./utils/request"
Vue.prototype.$axios = axios;
以上是我总结的vue相关面试题,可能有的不足、表达不全面,可以在下面评论,互相学习,谢谢观看!