《前后端面试题
》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。
文章目录
- 一、本文面试题目录
- 16. Vue3 中如何创建响应式对象?
- 17. Vue3 中如何监听数据变化?
- 18. Vue3 的生命周期钩子有哪些?与 Vue2 有何区别?
- 19. Vue3 中如何实现父子组件通信?
- 20. setup 函数的作用是什么?它有哪些参数?
- 21. Vue3 中如何实现计算属性(computed)?
- 22. Vue3 中如何实现跨层级组件通信(祖孙组件)?
- 23. Vue3 中如何使用插槽(Slot)?
- 24. Vue3 中如何使用 Teleport 组件将内容渲染到指定位置?
- 25. Vue3 中 Suspense 组件的作用是什么?如何使用?
- 26. Vue3 中 `<script setup>` 如何声明 props 和 emits?
- 27. Vue3 中如何动态绑定样式(class 和 style)?
- 28. Vue3 中如何注册全局组件?
- 29. Vue3 中如何实现自定义指令?
- 30. Vue3 中 `nextTick` 的作用是什么?如何使用?
- 二、120道面试题目录列表
一、本文面试题目录
16. Vue3 中如何创建响应式对象?
在 Vue3 中,创建响应式对象主要通过 reactive
和 ref
两个 API 实现:
- reactive:用于将对象或数组转换为响应式数据,基于 Proxy 实现,直接代理整个对象。示例:
import { reactive } from 'vue'; const user = reactive({ name: '张三', age: 25 }); // 修改属性时会触发响应式更新 user.age = 26;
- ref:主要用于基本数据类型(如字符串、数字等),也可用于对象。它会将数据包装为一个带有
value
属性的响应式对象,访问或修改时需通过.value
。示例:import { ref } from 'vue'; const count = ref(0); // 访问 console.log(count.value); // 0 // 修改 count.value++; // 触发更新
- 区别:
reactive
仅支持对象/数组,且访问属性无需额外操作;ref
支持所有数据类型,但需通过.value
访问(模板中使用时会自动解包,无需.value
)。
17. Vue3 中如何监听数据变化?
Vue3 提供 watch
和 watchEffect
两种方式监听数据变化:
- watch:需明确指定监听的数据源,支持监听单个或多个数据,可获取新旧值。示例:
import { ref, watch } from 'vue'; const count = ref(0); // 监听单个数据 watch(count, (newVal, oldVal) => { console.log(`count从${oldVal}变为${newVal}`); }); // 监听多个数据 watch([count, () => user.age], ([newCount, newAge], [oldCount, oldAge]) => { console.log('count或age变化了'); });
- watchEffect:自动收集依赖,无需指定数据源,回调函数中使用的响应式数据变化时会触发执行,且初始会执行一次。示例:
import { ref, watchEffect } from 'vue'; const count = ref(0); watchEffect(() => { console.log(`count当前值:${count.value}`); // 初始执行,后续count变化时再执行 });
- 适用场景:
watch
适合需要明确监听目标或需要新旧值对比的场景;watchEffect
适合依赖不明确、需要自动追踪的场景。
18. Vue3 的生命周期钩子有哪些?与 Vue2 有何区别?
Vue3 保留了 Vue2 的核心生命周期概念,但在组合式 API 中以函数形式提供,部分钩子名称略有调整:
- 常用钩子:
onBeforeMount
(挂载前)、onMounted
(挂载后)、onBeforeUpdate
(更新前)、onUpdated
(更新后)、onBeforeUnmount
(卸载前)、onUnmounted
(卸载后)、onErrorCaptured
(捕获子组件错误)。 - 与 Vue2 的区别:
- 写法不同:Vue3 需从
vue
中导入钩子函数,在setup
中使用;Vue2 是在组件选项中定义(如mounted() {}
)。 - 名称调整:Vue2 的
beforeDestroy
改为onBeforeUnmount
,destroyed
改为onUnmounted
。 - 新增钩子:
onRenderTracked
(追踪响应式依赖时触发,用于调试)、onRenderTriggered
(响应式依赖变化触发更新时触发,用于调试)。
- 写法不同:Vue3 需从
- 示例(组合式 API 中使用):
import { onMounted, onUnmounted } from 'vue'; setup() { onMounted(() => { console.log('组件已挂载'); }); onUnmounted(() => { console.log('组件已卸载'); }); }
19. Vue3 中如何实现父子组件通信?
父子组件通信主要通过 props 传值 和 事件触发 实现:
- 父传子(props):
- 父组件在使用子组件时,通过属性传递数据:
<ChildComponent :message="parentMsg" :count="10" />
- 子组件通过
defineProps
声明接收的 props(<script setup>
中):import { defineProps } from 'vue'; const props = defineProps({ message: String, // 类型校验 count: { type: Number, required: true, // 必传 default: 0 // 默认值(非必传时生效) } }); // 使用 console.log(props.message);
- 父组件在使用子组件时,通过属性传递数据:
- 子传父(自定义事件):
- 子组件通过
defineEmits
声明可触发的事件,然后通过emit
触发:import { defineEmits } from 'vue'; const emit = defineEmits(['change', 'submit']); // 声明事件 // 触发事件并传递数据 const handleClick = () => { emit('change', '新数据'); emit('submit'); };
- 父组件监听子组件触发的事件:
<ChildComponent @change="handleChange" @submit="handleSubmit" /> <script setup> const handleChange = (data) => { console.log('子组件传递的数据:', data); }; </script>
- 子组件通过
20. setup 函数的作用是什么?它有哪些参数?
setup
是 Vue3 中 Composition API 的入口函数,用于组织组件逻辑,在组件创建前(beforeCreate
之前)执行。
- 作用:
- 替代 Vue2 中的
data
、methods
等选项,将组件逻辑以函数形式组织。 - 提供响应式 API(如
ref
、reactive
)、生命周期钩子等的使用入口。 - 返回的对象属性或方法可直接在模板中使用。
- 替代 Vue2 中的
- 参数:
props
:接收父组件传递的 props,是响应式对象,不可直接解构(需用toRefs
解构)。context
:包含组件上下文信息,如attrs
(非 props 属性)、slots
(插槽)、emit
(触发事件的函数)。
- 示例:
import { toRefs } from 'vue'; setup(props, context) { // 解构 props(保持响应式) const { message } = toRefs(props); // 使用 context const { emit, attrs, slots } = context; const handleClick = () => { emit('submit'); // 触发事件 }; // 返回给模板 return { message, handleClick }; }
21. Vue3 中如何实现计算属性(computed)?
计算属性通过 computed
函数创建,用于依赖响应式数据的衍生值,具有缓存特性(依赖不变时不会重新计算)。
- 基本用法:
import { ref, computed } from 'vue'; const count = ref(0); // 只读计算属性 const doubleCount = computed(() => { return count.value * 2; }); console.log(doubleCount.value); // 0(初始值) count.value = 1; console.log(doubleCount.value); // 2(依赖变化后重新计算)
- 可写计算属性(需指定
get
和set
):const fullName = computed({ get: () => `${firstName.value} ${lastName.value}`, set: (newValue) => { const [f, l] = newValue.split(' '); firstName.value = f; lastName.value = l; } }); // 赋值时触发 set fullName.value = '李四 王';
- 特点:依赖变化时自动更新,适合处理需要基于现有数据计算衍生值的场景(如过滤列表、格式化数据)。
22. Vue3 中如何实现跨层级组件通信(祖孙组件)?
跨层级通信可通过 provide
和 inject
实现,无需手动逐层传递 props:
- 祖先组件提供数据(provide):
import { provide, ref } from 'vue'; setup() { const theme = ref('dark'); // 提供数据(key 为标识,value 为数据) provide('themeKey', theme); provide('userInfo', { name: '张三', age: 25 }); }
- 后代组件接收数据(inject):
import { inject } from 'vue'; setup() { // 接收数据(第一个参数为 key,第二个参数为默认值) const theme = inject('themeKey', 'light'); // 响应式数据(保持响应式) const userInfo = inject('userInfo', { name: '默认名称' }); // 普通数据 return { theme, userInfo }; }
- 注意:
provide
传递响应式数据(如ref
/reactive
对象)时,后代组件接收后可感知数据变化。- 若需在后代组件修改祖先提供的数据,建议祖先同时提供修改方法(如
provide('updateTheme', (newVal) => { theme.value = newVal; })
)。
No. | 大剑师精品GIS教程推荐 |
---|---|
0 | 地图渲染基础- 【WebGL 教程】 - 【Canvas 教程】 - 【SVG 教程】 |
1 | Openlayers 【入门教程】 - 【源代码+示例 300+】 |
2 | Leaflet 【入门教程】 - 【源代码+图文示例 150+】 |
3 | MapboxGL 【入门教程】 - 【源代码+图文示例150+】 |
4 | Cesium 【入门教程】 - 【源代码+综合教程 200+】 |
5 | threejs 【中文API】 - 【源代码+图文示例200+】 |
23. Vue3 中如何使用插槽(Slot)?
插槽用于父组件向子组件传递模板内容,Vue3 中插槽用法与 Vue2 类似,但支持更简洁的语法:
- 默认插槽:
- 子组件中用
<slot>
定义插槽位置:<!-- 子组件 --> <div class="container"> <slot></slot> <!-- 父组件传递的内容会插入这里 --> </div>
- 父组件使用子组件时,在标签内写入内容:
<ChildComponent> <p>这是默认插槽的内容</p> </ChildComponent>
- 子组件中用
- 具名插槽(多个插槽区分):
- 子组件用
name
属性定义具名插槽:<slot name="header"></slot> <!-- 头部插槽 --> <slot></slot> <!-- 默认插槽(name 默认为 default) --> <slot name="footer"></slot> <!-- 底部插槽 -->
- 父组件用
v-slot:name
(或缩写#name
)指定内容插入的插槽:<ChildComponent> <template #header> <h1>页面标题</h1> </template> <p>默认内容</p> <template #footer> <p>页脚信息</p> </template> </ChildComponent>
- 子组件用
- 作用域插槽(子组件向父组件传递数据):
- 子组件在插槽中通过
v-bind
传递数据:<slot :user="userInfo" :count="10"></slot>
- 父组件接收数据并使用:
<ChildComponent> <template #default="slotProps"> <!-- 接收数据 --> <p>用户名:{{ slotProps.user.name }}</p> <p>数量:{{ slotProps.count }}</p> </template> </ChildComponent>
- 子组件在插槽中通过
24. Vue3 中如何使用 Teleport 组件将内容渲染到指定位置?
Teleport
(瞬移)用于将组件内的部分内容渲染到 DOM 树的其他位置(如 <body>
),解决样式层级限制问题(如模态框、弹窗)。
- 基本用法:
<template> <button @click="showModal = true">打开弹窗</button> <!-- to 指定目标位置(CSS 选择器或 DOM 元素) --> <Teleport to="body"> <div v-if="showModal" class="modal"> <p>这是弹窗内容</p> <button @click="showModal = false">关闭</button> </div> </Teleport> </template> <script setup> import { ref } from 'vue'; const showModal = ref(false); </script>
- 特点:
- 渲染位置改变,但内容仍受当前组件的响应式数据控制(如
showModal
)。 - 可避免弹窗被父组件的
overflow: hidden
或z-index
样式影响。 to
属性支持动态值(如:to="targetElement"
)。
- 渲染位置改变,但内容仍受当前组件的响应式数据控制(如
25. Vue3 中 Suspense 组件的作用是什么?如何使用?
Suspense
用于处理异步操作(如异步组件、异步数据加载),在等待异步内容时显示占位符(loading 状态),提升用户体验。
- 使用步骤:
- 定义异步组件(通过
defineAsyncComponent
创建):import { defineAsyncComponent } from 'vue'; const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue') // 动态导入(返回 Promise) );
- 用
Suspense
包裹异步组件,通过default
插槽放异步内容,fallback
插槽放加载提示:<Suspense> <template #default> <AsyncComponent /> <!-- 异步组件 --> </template> <template #fallback> <div>加载中...</div> <!-- 等待时显示 --> </template> </Suspense>
- 定义异步组件(通过
- 注意:
Suspense
仅能处理返回 Promise 的异步操作(如异步组件、setup
中使用await
的组件)。- 若异步操作失败,需配合错误边界(
onErrorCaptured
)处理错误。
26. Vue3 中 <script setup>
如何声明 props 和 emits?
<script setup>
是 Vue3 中简化组件写法的语法糖,声明 props 和 emits 需使用 defineProps
和 defineEmits
编译宏(无需导入):
- 声明 props:
// 方式1:数组形式(仅指定名称) const props = defineProps(['name', 'age']); // 方式2:对象形式(带类型校验和配置) const props = defineProps({ name: { type: String, required: true, // 必传 default: '默认名称' // 非必传时的默认值 }, age: { type: Number, validator: (value) => { // 自定义校验 return value >= 0; } } }); // 使用 console.log(props.name);
- 声明 emits:
// 方式1:数组形式(仅指定事件名) const emit = defineEmits(['change', 'delete']); // 方式2:对象形式(带参数校验) const emit = defineEmits({ change: (value) => { // 校验参数 return typeof value === 'string'; }, delete: null // 无参数 }); // 触发事件 const handleClick = () => { emit('change', '新值'); emit('delete'); };
27. Vue3 中如何动态绑定样式(class 和 style)?
动态绑定样式通过 :class
和 :style
实现,支持对象、数组等形式:
- 动态 class:
- 对象形式(键为类名,值为布尔值,
true
则添加类):<div :class="{ active: isActive, 'text-danger': hasError }"></div> <script setup> import { ref } from 'vue'; const isActive = ref(true); const hasError = ref(false); </script>
- 数组形式(直接指定类名列表):
<div :class="[activeClass, errorClass]"></div> <script setup> const activeClass = ref('active'); const errorClass = ref('text-danger'); </script>
- 对象形式(键为类名,值为布尔值,
- 动态 style:
- 对象形式(键为样式名,值为样式值):
<div :style="{ color: textColor, fontSize: `${fontSize}px` }"></div> <script setup> import { ref } from 'vue'; const textColor = ref('red'); const fontSize = ref(16); </script>
- 数组形式(多个样式对象合并):
<div :style="[baseStyle, customStyle]"></div> <script setup> const baseStyle = ref({ backgroundColor: 'white' }); const customStyle = ref({ border: '1px solid black' }); </script>
- 对象形式(键为样式名,值为样式值):
28. Vue3 中如何注册全局组件?
注册全局组件需通过 app.component
方法,在应用实例上注册,所有组件均可直接使用:
- 步骤:
- 导入组件:
import GlobalButton from './components/GlobalButton.vue'; import GlobalCard from './components/GlobalCard.vue';
- 在创建应用实例后,通过
component
方法注册:import { createApp } from 'vue'; import App from './App.vue'; const app = createApp(App); // 注册单个组件(参数:组件名,组件对象) app.component('GlobalButton', GlobalButton); // 链式注册多个 app.component('GlobalCard', GlobalCard) .component('GlobalInput', GlobalInput); app.mount('#app');
- 在任意组件中直接使用,无需局部导入:
<GlobalButton label="全局按钮"></GlobalButton> <GlobalCard></GlobalCard>
- 导入组件:
- 注意:全局组件名建议使用 kebab-case 格式(如
global-button
),符合 HTML 标签规范。
29. Vue3 中如何实现自定义指令?
自定义指令用于封装 DOM 操作,通过 app.directive
注册,支持多个钩子函数(如 mounted
、updated
等):
- 全局注册:
import { createApp } from 'vue'; import App from './App.vue'; const app = createApp(App); // 注册自定义指令(参数:指令名,配置对象) app.directive('focus', { // 钩子函数:元素挂载到 DOM 时执行 mounted(el) { el.focus(); // 自动聚焦 }, // 元素更新时执行(如组件重新渲染) updated(el) { el.focus(); } }); app.mount('#app');
- 局部注册(仅在当前组件生效):
// 在组件中 export default { directives: { focus: { mounted(el) { el.focus(); } } } };
- 使用自定义指令:
<input v-focus type="text" /> <!-- 应用指令,会自动聚焦 -->
- 常用钩子函数:
mounted
:元素挂载后执行(初始化操作)。updated
:元素更新时执行(更新操作)。unmounted
:元素卸载时执行(清理操作)。
30. Vue3 中 nextTick
的作用是什么?如何使用?
nextTick
用于在 DOM 更新完成后执行回调函数,解决数据修改后立即操作 DOM 无法获取最新状态的问题(Vue 会批量更新 DOM,而非同步更新)。
- 作用:等待当前组件的 DOM 完成更新后,再执行后续逻辑(如获取更新后的 DOM 尺寸、操作新渲染的元素等)。
- 使用方式:
import { ref, nextTick } from 'vue'; const count = ref(0); const el = ref(null); const handleClick = async () => { count.value++; // 修改数据,触发 DOM 更新(异步) // 此时直接操作 DOM 可能获取旧状态 console.log(el.value.offsetHeight); // 可能为更新前的高度 // 等待 DOM 更新完成后执行 await nextTick(); console.log(el.value.offsetHeight); // 最新高度 };
- 场景:
- 数据修改后需要立即获取 DOM 元素的位置、尺寸等。
- 数据修改后需要操作新渲染的 DOM 元素(如滚动到指定位置)。
- 注意:
nextTick
返回 Promise,可配合async/await
使用,也可使用回调函数(nextTick(() => { ... })
)。