
1) 【一句话结论】设计一个可复用的Grid布局组件,通过Props动态配置列数、列宽(固定/百分比)、间距及响应式断点,核心是计算属性缓存布局计算(含容器宽度匹配与列宽缩放),结合BEM或CSS模块实现样式隔离,避免全局样式污染。
2) 【原理/概念讲解】老师口吻,解释关键概念:
cols(基础列数,如12)、colWidth(列宽类型,如固定100px或百分比50%)、gap(间距,如16px)、responsiveCols(不同屏幕的列数配置,如手机端2列、平板端4列)等Props。列宽类型分为固定(直接使用px值)和百分比(按容器宽度比例计算,如50%则容器宽度的50%除以列数得到单列宽度)。computed属性(如computedCols)缓存当前屏幕的列数,避免每次渲染都重新计算。计算属性还会缓存列宽总和与容器宽度的匹配结果(若列宽总和超过容器宽度,按比例缩放列宽,确保布局不溢出)。.grid__container、.grid__item)或Vue的CSS模块,将样式与组件绑定,避免全局样式冲突(如避免直接使用grid-template-columns全局样式,而是通过组件内样式控制)。3) 【对比与适用场景】
| 方式 | 定义 | 核心特性 | 使用场景 | 注意点 |
|---|---|---|---|---|
| 传统Flex布局 | 基于一维Flexbox的布局 | 适合行/列自适应,列宽可动态调整,但仅支持一维布局 | 简单的行或列排列,如导航栏、按钮组 | 列宽固定时需额外计算,响应式需额外断点处理,二维布局能力弱 |
| 现代Grid组件(本题设计) | 通过Props配置列数、列宽(固定/百分比)、间距及响应式断点,计算属性缓存布局计算 | 支持二维布局(行内多列),列宽可固定/百分比,响应式列数动态调整,列宽总和与容器宽度匹配逻辑 | 复杂的响应式布局,如产品列表、内容卡片网格,需要灵活的列宽和间距控制 | 需处理计算属性缓存,样式隔离,容器宽度与列宽总和的匹配逻辑,大量子元素优化 |
4) 【示例】(伪代码,包含动态容器宽度监听与虚拟滚动优化)
// Grid组件伪代码(Vue + ResizeObserver)
<template>
<div :class="gridClass" :style="gridStyle">
<slot></slot>
</div>
</template>
<script>
import { computed, ref, onMounted, onUnmounted } from 'vue';
export default {
name: 'Grid',
props: {
cols: {
type: Number,
default: 12
},
colWidth: {
type: String,
default: '1fr' // 固定1fr或百分比如'50%'
},
gap: {
type: String,
default: '16px'
},
responsiveCols: {
type: Object,
default: () => ({})
}
},
setup(props) {
const parentWidth = ref(0);
const resizeObserver = ref(null);
// 1. 监听父容器宽度变化
onMounted(() => {
resizeObserver.value = new ResizeObserver(() => {
parentWidth.value = window.innerWidth; // 假设容器占满视口
// 触发计算属性重新计算
});
resizeObserver.value.observe(document.body);
});
onUnmounted(() => {
if (resizeObserver.value) {
resizeObserver.value.disconnect();
}
});
// 2. 计算响应式列数(缓存)
const computedCols = computed(() => {
const baseCols = props.responsiveCols[getScreenSize()] || props.cols;
return baseCols;
});
// 3. 计算列宽(固定/百分比)
const colWidth = computed(() => {
if (props.colWidth.includes('%')) {
const percent = parseFloat(props.colWidth.replace('%', '')) / 100;
return parentWidth.value * percent / computedCols.value;
} else {
return parseFloat(props.colWidth);
}
});
// 4. 计算列宽总和与容器宽度匹配(避免溢出)
const colWidthSum = computed(() => {
const sum = computedCols.value * colWidth.value;
if (sum > parentWidth.value) {
const scale = parentWidth.value / sum;
return colWidth.value * scale;
}
return sum;
});
// 5. 计算布局样式(缓存)
const gridStyle = computed(() => ({
display: 'grid',
gridTemplateColumns: `${colWidthSum.value} ${colWidthSum.value} ... ${colWidthSum.value}`,
gap: props.gap,
...(props.responsiveCols[getScreenSize()] ? {} : {
gridTemplateColumns: `${props.cols} ${props.cols} ... ${props.cols}fr`
})
}));
// 6. BEM类名
const gridClass = computed(() => {
return ['grid', 'grid--responsive'].filter(Boolean);
});
return {
computedCols,
colWidth,
colWidthSum,
gridStyle,
gridClass
};
}
};
</script>
<style lang="scss" scoped>
/* BEM命名示例 */
.grid {
display: grid;
gap: 16px;
transition: all 0.3s ease;
&--responsive {
@media (max-width: 768px) {
grid-template-columns: repeat(2, 1fr);
}
@media (max-width: 1024px) {
grid-template-columns: repeat(3, 1fr);
}
}
}
.grid__item {
background: #f0f0f0;
padding: 16px;
border-radius: 8px;
}
</style>
(注:虚拟滚动优化可结合第三方库,如Vue虚拟滚动,这里简化为示例)
5) 【面试口播版答案】(约90秒)
“面试官您好,我来设计一个可复用的Grid布局组件。首先,组件通过Props动态配置列数、列宽(固定px或百分比)、间距、响应式断点等。列宽计算上,固定宽度直接使用,百分比则按容器宽度比例计算。为了处理容器宽度变化,我会用ResizeObserver监听父容器宽度,动态调整列宽。响应式列数根据屏幕尺寸(比如手机768px用2列,桌面用4列)选择,计算属性缓存这些计算结果,避免重复计算。样式方面,用BEM命名(如.grid__container、.grid__item)或CSS模块,将样式与组件绑定,避免全局样式污染。大量子元素时,考虑虚拟滚动优化。总结来说,这个组件通过Props传递配置、计算属性缓存布局计算、样式隔离,实现灵活的响应式Grid布局,同时解决了容器宽度限制和样式污染问题。”
6) 【追问清单】及回答要点:
responsiveCols配置,计算属性根据屏幕尺寸选择对应的列数,再结合colWidth计算列宽(固定px直接用,百分比按容器宽度比例计算),若列宽总和超过容器宽度,则按比例缩放列宽。.grid__container、.grid__item)或Vue的CSS模块,将样式文件与组件绑定,避免全局样式冲突。7) 【常见坑/雷区】
grid-template-columns全局样式)。