联系我们
简单又实用的WordPress网站制作教学
当前位置:网站首页 > 程序开发学习 > 正文

2023年Vue3前端面试题考点

作者:小教学发布时间:2023-09-29分类:程序开发学习浏览:63


导读:参考:前端面试题——vue篇_Jet_closer_burning的博客-CSDN博客 合集:2023年最全前端面试题考点HTML5+CSS3+JS+Vue3+React...

参考:前端面试题 — — vue篇_Jet_closer_burning的博客-CSDN博客 

合集:2023年最全前端面试题考点HTML5+CSS3+JS+Vue3+React18+八股文+手写+项目+笔试_参宿7的博客-CSDN博客

 [保姆级] Vue3 开发文档 - 掘金

Vue.js - 渐进式 JavaScript 框架 | Vue.js

框架为一二面,面试官尤其喜欢问为什么要用+怎么用

一到三颗⭐代表重要性,⭐选择性了解,⭐⭐熟悉,⭐⭐⭐掌握

目录

掌握⭐⭐⭐

Vue2和3​​​​​​​

核心变化

双向数据绑定原理

Vue2 :数据劫持+发布订阅(Object.defineProperty,getter,setter(通知变化))

Vue3 :Proxy API对数据代理

性能:监听整个操作(不用遍历),不仅是读写

功能:可监听属性的增删

类型推导:TypeScript

API 类型

Vue2 :Options API选项类型(属性data、methods、computed)

Vue3 :Composition API合成型(函数定义组件逻辑,不同组件中可复用)

定义数据变量和方法

Vue2:数据放在data选项 中Vue3 :setup()方法

生命周期

Vue3

使用:先导入

合并:beforeCreate+created=setup

新增on:destroy->unmount

onErrorCaptured监听组件的统一报错:onErrorCaptured(error, component, details)

Fragment碎片组件:树

Vue2 不支持碎片Vue3 支持碎片,可以拥有多个根节点

非核心变化【但重要,可根据标题引导一波提问】

computed:有缓存(不必重算),依赖值发生变化触发

都支持函数和对象两种写法

vue2 函数

vue3 对象

watch:无缓存,单个监听值

vue2 属性(配置项:immediate,deep)

vue3 函数(监听的属性,(newVal, oldVal) => { ... },配置项(+flush,debug))

nextTick:DOM更新之后执行回调函数,返回一个Promise

vue2:作为vue的实例方法,this.$nextTick(()=>{})调用

vue3:独立的函数导入

this

vue2:this=组件实例($router,$ref)

vue3:{ proxy, appContext } = getCurrentInstance()

组件传值​​​​​​​

父子组件:子组件props来接受,$emit触发父组件的自定义事件;

兄弟组件:一个公共组件bus.js.。传递方通过事件触发bus.$emit。接收方通过在mounted(){}生命周期里触发bus.$on

Vuex跨组件

Vuex​​​​​​​

熟悉⭐⭐


掌握⭐⭐⭐

Vue2和3​​​​​​​


核心变化

双向数据绑定原理


Vue2 :数据劫持+发布订阅(Object.defineProperty,getter,setter(通知变化))

Object.defineProperty的get方法用来获取值 set方法用来拦截设置值

把 data 对象里每个数据的读写转化成 getter/setter,当数据变化时通知视图更新。

  var obj = {};  //定义一个空对象
    Object.defineProperty(obj, 'val', {//定义要修改对象的属性
        get: function () {
            console.log('获取对象的值')
        },
        set: function (newVal) { 
            console.log('设置对象的值:最新的值是'+newVal);
        }
    });
    obj.hello = 'hello world'
Vue3 :Proxy API对数据代理
性能:监听整个操作(不用遍历),不仅是读写

Proxy可以监听对象的整个操作,而不仅仅是属性的读写操作,这使得数据变化的检测更加高效。在Vue2中,需要递归地遍历对象的所有属性,而Proxy可以一次性监听整个对象的操作。

功能:可监听属性的增删

Proxy可以监听新增属性和删除属性的操作,而Object.defineProperty只能监听已有属性的读写操作

类型推导:TypeScript

由于Proxy是基于原生的Proxy对象实现的,所以可以更好地支持TypeScript等静态类型检查工具,提供更准确的类型推导和代码提示。

API 类型


Vue2 :Options API选项类型(属性datamethodscomputed

选项在组件实例化后会被合并到组件实例中,可以在组件内部访问和使用。

<template>
  <div>
    <p>{{ message }}</p>
    <button @click="changeMessage">Change Message</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello Vue!'
    };
  },
  methods: {
    changeMessage() {
      this.message = 'New Message';
    }
  }
};
</script>
Vue3 :Composition API合成型(函数定义组件逻辑,不同组件中可复用)

合成型API不再依赖于特定的选项,而是通过一组函数来定义组件的逻辑,这些函数可以在不同的组件中复用。

<template>
  <div>
    <p>{{ message }}</p>
    <button @click="changeMessage">Change Message</button>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    //ref函数来创建一个响应式的数据
    const message = ref('Hello Vue!');

    const changeMessage = () => {
      message.value = 'New Message';
    };

    return {
      message,
      changeMessage
    };
  }
};
</script>


定义数据变量和方法

Vue2:数据放在data选项 中
Vue3 :setup()方法

生命周期

Vue3
使用:先导入
// vue3
<script setup>     
import { onMounted } from 'vue';   // 使用前需引入生命周期钩子
 
onMounted(() => {
  // ...
});
 
// 可将不同的逻辑拆开成多个onMounted,依然按顺序执行,不会被覆盖
onMounted(() => {
  // ...
});
</script>
 
// vue2
<script>     
export default {         
  mounted() {   // 直接调用生命周期钩子            
    // ...         
  },           
}
</script> 
合并:beforeCreate+created=setup
新增on:destroy->unmount

onErrorCaptured监听组件的统一报错:onErrorCaptured(error, component, details)
<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello, Vue!'
    };
  },
  methods: {
    throwError() {
      // 人为地抛出一个错误
      throw new Error('An error occurred.');
    }
  },
  onErrorCaptured(error, component, details) {
    console.error('Captured an error in component:', component);
    console.error('Error details:', error);
    console.error('Details:', details);
    // 可以选择返回 false 来阻止错误继续传播
    // return false;
  }
};
</script>

Fragment碎片组件:树

本质上 Vue3 每个组件还是一个根节点,因为 DOM 树只能是树状结构的,只是 Vue3 在编译阶段新增了判断,如果当前组件不只一个根元素,就添加一个 fragment 组件把这个多根组件的给包起来,相当于这个组件还是只有一个根节点。而 fragment 跟 keep-alive 一样是一个不会被渲染出来的内置组件

Vue2 不支持碎片
Vue3 支持碎片,可以拥有多个根节点
// vue2中在template里存在多个根节点会报错
<template>
  <header></header>
  <main></main>
  <footer></footer>
</template>
 
// 只能存在一个根节点,需要用一个<div>来包裹着
<template>
  <div>
    <header></header>
    <main></main>
    <footer></footer>
  </div>
</template>

非核心变化【但重要,可根据标题引导一波提问】

computed:有缓存(不必重算),依赖值发生变化触发

都支持函数和对象两种写法
vue2 函数
vue3 对象

watch:无缓存,单个监听值

vue2 属性(配置项:immediate,deep)
watch: {
  // 监听 userId 的变化,触发 getData 方法
  userId: 'getData',
  
  // 监听 userName 的变化,触发 getData 方法
  userName(newName, oldName) {
    this.getData()
  },
  
  // 监听 userInfo 的变化,触发 getData 方法
  userInfo: {
    // 处理函数,在 userInfo 发生变化时触发
    handler(newVal, oldVal) {
      this.getData()
    },
    // 在组件创建时立即执行一次处理函数
    immediate: true,
    // 深度监听 userInfo 内部的属性变化
    deep: true
  }
}
vue3 函数(监听的属性,(newVal, oldVal) => { ... },配置项(+flush,debug))
<script setup>
import { watch, ref, reactive } from 'vue'

const name = ref('沐华')
const data = reactive({
    age: 18,
    money: 100000000000000000000,
    children: []
})

// 监听 ref 属性
watch(name, (newName, oldName) => { ... })

// 监听其他属性、路由或者状态管理的都这样
watch(
    () => data.age, 
    (newAge, oldAge) => { ... }
)

// 监听多个属性,数组放多个值,返回的新值和老值也是数组形式
watch([data.age, data.money], ([newAge, newMoney], [oldAge, oldMoney]) => { ... })

// 第三个参数是一个对象,为可配置项,有5个可配置属性
watch(data.children, (newList, oldList) => { ... }, {
    // 这两个和 Vue2 一样,没啥说的
    immediate: true,
    deep: true,
    // 回调函数的执行时机,默认在组件更新之前调用。更新后调用改成post
    flush: 'pre', // 默认值是 pre,可改成 post 或 sync
    // 下面两个是调试用的
    onTrack (e) { debugger }
    onTrigger (e) { debugger }
})
</script>

nextTick:DOM更新之后执行回调函数,返回一个Promise

vue2:作为vue的实例方法,this.$nextTick(()=>{})调用
// Vue2中的nextTick使用
this.$nextTick(() => {
  // 在DOM更新后执行的操作
  const element = document.getElementById('myElement')
  console.log(element.getAttribute('data-value'))
})
vue3:独立的函数导入
<script setup>
import { nextTick} from 'vue'

// 方式 一
const handleClick = async () => {
  await nextTick()
  console.log('')
}

// 方式二
nextTick(() => {
    console.log('')
})

// 方式三
nextTick().then(() => {
    console.log('')
  })
</script>

this

vue2:this=组件实例($router,$ref)
vue3:{ proxy, appContext } = getCurrentInstance()
<script setup>
import { getCurrentInstance } from 'vue'

// proxy 就是当前组件实例,可以理解为组件级别的 this,没有全局的、路由、状态管理之类的
const { proxy, appContext } = getCurrentInstance()

// 这个 global 就是全局实例
const global = appContext.config.globalProperties
</script>

组件传值​​​​​​​

父子组件:子组件props来接受,$emit触发父组件的自定义事件;

兄弟组件:一个公共组件bus.js.。传递方通过事件触发bus.$emit。接收方通过在mounted(){}生命周期里触发bus.$on

Vuex跨组件

Vue父子组件通信的缺点

  1. 逐层传递 props: 在多层次的组件嵌套中,如果需要在更深层的子组件中访问某些数据,就需要通过逐层传递 props,这会导致组件之间的耦合增加,并且在层级较深的情况下,维护起来可能会变得复杂。

  2. Prop drilling(属性钻取): 如果父组件需要传递给孙子组件数据,但这些数据对于中间的子组件来说并不需要,那么这些数据会在组件层级之间进行无意义的传递,导致代码冗余和性能损耗。

  3. 难以追踪数据流: 在复杂的组件结构中,跟踪数据的传递和变化可能变得困难,特别是在大型应用中。这会增加调试和维护的难度。

  4. 无法直接修改 props: 从父组件传递给子组件的 prop 是只读的,子组件无法直接修改这些数据。如果需要在子组件中修改这些数据,需要通过触发事件或者使用 Vuex(状态管理模式)等方式来实现。

  5. 耦合性增加: 父子组件之间的通信可能导致它们之间紧密耦合,使得组件难以在其他上下文中重用,因为它们依赖于特定的数据传递方式。

为了解决这些问题,Vue 提供了其他更高级的组件通信机制,如:

  • Vuex(状态管理): 适用于管理大规模应用的共享状态,使得组件之间的数据共享和响应变得更加简单和可控。

  • 事件总线: 可以通过 Vue 实例作为中央事件总线来实现组件之间的通信,尽管这也可能带来一些全局状态的问题。

  • Provide / Inject: 可以在父组件中使用 provide,然后在子组件中使用 inject 来实现跨多层次的组件通信,但也会增加组件之间的依赖关系。

选择合适的通信方式取决于应用的规模、复杂度以及团队的偏好,但在设计时需要权衡各种因素来避免可能的问题。

Vuex​​​​​​​

熟悉⭐⭐

4.路由之间如何传参⭐⭐
通过router-link路由导航跳转传递
<router-link to=`/a/${id}`>routerlink传参</router-link>
1
跳转时使用push方法拼接携带参数。
  this.$router.push({
          path: `/getlist/${id}`,
        })
1
2
3
通过路由属性中的name来确定匹配的路由,通过params来传递参数。
this.$router.push({
          name: 'Getlist',
          params: {
            id: id
          }
        })
使用path来匹配路由,然后通过query来传递参数。
this.$router.push({
          path: '/getlist',
          query: {
            id: id
          }
        })
注意:query有点像ajax中的get请求,而params像post请求。

params在地址栏中不显示参数,刷新页面,参数丢失,
其余方法在地址栏中显示传递的参数,刷新页面,参数不丢失。

详情请看Vue-router之简单的路由传参三种方法

5.谈一谈VUEX⭐⭐
原理:Vuex是专门为vue.js应用程序设计的状态管理工具。
构成:

state:vuex的基本数据,用来存储变量,存放的数据是响应式的。
mutations:提交更改数据,同步更新状态。
actions:提交mutations,可异步操作。
getters:是store的计算属性。
modules:模块,每个模块里面有四个属性。
关于VUEX如何使用可以看VUE的传值问题
6.如何解决vuex页面刷新数据丢失问题?⭐⭐
原因:因为vuex里的数据是保存在运行内存中的,当页面刷新时,页面会重新加载vue实例,vuex里面的数据就会被清空。
解决方法:将vuex中的数据直接保存到浏览器缓存中。(一般是用sessionStorage)

9.Route和router的区别⭐
route:是路由信息对象,包括“path,parms,hash,name“等路由信息参数。
Router:是路由实例对象,包括了路由跳转方法,钩子函数等。
10.v-show和v-if的区别⭐
v-if:组件的销毁和重建,更适合带有权限的操作,切换开大。如果开始条件为false则什么都不做,只有为true才会编译。
v-show:css切换,隐藏显示更适合频繁切换。在任何情况下都会被编译,然后被缓存,而且dom元素会被保留。
11.vue中数据变了但是视图不跟新怎么解决?⭐
原因:

数组数据变动:使用某些方法操作数组,变动数据时,有些方法无法被vue监测。
Vue 不能检测到对象属性的添加或删除。
异步更新队列:数据第一次的获取到了,也渲染了,但是第二次之后数据只有在再一次渲染页面的时候更新,并不能实时更新。
12.vue中data为什么是函数而不是对象?⭐⭐
官网中有这么一段介绍,详情可以看组件的复用

意思就是,在Vue中组件是可以被复用的,而当data是一个函数的时候,每一个实例的data都是独立的,不会相互影响了。

更详细的解释 ==>


13.vue中父子组件传值,父组件异步请求,子组件不能实时更新怎么解决?(vue中数据不能实时更新怎么解决?)⭐⭐⭐
首先了解父子组件生命周期执行顺序 ==>
加载渲染数据过程
父组件 beforeCreate -->
父组件 created -->
父组件 beforeMount -->
子组件 beforeCreate -->
子组件 created -->
子组件 beforeMount -->
子组件 mounted -->
父组件 mounted -->
原因:因为生命周期只会执行一次,数据是要等到异步请求以后才能拿到,那么子组件的mounted钩子执行的时候,还没有拿到父组件传递过来的数据,但是又必须要打印出来结果,那这样的话,就只能去打印props中的默认值空字符串了,所以打印的结果是一个空字符串。
解决办法:

使用v-if控制组件渲染的时机
初始还没拿到后端接口的异步数据的时候,不让组件渲染,等拿到的时候再去渲染组件。使用v-if="变量"去控制,初始让这个变量为false,这样的话,子组件就不会去渲染,等拿到数据的时候,再让这个变量变成true,
举例:
  data() {
    return {
      isTrue:false // 初始为false
    };
  },
  monted(){
  this.$post.a.b.c.getData(res=>{
        if(res.result){
            this.isTrue = true
         }
     })
  }

使用watch监听数据的变化
举例:
  props: {
    tableData: {
      type: Array,
      default: [],
    },
  },
  watch: {
     tableData(val){
         console.log(val)
     }
  },

使用VueX
14.父子组件传参emit如何传多个参数?⭐
子组件:

submit(){
    this.$emit('g',1,2,3,4,5)
}

父组件

g(val1,val2,val3,val4,val5) {
    console.log(val1,val2,val3,val4,val5)
}

15.Vue 路由跳转方式⭐⭐
router-link 标签跳转
this.$router.push()
this.$router.replace()
this.$router.go(n):(0:当前页,-1上一页,+1下一页,n代表整数)
16.条件渲染v-if 与 v-for 优先级⭐
vue2.0文档是这么说的

vue2列表渲染指南

vue3.0文档是这么说的

vue3条件渲染


18.Vue 中 for循环为什么加 key?⭐⭐
为了性能优化, 因为vue是虚拟DOM,更新DOM时用diff算法对节点进行一一比对,比如有很多li元素,要在某个位置插入一个li元素,但没有给li上加key,那么在进行运算的时候,就会将所有li元素重新渲染一遍,但是如果有key,那么它就会按照key一一比对li元素,只需要创建新的li元素,插入即可,不需要对其他元素进行修改和重新渲染。
key也不能是li元素的index,因为假设我们给数组前插入一个新元素,它的下标是0,那么和原来的第一个元素重复了,整个数组的key都发生了改变,这样就跟没有key的情况一样了。



20.为什么vue3不继续用$set?⭐
$set的作用:在vue2.0中:使用对象和数组来定义数据,当需要向对象或数组中新增一个属性或元素,并希望它在更新 View 时响应式地更新,就需要使用 $set方法来完成。
vue2是用object.definedProperty来实现数据响应的,无法监听深层数据的变化。

Vue3 中使用Proxy对数据代理通过ref和reactive将值和对象类型变为响应式对象,这样对它的修改和添加就能被vue捕获到,从而实现页面的自动刷新。

参考官网响应式基础

21.vue路由中的history和hash的区别⭐⭐
地址栏带不带"#"号
hash:http://localhost:8080/#/
history:http://localhost:8080/

都是利用浏览器的两种特性实现前端路由
history是利用浏览历史记录栈的API实现
hash是监听location对象hash值变化事件来实现

相同的url
history会触发添加到浏览器历史记录栈中,hash不会触发,
history需要后端配合,如果后端不配合刷新页面会出现404,hash不需要

hashRouter原理:通过window.onhashchange获取url中hash值
historyRouter原理:通过history.pushState,使用它做页面跳转不会触发页面刷新,使用window.onpopstate监听浏览器的前进和后退

详情可以看官网不同历史模式|Vue Router

 参考:

​​​​​​​

前端面试题(附答案)完善中……_Jet_closer_burning的博客-CSDN博客





程序开发学习排行
最近发表
网站分类
标签列表