1. Vue组件之间通信方式有哪些

vue组件化开发框架,所以对于vue应用来说组件间的数据通信非常重要。 此题主要考查大家vue基本功,对于vue基础api运用熟练度。 另外一些边界知识如provide/inject/$attrs则提现了面试者的知识广度。


组件传参的各种方式 Vue经典面试题(一) - 图1

思路分析:

  1. 总述知道的所有方式
  2. 按组件关系阐述使用场景

回答范例:

  1. 组件通信常用方式有以下8种:
  • props
  • $emit/$on
  • $children/$parent
  • $attrs/$listeners
  • ref
  • $root
  • eventbus
  • vuex
  1. vue3中废弃了一些
  • $children
  • $on
  • $listener

注意vue3中废弃的几个API:

  • 父子组件:
    • props/$emit/$parent/ref/$attrs
  • 兄弟组件
    • $parent/$root/eventbus/vuex
  • 跨层级关系
    • eventbus/vuex/provide+inject :::

      2. v-if和v-for哪个优先级更高?

      分析:

      此题考查常识,文档中曾有详细说明v2|v3;也是一个很好的实践题目,项目中经常会遇到,能够看出面试者api熟悉程度和应用能力。

      思路分析:

  1. 先给出结论
  2. 为什么是这样的,说出细节
  3. 哪些场景可能导致我们这样做,该怎么处理
  4. 总结,拔高

    回答范例:

  5. 实践中不应该把v-for和v-if放一起

  6. vue2中,v-for的优先级是高于v-if,把它们放在一起,输出的渲染函数中可以看出会先执行循环再判断条件,哪怕我们只渲染列表中一小部分元素,也得在每次重渲染的时候遍历整个列表,这会比较浪费;另外需要注意的是在vue3中则完全相反,v-if的优先级高于v-for,所以v-if执行时,它调用的变量还不存在,就会导致异常.
  7. 通常有两种情况下导致我们这样做:
    • 为了过滤列表中的项目 (比如 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即可。
  8. 文档中明确指出永远不要把 v-if 和 v-for 同时用在同一个元素上,显然这是一个重要的注意事项。
  9. 源码里面关于代码生成的部分,能够清晰的看到是先处理v-if还是v-for,顺序上vue2和vue3正好相反,因此产生了一些症状的不同,但是不管怎样都是不能把它们写在一起的。

    知其所以然:

    做个测试,test.html 两者同级时,渲染函数如下:

    1. ƒ anonymous() {
    2. with(this){
    3. return _c(
    4. 'div',
    5. {attrs:{"id":"app"}},
    6. _l((items),
    7. function(item){
    8. return (item.isActive)?_c('div',{key:item.id},[_v("\n "+_s(item.name)+"\n ")]):_e()}),0)}
    9. }

    通过源码可以看出vue3是先通过三目运算执行v-if, 然后在执行v-for.
    做个测试,test-v3.html
    Vue经典面试题(一) - 图2
    源码中找答案:
    v2:github1s.com/vuejs/vue/b…
    v3:github1s.com/vuejs/core/…

    3. 简述 Vue 的生命周期以及每个阶段做的事

    必问题目,考查vue基础知识。

    思路

  10. 给出概念

  11. 列举生命周期各阶段
  12. 阐述整体流程

结合实践

  1. 扩展:vue3变化

    回答范例

    1.每个Vue组件实例被创建后都会经过一系列初始化步骤,比如,它需要数据观测,模板编译,挂载实例到dom上,以及数据变化时更新dom。这个过程中会运行叫做生命周期钩子的函数,以便用户在特定阶段有机会添加他们自己的代码。
    2.Vue生命周期总共可以分为8个阶段:创建前后, 载入前后, 更新前后, 销毁前后,以及一些特殊场景的生命周期。vue3中新增了三个用于调试和服务端渲染场景。
生命周期v2 生命周期v3 描述
beforeCreate beforeCreate 组件实例被创建之初
created created 组件实例已经完全创建
beforeMount beforeMount 组件挂载之前
mounted mounted 组件挂载到实例上去之后
beforeUpdate beforeUpdate 组件数据发生变化,更新之前
updated updated 数据数据更新之后
beforeDestroy beforeUnmount 组件实例销毁之前
destroyed unmounted 组件实例销毁之后
生命周期v2 生命周期v3 描述
activated activated keep-alive 缓存的组件激活时
deactivated deactivated keep-alive 缓存的组件停用时调用
errorCaptured errorCaptured 捕获一个来自子孙组件的错误时被调用
- renderTracked 调试钩子,响应式依赖被收集时调用
- renderTriggered 调试钩子,响应式依赖被触发时调用
- serverPrefetch ssr only,组件实例在服务器上被渲染前调用

3.Vue生命周期流程图
Vue经典面试题(一) - 图3
4.结合实践: :::success beforeCreate:通常用于插件开发中执行一些初始化任务
created:组件初始化完毕,可以访问各种数据,获取接口数据等
mounted:dom已创建,可用于获取访问数据和dom元素;访问子组件等。
beforeUpdate:此时view层还未更新,可用于获取更新前各种状态
updated:完成view层的更新,更新后,所有状态已是最新
beforeunmount:实例被销毁前调用,可用于清除定时器或取消订阅
unmounted:销毁一个实例。可清理它与其它实例的连接,解绑它的全部指令及事件监听器 :::

可能的追问

  1. setup和created谁先执行?
    1. setup先执行。
  2. setup中为什么没有beforeCreate和created?

    1. setup是beforeCreated和created的集合。

      知其所以然

      vue3中生命周期的派发时刻:
      github1s.com/vuejs/core/…
      vue2中声明周期的派发时刻:
      github1s.com/vuejs/vue/b…

      4. 能说一说双向绑定使用和原理吗?

      题目分析:

      双向绑定是vue的特色之一,开发中必然会用到的知识点,然而此题还问了实现原理,升级为深度考查。

      思路分析:

  3. 给出双绑定义

  4. 双绑带来的好处
  5. 在哪使用双绑
  6. 使用方式、使用细节、vue3变化
  7. 原理实现描述

    回答范例:

  8. vue中双向绑定是一个指令v-model可以绑定一个响应式数据到视图,同时视图中变化能改变该值

  9. v-model是语法糖,默认情况下相当于:value和@input。使用v-model可以减少大量繁琐的事件处理代码,提高开发效率。
  10. 通常在表单项上使用v-model,还可以在自定义组件上使用,表示某个值的输入和输出控制。
  11. 通过的方式将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,非常强大!
  12. v-model是一个指令,它的神奇魔法实际上是vue的编译器完成的。我做过测试,包含v-model的模板,转换为渲染函数之后,实际上还是value属性的绑定以及input事件监听,事件回调函数中会做相应变量更新操作。编译器根据表单元素的不同会展开不同的DOM属性和事件对,比如text类型的inputtextarea会展开为value和input事件;checkbox和radio类型的input会展开为checked和change事件;select用value作为属性,用change作为事件。

    可能的追问:

  13. v-model和sync修饰符有什么区别? :::success

  • 相同点: 都是语法糖, 都可以实现父子组件中的数据双向通信。
  • 不同点:
    • 语法不同:
      • 格式不同 v-model=“num” :num.sync = “num”
      • v-model:@input + :value = “num”
      • :num.sync: @update:a + :a = “num”
      • v-model只能用一次, .sync可以有多个
    • 使用场景不同:
      • v-model针对更多的是最终操作结果, 是双向绑定的结果, 是value, 是一种change操作。比如:输入款的值、多选框的value值列表等等…
      • .sync针对的更多的是各种各样的状态, 是状态的相互传递, 是status是一种update操作。比如组件loading状态、模态框的显示状态…. ::: ```javascript //父组件

//子组件

  1. ```javascript
  2. //父组件
  3. <template>
  4. <div class="parent">
  5. <my-child2 :num1.sync="num" :num2.sync="num2"></my-child2>
  6. </div>
  7. </template>
  8. <script>
  9. import MyChild2 from './MyChild2.vue'
  10. export default {
  11. components: {
  12. MyChild2,
  13. },
  14. data() {
  15. return {
  16. num: 100,
  17. num2: 99,
  18. }
  19. }
  20. }
  21. </script>
  22. //子组件
  23. <template>
  24. <div class="child2">
  25. <h2>我是子元素2222222222222</h2>
  26. <h1>{{num1}}</h1>
  27. <h1>{{num2}}</h1>
  28. </div>
  29. </template>
  30. <script>
  31. export default {
  32. props: {
  33. num1: {
  34. type: Number,
  35. required: true,
  36. },
  37. num2: {
  38. type: Number,
  39. required: true,
  40. },
  41. },
  42. methods: {
  43. changeNum() {
  44. this.$emit('update:num2', 0) //把传过来的值变为0
  45. this.$emit('update:num1', this.num1 * 100) //传过来的值 * 100
  46. },
  47. }
  48. }
  49. </script>

.sync修饰符可以实现子组件与父组件的双向绑定,并且可以实现子组件同步修改父组件的值。

  1. 自定义组件使用v-model如果想要改变事件名或者属性名应该怎么做?

允许一个自定义组件在使用v-model时定制propevent.默认情况下, 一个组件上的v-model会把value用作prop且把input用作event。但是一些输入类型比如单选框和复选框按钮可能想使用value prop来达到不同的目的, 使用model选项可以回避这些情况产生的冲突。

  1. //父组件
  2. <template>
  3. <div class="parent">
  4. <h2>我是父组件</h2>
  5. <my-child1 v-model="num"></my-child1>
  6. </div>
  7. </template>
  8. <script>
  9. import MyChild1 from './MyChild1.vue'
  10. export default {
  11. components: {
  12. MyChild1
  13. },
  14. data() {
  15. return {
  16. num: 100
  17. }
  18. }
  19. }
  20. </script>
  21. //子组件
  22. <template>
  23. <div class="child">
  24. <h2>我是子元素11111111111111</h2>
  25. <h1>当前的值:{{currNum}}</h1>
  26. <button @click="changeValue">修改value值</button>
  27. </div>
  28. </template>
  29. <script>
  30. export default {
  31. model: {
  32. prop: 'currNum', //默认是value
  33. event: 'change', // 默认是input
  34. },
  35. props: {
  36. currNum: {
  37. type: Number,
  38. }
  39. },
  40. methods: {
  41. changeValue() {
  42. this.$emit('change', this.currNum+10)
  43. }
  44. },
  45. }
  46. </script>

知其所以然:

测试代码,test.html

5. Vue中如何扩展一个组件

此题属于实践题,考察大家对vue常用api使用熟练度,答题时不仅要列出这些解决方案,同时最好说出他们异同。

答题思路:

  1. 按照逻辑扩展和内容扩展来列举,
    • 逻辑扩展有:mixinsextendscomposition api
    • 内容扩展有slots
  2. 分别说出他们使用方法、场景差异和问题。
  3. 作为扩展,还可以说说vue3中新引入的composition api (Hooks)带来的变化

    回答范例:

  4. 常见的组件扩展方法有:mixinsslotsextends

  5. 混入mixins是分发 Vue 组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。

混入mixins的缺点:混入的数据和方法不能明确判断来源且可能和当前组件内变量产生命名冲突,

  1. // 复用代码:它是一个配置对象,选项和组件里面一样
  2. const mymixin = {
  3. methods: {
  4. dosomething(){}
  5. }
  6. }
  7. // 全局混入:将混入对象传入
  8. Vue.mixin(mymixin)
  9. // 局部混入:做数组项设置到mixins选项,仅作用于当前组件
  10. const Comp = {
  11. mixins: [mymixin]
  12. }
  1. 插槽主要用于vue组件中的内容分发,也可以用于组件扩展。插槽主要包含:内容插槽具名插槽作用域插槽。Slot 通俗的理解就是“占坑”,在组件模板中占好了位置,当使用该组件标签时候,组件标签里面的内容就会自动填坑(替换组件模板中slot位置)
  • 内容插槽:可以包含文本、板代码,包括HTML。

    1. //父组件
    2. <template>
    3. <div class="parent">
    4. <my-child1 v-model="num">
    5. <p style="color:pink;font-size:20px">我是内容插槽</p>
    6. </my-child1>
    7. </div>
    8. </template>
    9. //子组件
    10. <template>
    11. <div class="child">
    12. <h2>我是子元素11111111111111</h2>
    13. <h1>当前的值:{{currNum}}</h1>
    14. <button @click="changeValue">修改value值</button>
    15. <slot></slot>
    16. </div>
    17. </template>
  • 具名插槽: 如果一个组件需要多个插槽的时候, 就需要具名插槽。如果一个不带name属性的话,那么它的name默认为default。在向具名插槽提供内容的时候,我们可以在