01-Vue组件之间通信方式有哪些
vue是组件化开发框架,所以对于vue应用来说组件间的数据通信非常重要。此题主要考查大家vue基本功,对于vue基础api运用熟练度。另外一些边界知识如provide/inject/$attrs则体现了面试者的知识广度。
思路分析:
1.总述知道的所有方式
2.按组件关系阐述使用场景
回答范例:
1.组件通信常用方式有以下8种:
js
props
$emit/$on(vue3废弃)
$children(vue3废弃)/$parent
$attrs/$listeners(vue3废弃)
ref
$root
eventbus
vuex
2.根据组件之间关系讨论组件最为清晰有效
- 父子组件
js
props/$emit/$parent/ref/$attrs
2.兄弟组件
js
$parent/$root/eventbus/vuex
3.跨层级关系
js
eventbus/vuex/provide+inject
02-v-if和v-for哪个优先级更高?
分析:
此题考查常识,文档中曾有详细说明v2|v3;也是一个很好的实践题目,项目中经常会遇到,能够看出面试者api熟悉程度和应用能力。
思路分析:
1.先给出结论
2.为什么是这样的,说出细节
3.哪些场景可能导致我们这样做,该怎么处理
4.总结,拔高
个人面试技巧(适用任何面试问题):
- 结合做过的项目
三个方面
1.首先说出答案
2.
尽量遵循WWWW (4W)回答方法,where,when,what,why
3.讲人话:在哪里,在什么时候,在什么事情,为什么这样做
4.以及有什么可以优化的
回答范例
-
实践中不应该把v-for和v-if放一起
-
在vue2中,v-for的优先级是高于v-if,把它们放在一起,输出的渲染函数中可以看出会先执行循环再判断条件,哪怕我们只渲染列表中一小部分元素,也得在每次重渲染的时候遍历整个列表,这会比较浪费;另外需要注意的是在vue3中则完全相反,v-if的优先级高于v-for,所以v-if执行时,它调用的变量还不存在,就会导致异常
-
通常有两种情况下导致我们这样做:
-
为了过滤列表中的项目 (比如 v-for="user in users" v-if="user.isActive")。此时定义一个计算属性 (比如 activeUsers),让其返回过滤后的列表即可(比如users.filter(u=>u.isActive))。
-
为了避免渲染本应该被隐藏的列表 (比如 v-for="user in users" v-if="shouldShowUsers")。此时把 v-if 移动至容器元素上 (比如 ul、ol)或者外面包一层template即可。
-
文档中明确指出永远不要把 v-if 和 v-for 同时用在同一个元素上,显然这是一个重要的注意事项。
-
源码里面关于代码生成的部分,能够清晰的看到是先处理v-if还是v-for,顺序上vue2和vue3正好相反,因此产生了一些症状的不同,但是不管怎样都是不能把它们写在一起的。
03-能说一说双向绑定使用和原理吗?
题目分析:
双向绑定是vue的特色之一,开发中必然会用到的知识点,然而此题还问了实现原理,升级为深度考查。 思路分析:
- 给出双绑定义
- 双绑带来的好处
- 在哪使用双绑
- 使用方式、使用细节、vue3变化
- 原理实现描述
回答范例:
- vue中双向绑定是一个指令v-model,可以绑定一个响应式数据到视图,同时视图中变化能改变该值。
- v-model是语法糖,默认情况下相当于:value和@input。使用v-model可以减少大量繁琐的事件处理代码,提高开发效率。
- 通常在表单项上使用v-model,还可以在自定义组件上使用,表示某个值的输入和输出控制。
- 通过
<input v-model="xxx">
的方式将xxx的值绑定到表单元素value上;对于checkbox,可以使用true-value和false-value指定特殊的值,对于radio可以使用value指定特殊的值;对于select可以通过options元素的value设置特殊的值;还可以结合.lazy,.number,.trim对v-mode的行为做进一步限定;v-model用在自定义组件上时又会有很大不同,vue3中它类似于sync修饰符,最终展开的结果是modelValue属性和update:modelValue事件;vue3中我们甚至可以用参数形式指定多个不同的绑定,例如v-model:foo和v-model:bar,非常强大! - v-model是一个指令,它的神奇魔法实际上是vue的编译器完成的。我做过测试,包含v-model的模板,转换为渲染函数之后,实际上还是是value属性的绑定以及input事件监听,事件回调函数中会做相应变量更新操作。编译器根据表单元素的不同会展开不同的DOM属性和事件对,比如text类型的input和textarea会展开为value和input事件;checkbox和radio类型的input会展开为checked和change事件;select用value作为属性,用change作为事件。
04-Vue中如何扩展一个组件
答题思路:
- 按照逻辑扩展和内容扩展来列举
- 逻辑扩展有:mixins、extends、composition api;
- 内容扩展有slots;
- 分别说出他们使用方法、场景差异和问题。
- 作为扩展,还可以说说vue3中新引入的composition api带来的变化
回答范例:
- 常见的组件扩展方法有:mixins,slots,extends等
- 混入mixins是分发 Vue组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
- 插槽主要用于vue组件中的内容分发,也可以用于组件扩展。
- 混入的数据和方法不能明确判断来源且可能和当前组件内变量产生命名冲突,
vue3中引入的composition api
,可以很好解决这些问题,利用独立出来的响应式模块可以很方便的编写独立逻辑并提供响应式的数据,然后在setup选项中组合使用,增强代码的可读性和维护性。例如:
js
// 复用逻辑1
function useXX() {}
// 复用逻辑2
function useYY() {}
// 逻辑组合
const Comp = {
setup() {
const {xx} = useXX()
const {yy} = useYY()
return {xx, yy}
}
}
05-子组件可以直接改变父组件的数据么,说明原因
分析
这是一个实践知识点,组件化开发过程中有个
单项数据流原则
,不在子组件中修改父组件是个常识问题。
思路
- 讲讲单项数据流原则,表明为何不能这么做
- 举几个常见场景的例子说说解决方案
- 结合实践讲讲如果需要修改父组件状态应该如何做
回答范例
- 所有的 prop 都使得其父子之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。另外,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器控制台中发出警告。
- 实际开发过程中有两个场景会想要修改一个属性:
这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用
。在这种情况下,最好定义一个本地的 data,并将这个 prop 用作其初始值:
js
const props = defineProps(['initialCounter'])
const counter = ref(props.initialCounter)
这个 prop 以一种原始的值传入且需要进行转换
。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
js
const props = defineProps(['size'])
// prop变化,计算属性自动更新
const normalizedSize = computed(() => props.size.trim().toLowerCase())
- 实践中如果确实想要改变父组件属性应该
emit一个事件让父组件去做这个变更
。注意虽然我们不能直接修改一个传入的对象或者数组类型的prop,但是我们还是能够直接改内嵌的对象或属性。
06-Vue要做权限管理该怎么做?控制到按钮级别的权限怎么做?
分析
综合实践题目,实际开发中经常需要面临权限管理的需求,考查实际应用能力。 权限管理一般需求是两个:页面权限和按钮权限,从这两个方面论述即可。
思路
- 权限管理需求分析:页面和按钮权限
- 权限管理的实现方案:分后端方案和前端方案阐述
- 说说各自的优缺点
回答范例
- 权限管理一般需求是页面权限和按钮权限的管理
- 具体实现的时候分后端和前端两种方案:
- 前端方案会把所有路由信息在前端配置,通过路由守卫要求用户登录,用户登录后根据角色过滤出路由表。比如我会配置一个asyncRoutes数组,需要认证的页面在其路由的meta中添加一个roles字段,等获取用户角色之后取两者的交集,若结果不为空则说明可以访问。此过滤过程结束,剩下的路由就是该用户能访问的页面,最后通过router.addRoutes(accessRoutes)方式动态添加路由即可。
- 后端方案会把所有页面路由信息存在数据库中,用户登录的时候根据其角色查询得到其能访问的所有页面路由信息返回给前端,前端再通过addRoutes动态添加路由信息
- 按钮权限的控制通常会实现一个指令,例如v-permission,将按钮要求角色通过值传给v-permission指令,在指令的moutned钩子中可以判断当前用户角色和按钮是否存在交集,有则保留按钮,无则移除按钮。
- 纯前端方案的优点是实现简单,不需要额外权限管理页面,但是维护起来问题比较大,有新的页面和角色需求就要修改前端代码重新打包部署;服务端方案就不存在这个问题,通过专门的角色和权限管理页面,配置页面和按钮权限信息到数据库,应用每次登陆时获取的都是最新的路由信息,可谓一劳永逸!
服务端返回的路由信息如何添加到路由器中?
js
// 前端组件名和组件映射表
const map = {
//xx: require('@/views/xx.vue').default // 同步的⽅式
xx: () => import('@/views/xx.vue') // 异步的⽅式
}
// 服务端返回的asyncRoutes
const asyncRoutes = [
{ path: '/xx', component: 'xx',... }
]
// 遍历asyncRoutes,将component替换为map[component]
function mapComponent(asyncRoutes) {
asyncRoutes.forEach(route => {
route.component = map[route.component];
if(route.children) {
route.children.map(child => mapComponent(child))
}
})
}
mapComponent(asyncRoutes)
07-计算属性和watch以及methods的区别
- 作用机制上: watch 和 computed 都是以 Vue 的依赖追踪机制为基础的,当某一个数据发生变化的时候, 所有依赖这个数据的相关数据,自动发生变化,也就是自动调用相关的函数去实现数据的变动 methods 里面是用来定义函数的,它需要手动调用才能执行。而不像 watch 和 computed 那样, "自动执行"预先定义的函数,相比于 watch / compute;methods 不处理数据逻辑关系,只提供可调用的函数,类似储存函数的一个库。
- 从性质上: methods里面定义的是函数,你显然需要像"fuc()"这样去调用它(假设函数为fuc)。 computed是计算属性,事实上和和data对象里的数据属性是同一类的(使用上)。 watch:类似于监听机制+事件机制,回调函数自动调用。
08- <math xmlns="http://www.w3.org/1998/Math/MathML"> r o u t e 和 route和 </math>route和router 的区别
router是VueRouter的一个对象,通过Vue.use(VueRouter)和VueRouter构造函数得到一个router的实例对象,这个对象中是一个全局的对象,包含了所有的路由包含了许多关键的对象和属性。例如history对象
route是一个跳转的路由对象,每一个路由都会有一个route对象,是一个局部的对象,可以获取对应的name,path,params,query等
09-请详细说下你对vue生命周期的理解
vue2.0生命周期:
- 创建阶段 beforeCreate()创建前阶段,这个时候还不能使用data中的数据。 created()创建完成 最早可以使用data中的数据
- 挂载阶段 beforeMount:在挂载开始之前被调用: 相关的 render 函数首次被调用 mounted: 挂载完成,DOM节点挂载到实例上去之后调用该钩子
- 更新阶段 beforeUpdate:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。 updated: 数据更新完成并且DOM更新完成后调用销
- 销毁阶段 beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用 destroved:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定
vue3.0
- 去掉了vue2.0中的 beforeCreate 和 created 两个阶段,同样的新增了一个 setup
- beforeMount 挂载之前 改名 onBeforeMount
- mounted 挂载之后 改名 onMounted
- beforeUpdate 数据更新之前 改名 onBeforeUpdate
- updated 数据更新之后 改名 onUpdated
- beforeDestroy 销毁前 改名 onBeforeUnmount,可在此进行清除定时器,进行事件监听,发布订阅。
- destoryed 销毁后 改名 onUnmounted
- errorCaptured 报错 改名 onErrorCaptured
总结
不难发现3.0中钩子函数最大的变化就是setup代替了2.0中的beforeCreate和created,而对应其它几个函数来说都是名称上发生了一些变化,其功能基本上还是一样的。
10-vue路由跳转传参的方式有哪些
在template中可以使用标签实现跳转。
- router-link 标签直接拼接参数实现跳转
js
<router-link to="/t?index=1">
<button class="btn btn-default">点击跳转</button>
</router-link>
- router-link 标签使用to属性结合query实现跳转,里面是参数字典
js
<router-link :to="{path:'/t',query: {index: 1}}">
<button class="btn btn-default">点击跳转</button>
</router-link>
- this.$router.push() (@click函数里面调用)
js
this.$router.push({path: '/路径?参数=1'})
接收参数
this.$route.query.index
- query和 params区别
- query类似 get, 跳转之后页面 url后面会拼接参数,类似?id=1, 非重要性的可以这样传,密码之类还是用params刷新页面id还在
- params类似 post, 跳转之后页面 url后面不会拼接参数 , 但是刷新页面id 会消失
- VUE几种路由跳转几种方式的区别
- this.$router.push:跳转到指定url路径,并想history栈中添加一个记录,点击后退会返回到上一个页面
- this.$router.replace:跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)
- this.$router.go(n):向前或者向后跳转n个页面,n可为正整数或负整数
11-刷新浏览器后,Vuex的数据是否存在?如何解决
vuex的数据在刷新浏览器后不会保存,
这是因为vuex实例store里的数据保存在运行内存中,页面刷新会重载vue实例,导致store里的数据丢失
解决方法:
- 下载持久化存储插件,例如:vuex-along,vuex-persist、vuex-persistedstate
- 使用localStorage 或者 sessionStroage