Skip to content

vue面试题

v-show和v-if的区别

1、v-if 删除节点,适用于接口后端判断是否显示。

2、v-show 通过display控制是否显示,适用于多次切换的场景。

v-for和v-if能否同时使用

1、不应同时使用,vue2里 v-for优先级比v-if大,每一次都要遍历整个数组,影响速度。

2、vue3里v-if优先级大于v-for,v-if运行时,变量可能还不存在,会导致报错

解决方法

通过计算属性过滤一下

v-if不依赖v-for的前提下,我们可以把v-if放在v-for的外层

vue3组件传值的几种方式

1、父组件provide,子组件inject

2、父组件v-model也就是 :title,子组件defineProps

3、子组件defineEmits方法然后emits(add, value.value),父组件@add

4、pinia和vuex状态管理

hash 模式和 history 模式

1、首先是在 URL 的展示上,hash 模式有“#”,history 模式没有

2、刷新页面时,hash 模式可以正常加载到 hash 值对应的页面,而 history 没有处理的话,会返回404,一般需要后端将所有页面都配置重定向到首页路由

3、在兼容性上,hash 可以支持低版本浏览器和 IE

vue的性能优化体现在哪些方面

一、代码层面的优化

1、v-if 和 v-show 区分使用场景

2、computed 和 watch 区分场景使用

3、v-for 遍历必须加 key,key最好是id值,且避免同时使用 v-if

4、图片懒加载,图片懒加载

5、适当采用 keep-alive 缓存组件

6、防抖、节流的运用

computed 和 watch 的区别和运用的场景

1、computed 是计算属性,依赖其它属性计算值,并且 computed 的值有缓存,只有当计算值变化才会返回内容,他可以设置getter和setter。

2、watch 监听到值的变化就会执行回调,在回调中可以进行一系列的操作

for in 和 for of的区别

1、for in 和 for of 都可以循环数组,for in 输出的是数组的index下标,而for of 输出的是数组的每一项的值

2、for in 可以遍历对象,for of 不能遍历对象,只能遍历带有iterator接口的,例如Set,Map,String,Array,arguments对象,Nodelist对象,类数组

3、总结:for in适合遍历对象,for of适合遍历数组。for in遍历的是数组的索引,对象的属性,以及原型链上的属性

vue
<script setup lang="ts">
//遍历对象
var obj = {name: 'saucxs',age: 21,sex: 1};
for(key in obj){
    console.log(key); //输出name age sex
}
for(key of obj){
    console.log(key); // 输出obj is not iterable报错 。说明obj对象没有iterable属性报错,使用不了for of
}

//遍历数组
var array = ['a','b','c'];
for(var key in array){
    console.log(key); //输出0 1 2
}
for(var key of array){
    console.log(key); //输出a b c
}

//如果非常想让对象用for of,可以使用Object.keys()获取对象的key值集合,再用for of
var obj = {name: 'saucxs',age: 18, sex: 1};
for(var key of Object.keys(obj)){
    console.log(key, obj[key]);
    // name saucxs
    // age 21
    // sex 1
}
</script>

组件中name选项有什么作用

1、项目使用 keep-alive 时,可搭配组件 name 进行缓存过滤

2、DOM 做递归组件时需要调用自身 name

3、vue-devtools 调试工具里显示的组见名称是由vue中组件name决定的

Vue 中 key 值的作用

当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。key 的作用主要是为了高效的更新虚拟DOM。

route 和 router 的区别是什么?

1、route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。

2、router是“路由实例对象”,包括了路由的跳转方法(push、replace),钩子函数等。

nextTick() 的使用场景

1、nextTick() 最常见的用途。例如,你可能更改了一个数据属性,该属性控制一个元素的可见性。然后你可能想要等待DOM更新以便可以获取该元素的新的宽度或高度。在这种情况下,你可以使用 nextTick() 来确保你的代码在DOM更新后执行

vue
<template>  
    <div>
        <div ref="myDiv">{{ message }} </div>  
        <div @click="updateMessage">修改</div>
    </div> 
</template>  

<script setup lang="ts">
const message = ref('Hello Vue!');
const myDiv = ref(null);

const updateMessage = () =>{
  message.value = 'Updated!';
  nextTick(() => {
    console.log(myDiv.value.textContent); // 输出: Updated!
  });
}

</script>

2、当创建或销毁Vue组件时,Vue需要时间来更新DOM。如果你需要立即访问新创建或已销毁的DOM元素,你可能会遇到问题,因为DOM可能还没有更新。在这种情况下,你可以使用 nextTick() 来确保你的代码在DOM更新后执行

vue

<template>  
  <div>  
    <button @click="toggleComponent">Toggle Component</button>  
    <ChildComponent v-if="showChild" ref="childComponentRef" />  
  </div>  
</template>

<script setup lang="ts">
    const showChild = ref(false);  
    const childComponentRef = ref(null);  
    async function toggleComponent() {  
      showChild.value = !showChild.value;  
      // 等待DOM更新  
      await nextTick();  
      if (showChild.value) {  
        // 现在ChildComponent已经被创建并挂载到DOM上  
        console.log('Child component has been mounted:', childComponentRef.value);  
      } else {  
        // 现在ChildComponent已经被销毁并从DOM上移除  
        console.log('Child component has been unmounted.');  
      }  
    }
</script>

3、在处理大量数据时,Vue.js 可能会变得有点慢,因为它需要时间来处理所有的数据并更新DOM。在这种情况下,你可能想要使用 nextTick() 来分批处理数据,以便给浏览器一些时间来更新DOM。这可以提高应用程序的性能,并减少用户在处理大量数据时的等待时间。

vue
<template>  
  <div>  
    <div v-for="(item, index) in items" :key="index">{{ item }}</div>  
  </div>  
</template>

<script setup lang="ts">
    const items = ref([]);  
    function fetchData() {  
      // 假设 fetchDataFromAPI 是一个异步函数,用于从 API 获取数据  
      fetchDataFromAPI().then(newItems => {  
        items.value = newItems;  
        nextTick(() => {  
          console.log('All items are rendered');  
          // 这里可以执行依赖于所有项都已渲染的代码  
        });  
      });  
    } 
</script>

4、与第三方库集成,确保库在正确的DOM状态下执行

宏任务和微任务的理解

1、执行顺序。同步任务 > 微任务 > 宏任务

宏任务:setTimeout、setInterval、setImmediate、i/o操作、异步的ajax。

(执行顺序:setImmediate --> setTimeout --> setInterval --> i/o操作 --> 异步ajax)

微任务:Promise(then、catch、finally)、async/await、process.nextTick、Object.observe、 MutationObserver。

(执行顺序:process.nextTick --> Promise)

vue
<script setup lang="ts">
    setTimeout(function () {
        console.log('1');
    })
    new Promise(function (resolve) {
        console.log('2');
        resolve();
    }).then(function () {
        console.log('3');
    })
    console.log('4');
    //打印顺序 2 4 3 1

    // 分析:
    // 1、遇到setTimeout加入宏任务列表。
    // 2、new Promise 在实例化过程中所执⾏的代码都是同步执⾏。 输出2
    // 3、Promise的回调函数then是微任务,加入微任务列表。
    // 4、同步任务。输出4
    // 原则:同步任务 > 微任务 > 宏任务所以输出 2 4 3 1

</script>
vue
<script setup lang="ts">
    console.log(1);
    setTimeout(function () {
        console.log(2);
        let promise = new Promise(function (resolve, reject) {
            console.log(3);
            resolve();
        }).then(function () {
            console.log(4);
        });
    }, 1000);
    setTimeout(function () {
        console.log(5);
        let promise = new Promise(function (resolve, reject) {
            console.log(6);
            resolve();
        }).then(function () {
            console.log(7);
        });
    }, 0);
    let promise = new Promise(function (resolve, reject) {
        console.log(8);
        resolve()
    }).then(function () {
        console.log(9);
    }).then(function () {
        console.log(10)
    });
    console.log(11);

    //打印顺序 1 8 11 9 10 5 6 7 2 3 4
    // 1、同步任务 console.log(1)。输出 1 
    // 2、setTimeout宏任务
    // 3、new Promise 在实例化过程中所执⾏的代码都是同步执⾏。 输出8
    // 4、同步任务 console.log(11)。输出 11 
    // 5、Promise的回调函数then是微任务,加入微任务列表。输出9,10
    // 6、接下来是宏任务 时间为0的setTimeout。输出同步任务 5 6
    // 7、接着是微任务7。输出7。之后再是宏任务 时间为1000的setTimeout.输出2 3 4
</script>
vue
<script setup lang="ts">
  console.log('begin');
  Promise.resolve().then(()=>{
    console.log('promise');
  })
  process.nextTick(()=>{
    console.log('nextTick');
  });
  console.log('end');
  //执行顺序:begin end nextTick promise
</script>

vue3生命周期执行顺序

1、setup() 开始创建组件之前 创建的是data和method

2、onBeforeMount() 组件挂载到节点上之前执行的函数

onBeforeMount使用场景

可以用来在组件挂载前执行一些初始化操作

3、onMounted() 组件挂载完成之后执行的函数

onMounted使用场景

通常用于获取数据和初始化页面状态等操作

4、onBeforeUpdate() 组件更新之前执行的函数(数据修改前)

onBeforeUpdate使用场景

可以用来在组件更新前执行一些操作

5、onUpdated() 组件更新之后执行的函数(数据修改后)

onUpdated使用场景

通常用于更新 DOM、执行动画或获取最新的状态等操作

6、onBeforeUnmount() 组件卸载之前执行的函数(销毁前)

onBeforeUnmount使用场景

可以在这里清理定时器、移除事件监听器等

7、onUnmounted() 组件卸载之后执行的函数(销毁后)

onUnmounted使用场景

通常用于清理一些资源或取消订阅

8、onActivated() 被包含在 keep-alive 中的组件,会多出两个生命周期钩子函数,被激活时执行

9、onDeactivated() 比如从A组件切换到B组件,A组件消失时执行

10、onErrorCaptured() 当捕获一个来自子孙组件的异常时激活钩子函数。

11、onRenderTracked() 检查依赖被追踪。当render函数被调用时,会检查哪个响应式数据被收集依赖。

12、onRenderTriggered() 当执行update操作时,会检查哪个响应式数据导致组件重新渲染。

vue3 reactive的局限性

经常遇到reactive定义的数据失去响应式。解决方案:

1、再封装一层数据

vue
<script setup lang="ts">
  import { reactive, ref } from "vue";
  // 定义响应式 
  let choosenList = reactive({list:[]});
  // 请求的数据
  let newList1 = [
    { name: "Eula", age: "18", isActive: false },
    { name: "Umbra", age: "17", isActive: false },
  ]
  // 更改数据
  const setList = () => {
    choosenList.list= newList1
  }
</script>

2、使用数组的splice来直接更改原数组,还是用reactive来定义响应式数据,只不过改数据的方式变了,使用数组的原生方法splice()来更改原数组,不是直接覆盖所以并不会影响响应式,可改变原数组的原生方法还有push、unshift、pop、shift、reverse、sort、splice、fill

vue
<script setup lang="ts">
  import { reactive, ref } from "vue";
  // 定义响应式 
  let list1 = reactive([]);

  // 请求的数据
  let newList1 = [
    { name: "Eula", age: "18", isActive: false },
    { name: "Umbra", age: "17", isActive: false },
  ]

  // 更改数据
  const setList1 = () => {
    // splice三个参数时 第一项是起始索引  第二项是长度  第三项是新插入的元素,可以有多个
    list1.splice(0,list1.length,...newList1)
  }
</script>

3、使用 ref 来定义数据

vue3 defineProps和withDefaults

在 Vue 3 中,withDefaults 是一个用于 defineProps 和 defineEmits 的辅助函数,主要用于为组件的 props 设置默认值。这个功能是 Vue 3 的一个新增特性,帮助简化组件的 Props 配置和默认值设置。

1、defineProps:用于定义组件的 props 类型。

2、withDefaults:用于为 defineProps 定义的 props 设置默认值。

withDefaults 的优势

类型安全:如果你使用 TypeScript,这种方式能确保默认值和 props 类型一致,提供更好的类型检查。

简化配置:使得默认值的配置更加集中和简洁。

注意事项 withDefaults 只在 setup 语法糖中可用,不能在传统的选项式 API 中使用。 默认值只会在 props 没有被父组件传递时使用。

ES6的Proxy

1、作用:代理器

2、应用场景:vue3的响应式数据采用的就是Proxy对象,vue2则是通过defineproperty进行数据代理。两者作用一致,Proxy相当于是defineProperty在数据代理上的优化,属于ES6语法。

3、前提:必须为Object类型(即对象类型),Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写

vue3 toRaw()

作用:将响应式对象转为原始对象。做一些不想被监听的事情,从 ref 或 reactive 得到原始数据。

1、修改原响应式数据时,toRaw 转换得到的数据会被修改,视图也会更新。

vue
<script lang="ts" setup>
    import { ref, isRef, toRef, toRefs, reactive, toRaw } from 'vue'
    let obj = reactive({
      name: '姓名',
      age: 18,
    })
    let newObj = toRaw(obj)
    const chang = () => {
      obj.name = '钻石王老五'
      obj.age++
    }
</script>
<template>
  <div>
    {{ obj.name }} ------- {{ obj.age }}
    <button type="button" @click="chang">修改值</button>
    <br />
    {{ newObj }}
  </div>
</template>

2.如果修改 toRaw 得到的原始数据,原数据也会被修改,但是视图不更新。

vue
<script lang="ts" setup>
    import { ref, isRef, toRef, toRefs, reactive, toRaw } from 'vue'
    let obj = reactive({
      name: '姓名',
      age: 18,
    })
    let newObj = toRaw(obj)
    const chang = () => {
      obj.name = '钻石王老五'
      obj.age++
    }
    const changNew = () => {
      newObj.name = '搞笑'
      console.log('newObj', newObj)
      console.log('obj', obj)
    }
</script>
<template>
  <div>
    {{ obj.name }} ------- {{ obj.age }}
    <button type="button" @click="chang">修改值</button>
    <br />
    {{ newObj }}
    <button @click="changNew">修改</button>
  </div>
</template>

vue3 toRef()

创建一个 ref 对象,其 value 值指向另一个对象中的某个属性。 toRef(obj, key) 将对象中的某个值转化为响应式数据,分为两种情况: 1、toRef 定义原始非响应式数据,修改值时,原始数据和 copy 数据都会变的,但是视图不更新。 注意:如果是在 DOM 挂载之前调用 chang 方法,改变数值,此时数据和视图都会发生改变。

vue
<script lang="ts" setup>
  import { ref, isRef, toRef, reactive } from 'vue'
  let obj = {
    name: '姓名',
    age: 18,
  }
  let name: string = toRef(obj, 'name')
  const chang = () => {
    obj.name = '钻石王老五'
    name.value = '李四'
    console.log(obj.name) // 李四
    console.log('name', name) // 李四
  }
  //chang() //DOM挂载前调用
</script>
<template>
  <div>
    {{ obj.name }} ------- {{ name }}
    <button type="button" @click="chang">修改值</button>
  </div>
</template>

2、toRef 定义原始数据响应式数据,修改值时,原始数据,和 copy 数据都会改变,视图也会更新。

vue
<script lang="ts" setup>
  import { ref, isRef, toRef, reactive } from 'vue'
  let obj = reactive({
    name: '姓名',
    age: 18,
  })
  let name: string = toRef(obj, 'name')
  const chang = () => {
    obj.name = '钻石王老五'
    name.value = '李四'
  }
</script>
<template>
  <div>
    {{ obj.name }} ------- {{ name }}
    <button type="button" @click="chang">修改值</button>
  </div>
</template>

vue3 toRefs()

1、toRefs 用来解构 ref、reactive 包裹的响应式数据。接收一个对象作为参数,遍历对象上的所有属性,将对象上的所有属性变成响应式数据。

vue
<script lang="ts" setup>
  let obj = reactive({
    name: '姓名',
    age: 18,
  })
  let { name, age } = toRefs(obj)
  const chang = () => {
    name.value = '钻石王老五'
    age.value++
  }
</script>
<template>
  <div>
    {{ name }} ------- {{ age }}
    <button type="button" @click="chang">修改值</button>
  </div>
</template>

2、toRefs 解构数据时,如果某些参数作为可选参数,可选参数不存在时就会报错,如:

vue
<script lang="ts" setup>
  let obj = reactive({
    name: '姓名',
    age: 18,
  })
  let { name, age, work } = toRefs(obj)
  const chang = () => {
    name.value = '钻石王老五'
    age.value++
    console.log('work', work.value)
    work.value = '程序媛'
  }
</script>

3、此时可以使用 toRef 解决此问题,使用 toRef 解构对象某个属性时,先检查对象上是否存在该属性,如果存在就继承对象上的属性值,如果不存在就会创建一个。例如:

vue
<script lang="ts" setup>
  let obj = reactive({
    name: '姓名',
    age: 18,
  })
  let { name, age } = toRefs(obj)
  let work = toRef(obj, 'work')
  const chang = () => {
    name.value = '钻石王老五'
    age.value++
    console.log('work', work.value)
    work.value = '程序媛'
  }
</script>

npm 和 pnpm的区别是什么

1、包的存储方式

npm:npm将每个包都下载到项目的node_modules目录中。这意味着,如果多个项目使用相同的包版本,它们会在各自的node_modules目录下存储独立的副本。

pnpm:pnpm使用一个全局的存储库(store)来存储所有已下载的包,并在每个项目中创建符号链接(或硬链接)指向这些包。这种方式避免了在多个项目中重复存储相同的依赖项,从而节省了磁盘空间。


2、空间占用

npm:由于每个项目都存储了独立的包副本,因此空间占用相对较大。

pnpm:通过共享存储库和符号链接的方式,pnpm显著减少了空间占用。


3、安装速度

npm:由于每个项目都需要下载并安装其所需的依赖项,安装速度可能会受到网络速度、包大小等因素的影响。

pnpm:由于pnpm使用全局存储库并通过符号链接引用包,因此安装速度通常更快。特别是在处理大量依赖项或需要频繁安装依赖项的项目中,pnpm的优势更为明显。


4、依赖解析和node_modules结构

npm:npm的依赖解析策略可能会导致依赖项被扁平化到顶层node_modules目录,这可能会引入版本冲突或意外的行为。

pnpm:pnpm采用严格的依赖解析策略,并创建非扁平化的node_modules结构。这减少了意外覆盖依赖的风险,并避免了由于包之间的版本冲突所导致的问题。


5、命令行接口(CLI)

npm 和 pnpm 的命令行接口非常相似,大多数常用命令(如install、run、test等)都是一致的。然而,在某些高级功能和命令上,它们之间可能存在差异。


6、兼容性

npm:作为最早和最广泛使用的包管理器,npm几乎被所有的Node.js项目所支持。

pnpm:虽然pnpm在许多项目中能够无缝工作,但在某些依赖于特定node_modules结构的工具或项目中可能会遇到兼容性问题。

Released under the MIT License.