前端面试题(三)
一、once用原生js如何生效
在原生 JavaScript 中,可以使用 addEventListener 的第三个参数 { once: true },事件触发一次后浏览器会自动移除监听器。
const button = document.querySelector('#btn');
button.addEventListener('click', () => {
console.log('只会执行一次');
}, {
once: true
});
如果是 Lodash 的 once 函数,可以通过闭包保存一个状态,第一次调用时执行原函数,后续调用直接返回第一次的结果:
function once(fn) { let called = false; let result; return function (...args) { if (!called) { called = true; result = fn.apply(this, args); } return result; }; } const init = once(() => { console.log('只执行一次'); }); init(); // 执行 init(); // 不再执行
二、获取div在屏幕上的位置
在 Vue 中通过 ref 获取 DOM 元素,然后调用原生的 getBoundingClientRect() 获取元素相对于视口的位置和尺寸。getBoundingClientRect() 返回的是元素相对于当前浏览器视口的位置:
// this.$refs.box.getBoundingClientRect(); vue2的写法
const rect = boxRef.value.getBoundingClientRect(); rect.top; // 元素顶部距离视口顶部 rect.left; // 元素左侧距离视口左侧 rect.right; // 元素右侧距离视口左侧 rect.bottom; // 元素底部距离视口顶部 rect.width; // 元素宽度 rect.height; // 元素高度
如果需要获取元素相对于整个文档的位置,需要加上页面滚动距离
const rect = boxRef.value.getBoundingClientRect(); const top = rect.top + window.scrollY; const left = rect.left + window.scrollX; console.log(top, left);
三、css 变量如何声明和使用
CSS 变量也叫自定义属性,使用 --变量名 声明,使用 var(--变量名) 读取。通常会声明在 :root 中,这样可以全局使用。
:root { --primary-color: #409eff; --font-size: 16px; --border-radius: 4px; } .button { color: white; background-color: var(--primary-color); font-size: var(--font-size); border-radius: var(--border-radius); }
也可以声明局部变量:
.card { --card-padding: 20px; padding: var(--card-padding); }
CSS 变量具有继承性,子元素可以使用父元素声明的变量:
.parent { --text-color: red; } .child { color: var(--text-color); }
可以设置备用值:
// 如果--title-color没有定义,就会使用#333。
.title { color: var(--title-color, #333); }
JavaScript 也可以读取和修改 CSS 变量:
const root = document.documentElement; // 修改 root.style.setProperty('--primary-color', 'green'); // 读取 const color = getComputedStyle(root) .getPropertyValue('--primary-color') .trim(); console.log(color);
在 Vue 中也可以动态绑定:
<template>
<div class="box" :style="{ '--box-color': color }">
内容
</div>
</template>
<script setup>
import { ref } from 'vue';
const color = ref('skyblue');
</script>
<style>
.box {
color: var(--box-color);
}
</style>
四、fetch下载文件
使用 fetch 下载文件时,先发起请求,再通过 response.blob() 将响应转换成二进制对象,最后创建一个临时 <a> 标签,设置 href 和 download 属性,模拟点击下载。
async function downloadFile(url, filename) { const response = await fetch(url, { method: 'GET', headers: { Authorization: `Bearer ${token}` }, credentials: 'include' }); if (!response.ok) { throw new Error('文件下载失败'); } const blob = await response.blob(); const urlObject = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = urlObject; a.download = filename; a.click(); URL.revokeObjectURL(urlObject); }
如果接口通过响应头返回文件名,可以读取 Content-Disposition
const disposition = response.headers.get('Content-Disposition');
let filename = 'download';
const match = disposition?.match(/filename\*?=(?:UTF-8'')?["']?([^"';]+)["']?/i);
if (match) {
filename = decodeURIComponent(match[1]);
}
需要注意几个点:
fetch遇到404或500时不会自动进入catch,所以要手动判断response.ok。- 文件下载要使用
response.blob(),JSON 数据则使用response.json()。 URL.createObjectURL(blob)创建的是临时对象 URL,下载完成后应调用URL.revokeObjectURL()释放资源。- 如果是跨域请求,服务端需要配置 CORS;如果要读取文件名响应头,还需要暴露请求头:
Access-Control-Expose-Headers: Content-Disposition
一句话总结:
fetch 下载文件的核心流程是:fetch 请求、判断 response.ok、调用 blob()、创建对象 URL、通过 <a download> 模拟点击,最后释放对象 URL。

浙公网安备 33010602011771号