- 今天去面试,碰到了几个有意思的题目,回答没到面试官想得到的答案,所以回来复盘一下,总结一下。
1. watch如果监听的属性层级很深的情况下你会怎么办。
-
这个其实是面试官考察我对watch的第三个参数层级了解,第一个参数为监听的对象,第二个参数为回调,第三个参数为options。
-
答案为:第三个参数options的设置为
deep:true就可以了
- 举个例子
import { watch, reactive } from 'vue'
const arr = reactive({
brr: {
crr: {
err: {
frr: '初始值'
}
}
}
})
// 监听整个arr对象,并开启深度监听
watch(
arr,
(newVal, oldVal) => {
console.log('深层属性变化了', newVal.brr.crr.err.frr)
},
{ deep: true } // 关键配置:开启深度监听
)
// 修改深层属性时会触发watch
arr.brr.crr.err.frr = '修改后的值'
2. watch如何实现watchEffect的第一次加载就进行监听呢。
- 先说答案,在 Vue3 中,
watch默认不会在初始加载时执行回调,而watchEffect会在初始化时立即执行一次(收集依赖)。如果想让watch实现类似watchEffect的初始执行效果,可以通过options参数中的immediate: true来实现,没想到第三个参数这么重要。
import { watch, ref } from 'vue'
const count = ref(0)
// 使用 immediate: true 让 watch 在初始时就执行一次
watch(
count,
(newVal, oldVal) => {
console.log('count的值:', newVal)
},
{ immediate: true } // 关键配置:初始时立即执行
)
都到这里了,顺便就科普下watch的第三个参数都有哪些选项吧,对了这些都是可选属性,可写可不写。
immediate:是否在初始时立即执行一次回调,默认为falsedeep:是否深度监听对象内部属性的变化,默认为falseflush:控制回调函数的执行时机,可选值为'pre'(默认,DOM 更新前)、'post'(DOM 更新后)、'sync'(同步执行)
3.ref能做到的事为什么要reactive?
-
这个很有意思,这个是面试官问,其实ref也能定义reactive的object对象,那么我为什么还要使用reactive?
-
这个给我问懵了,我只能说习惯这么写了,面试官是这么回答我的。
-
其实Vue3并没有抛弃掉
Object.defineProperty,如果我们使用ref的话,其实底层还是Object.defineProperty 去进行数据劫持,那么使用ref定义对象的话,是可以的,不过为了避免出现无法响应式,所以会在底层使用reactive去再包一层,这样就会显得多此一举,对于对象 / 数组类型,当用ref包裹时,Vue3 会自动将其转换为 Proxy 代理对象(相当于ref(reactive(obj)))。也就是说,ref定义的对象底层最终还是通过 Proxy 实现响应式,而非Object.defineProperty。
4.跨窗口通信。
-
这个其实我之前接触过,被问到过一次,就是同一个项目,然后我A窗口点击一下,让B窗口跳出个弹窗,我回答用
BroadcastChannel,面试官对这个方法挺满意的,但是后续又问了句还有其他方法吗,我就回答不上来了。 -
直接说答案
1. window.postMessage()(最常用)
postMessage 是 HTML5 提供的跨窗口通信 API,支持同源和跨域场景,是最灵活的方式。
语法:
// 发送消息
targetWindow.postMessage(message, targetOrigin, [transfer])
-
targetWindow:接收消息的窗口对象(如iframe.contentWindow、window.open()返回的窗口等 -
message:要发送的数据(可序列化的任意类型,如对象、字符串等)。 -
targetOrigin:指定接收窗口的域名(*表示不限制,但不安全)。
接收消息:
// 监听 message 事件
window.addEventListener('message', (event) => {
// 验证发送方域名(重要!防止恶意消息)
if (event.origin !== 'https://example.com') return;
// event.data 为发送的消息
console.log('收到消息:', event.data);
// 可选:向发送方回复消息
event.source.postMessage('已收到', event.origin);
});
2. 共享全局变量(仅限同源且有引用关系)
如果两个窗口存在直接引用关系(如父窗口与子窗口通过 window.open() 打开),可以通过共享全局变量通信。
示例(父窗口打开子窗口) :
// 父窗口打开子窗口
const childWindow = window.open('https://example.com/child.html');
// 向子窗口发送数据(需等待子窗口加载完成)
childWindow.onload = () => {
childWindow.sharedData = { name: 'Parent' }; // 子窗口可访问此变量
};
// 子窗口中
console.log(window.sharedData); // 访问父窗口设置的变量
// 向父窗口发送数据
window.opener.sharedData = { name: 'Child' }; // 父窗口通过 window.sharedData 访问
3. localStorage/sessionStorage(利用存储事件)
同源窗口可以通过监听 localStorage 的 storage 事件实现通信(sessionStorage 仅在同一窗口的标签页共享,不适合跨窗口)。
原理:当一个窗口修改 localStorage 时,其他同源窗口会触发 storage 事件。
偷个懒这玩意其实就是监听,知道就行,感觉用不到,但是知道这个概念以后真用的时候直接百度。
5.vue3的diff算法更新更新了什么
| 特性 | Vue2 做法 | Vue3 优化 |
|---|---|---|
| 静态节点处理 | 每次 diff 都会比较 | 提升到渲染函数外,只创建一次 |
| 动态节点识别 | 全量比较节点所有属性 | 通过 PatchFlag 精准定位动态部分 |
| 列表 diff 算法 | 双端比较,可能产生较多 DOM 移动 | 最长递增子序列,减少 DOM 操作 |
| 根节点限制 | 必须有唯一根节点 | 支持 Fragment 多根节点 |
6.使用element来实现文件上传的百分比。
-
这个还是我不够细致,我回答的是根据接口的反应,比如调用接口0%,请求中padding为50%,然后请求完毕100%。
-
真实操作应该是在 Element UI(或 Element Plus)中主要通过监听上传过程的
progress事件来实现。这个事件会实时返回当前上传的进度信息,包括已上传字节数、总字节数等,据此可以计算出百分比。
<template>
<el-upload
class="upload-demo"
action="/api/upload" <!-- 后端上传接口 -->
:on-progress="handleProgress" <!-- 监听上传进度 -->
:on-success="handleSuccess"
:on-error="handleError"
:file-list="fileList"
:auto-upload="true"
>
<el-button type="primary">点击上传</el-button>
<!-- 进度条显示 -->
<template #file="scope">
<div class="file-item">
<span>{{ scope.file.name }}</span>
<!-- 上传中显示进度条 -->
<el-progress
v-if="scope.file.status === 'uploading'"
:percentage="scope.file.percentage" <!-- 百分比数据 -->
stroke-width="2"
style="margin-top: 5px;"
></el-progress>
<!-- 上传完成/失败状态 -->
<span
v-else-if="scope.file.status === 'success'"
class="success-status"
>
上传成功
</span>
<span
v-else-if="scope.file.status === 'error'"
class="error-status"
>
上传失败
</span>
</div>
</template>
</el-upload>
</template>
<script setup>
import { ref } from 'vue';
const fileList = ref([]);
// 处理上传进度
const handleProgress = (event, file, fileList) => {
// event.loaded: 已上传字节数
// event.total: 总字节数
// 计算百分比并赋值给 file 对象(Element 会自动更新视图)
file.percentage = Math.round((event.loaded / event.total) * 100);
};
// 上传成功回调
const handleSuccess = (response, file, fileList) => {
console.log('上传成功', response);
};
// 上传失败回调
const handleError = (error, file, fileList) => {
console.error('上传失败', error);
};
</script>
总结
- 说实话,这些都是比较基础的东西,没想到栽在这里了,有点不应该,所以回来赶紧复盘了,本身就是想当个笔记,若有错误还请评论区指出,感激不尽,若能帮到你,也祝你工作顺利、面试成功。