前端开发面试题总结[2]
一、Vue2 相关
1. MVVM视图模型:
M
:模型Model,指data
中的数据;
V
:视图View,模板代码;
VM
:视图模型ViewModel,指Vue实例对象;
总结:后端传递的数据M
是通过VM
将数据与页面V
连接起来的。
观察发现:data
身上所有的属性都出现在VM身上,VM
和 Vue原型
身上所有的属性,在Vue模板中都可以直接使用
2. Vue2 和 Vue3 是如何实现双向数据绑定的?
Vue2:
Vue2的双向数据绑定是通过数据劫持结合订阅发布
实现的,通过Object.defineProperty()
来劫持各个属性的setter
、getter
,在数据变动时发布消息给订阅者,触发相应的监听回调。
Vue3:
Vue3的双向数据绑定是通过Proxy
数据代理,Vue3会为所有的主要数据属性创建一个Proxy
对象,当数据发生变化时Proxy
对象会捕获其更新并将其传播到视图上,这样就实现了双向数据绑定。
3.Vue2 和 Vue3 的生命周期
Vue2 生命周期:
-
beforeCreate
:创建前,此时vm无法访问data中的数据、methods中的方法。 -
ceated
:创建后,此时可以访问到data中的数据、methods中配置的方法。 -
beforeMount
:挂载前,完成模板的编译,虚拟Dom也完成了创建,即将渲染、修改数据,不会触发updated(所有对Dom的操作,都不奏效) -
mounted
:挂载后,把编译好的模板挂载到页面,这里可以发送异步请求,也可以访问Dom节点。 -
beforeUpdate
:更新前,此时数据是新的,页面上的数据是旧的,组件即将更新,准备渲染,可以修改数据 -
updated
:更新后,render重新渲染,此时数据和页面都是新的,避免在此更新数据 -
beforeDestroy
:销毁前,在这里实例都还可以用,可关闭定时器、解绑自定义事件、取消订阅消息等 -
destroy
:销毁后,组件已经全部销毁了,全部都销毁。
Vue3 生命周期:
除了beforecate
和created
(它们被setup方法本身所取代),我们可以在setup方法中访问的API生命周期钩子有9个选项:
1.onBeforeMount
– 在挂载开始之前被调用:相关的 render 函数首次被调用。
2.onMounted
– 组件挂载时调用
3.onBeforeUpdate
– 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
4.onUpdated
– 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
5.onBeforeUnmount
– 在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。
6.onUnmounted
– 卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。
注:Vue2 和 Vue3 的keep-alive
多的两个生命周期再此未写。activited
组件激活时,deactivited
组件销毁时。
4.第一次加载页面时会触发哪几个生命周期?
会触发:beforeCreate
ceated
beforeMount
mounted
这几个生命周期函数。
5.Vue2 和 Vue3是如何进行组件传值的?
Vue2:
父传子
:父组件在子组件上绑定一个自定义属性并把数据绑定在自定义属性上,在子组件添加参数props即可子传父
:子组件通过Vue实例方法$emit进行触发并且可以携带参数,父组件监听使用@(v-on)进行监听,然后进行方法处理。兄弟组件传值
:
引入第三方的new Vue定义为eventBus
在组件created中订阅方法eventBus.$on(‘自定义事件名’,methods中的方法名);
在另一个兄弟组件methods中写函数,函数发布eventBus订阅方法eventBus.$on(‘自定义事件名’);
在组件中绑定事件名。(eg:click)
Vue3:
父传子
:父组件通过数据绑定子组件通过 方法创建props对象,子组件通过definePropty
即可拿到父组件传来的数据。
父组件
<child-components :formData='formData'>
子组件
<template>
<ul>
<li v-for="i in props.list" :key="i">{{ i }}</li>
</ul>
</template>
<script setup lang='ts'>
// 引入 defineProps
import { defineProps } from 'vue'
const props = defineProps({
formData: {
type: Array,
default: () => [],
},
})
</script>
子传父
:子组件使用emit可以想=向父组件传值,父组件接收
子组件 传值
const emits = defineEmits(['add'])
emits('add', 1)
父组件 接收
<!-- add是子组件要传递的动作,handleAdd是监听到之后执行的事件 -->
<child-components @add="handleAdd"></child-components>
<script>
const list = ref([1,2,3])
const handleAdd = value => {
list.value.push(value)
}
</script>
推荐使用依赖注入的方式:点此查看依赖注入使用方式(注意代码块的注释)
Vuex
和Pinia
也可以实现方便的传参
6.Vue组件中的data为什么是一个函数?
data是函数的话方便使用,防止数据污染(假设两个组件有相同属性名的数据,页面不知道渲染哪个)。
7.Vue常用指令有哪些?
v-model
:用于双向数据绑定;
v-on:click
:给标签绑定函数,例如点击事件;
v-for
:用于循环遍历数组
v-if
:用于控制显示隐藏Dom元素(从Dom中删除元素,配和v-else
使用)
v-show
:用于控制显示隐藏Dom元素(相当于给元素加display:none
,只是隐藏,元素还在)
v-bind
: 动态单向绑定数据
v-text
:解析文本
v-html
:解析html标签
v-once
:只渲染一次
v-cloak
: 防止闪烁
v-pre
: 把标签内部元素原位输出
8.Vue如何自定义指令?
全局定义:通过Vue.directive(id,definition)
方法可以注册一个全局自定义指令,该方法可以接收两个参数:指令ID和定义对象。指令ID是指令的唯一标识,定义对象是定义的指令的钩子函数
<div id="element">
<input v-ikun>
</div>
<script type="text/javascript">
// 第一个参数为指令名字 第二个参数为执行的函数
Vue.directive('ikun',{
// inserted(节点使用时触发的函数)
inserted:function(el){
//使元素自动获得焦点
el.focus();
}
})
var demo = new Vue({
el: '#element'
})
</script>
9.watch、methods、computed 的区别?
computed(计算属性)
:在Dom元素加载后马上执行methods
:必须要有一定条件才能触发,例如点击事件watch
:监听属性,它需要观察到Vue实例上的数据变动后才执行
10.watch开启深度监听以及开启立即调用?
// 开启深度监听 开启立即监听
{deep:true;immediated:true}
11.路由之间是如何跳转的?有哪些方式?
1.< router-link to=‘需要跳转到的页面路径’ />
2.this.$router.push() 跳转到指定的url,并在history添加记录,点击返回退回到上一个页面。
3.this.$router.go(n)向前或者向后跳转n个页面,n可以是正数,也可以是负数。
12.路由之间是如何实现传参的?
this.$router.params
和 this.$router.query
区别:query需要path引入,params要用name来引入,
接收参数时分别是:this.$route.params.name
和 this.$route.query.name
(注意是$route)