前几天用了 vfit 做大屏适配,感觉挺好用的。 作为一个有追求的前端(其实是闲得蛋疼),我忍不住扒了一下它的源码。 结果发现... 核心代码居然不到 50 行? 🤯
今天就带大家深入浅出地拆解一下这个库,看看它是如何优雅地解决大屏适配问题的。
🎯 核心原理:Scale 方案
大屏适配的核心流派无非就那几种:
- REM / VW:修改基准值,所有单位都要改,侵入性强。
- Scale :CSS
transform: scale(),简单粗暴,不改变布局。
vfit 采用的是 Scale 方案。 它的思路非常清晰:
计算
当前屏幕宽高/设计稿宽高=缩放比例,然后应用到根容器上。
🔍 源码拆解
1. 监听屏幕变化 (ResizeObserver)
在 src/scale.ts 里,它没有使用传统的 window.onresize,而是用了更现代、性能更好的 ResizeObserver。
typescript
// src/scale.ts (简化版)
export function observeScale(target, designHeight, onScale) {
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width, height } = entry.contentRect
// ... 计算 scale 逻辑
onScale(scaleVal)
}
})
observer.observe(target)
}
为什么用 ResizeObserver? 因为它能监听任意元素的大小变化,而不仅仅是 window。这意味着你可以把大屏嵌入到一个 div 里(比如后台管理系统的某个 dashboard 页签),它依然能正常缩放!
2. 三种缩放模式的算法
vfit 支持 width | height | auto 三种模式。 看看它是怎么算的:
typescript
// src/scale.ts
if (scaleMode === 'height') {
// 1. 只要高度撑满,宽度按比例缩放(适合定高宽屏)
scaleVal = rectHeight / designHeight
} else if (scaleMode === 'width') {
// 2. 只要宽度撑满,高度按比例缩放(适合定宽长页面)
scaleVal = rectWidth / designWidth
} else {
// 3. auto 模式 (默认):保持宽高比,全部显示 (Contain)
const designRatio = designWidth / designHeight
// 如果当前屏幕更宽(带鱼屏),就以高度为基准
// 如果当前屏幕更窄(手机),就以宽度为基准
if (rectWidth / rectHeight < designRatio) {
scaleVal = rectWidth / designWidth
} else {
scaleVal = rectHeight / designHeight
}
}
这段逻辑非常经典,保证了内容永远不会被裁剪,同时也保持了设计稿的比例。
3. 神奇的 FitContainer
大家用 scale 方案最头疼的是什么? 是定位! 😫 一旦缩放了,原来的 top: 100px 可能就不是视觉上的 100px 了,而且居中后的偏移量也很难算。
vfit 的 FitContainer.vue 组件用了一个很巧妙的思路解决这个问题:
typescript
// src/components/FitContainer.vue
// 监听 scale 和 props 变化
watch([() => props.scale, fitScale], () => {
const s = fitScale.value
// 对于 left 和 top,乘以缩放比例 s
// 这样就能让元素相对于原点进行位移
if (key === 'left' || key === 'top') {
position[key] = val * s + 'px'
}
// 对于 right 和 bottom,不乘!
// 这样元素就会死死贴住容器边缘
else {
position[key] = val + 'px'
}
})
这里有个细节设计得很赞:
top/left:参与缩放。这意味着你给的坐标是相对于"设计稿左上角"的。right/bottom:不参与缩放。这意味着你给的坐标是相对于"屏幕边缘"的。
这解决了大屏开发的一个痛点:中间内容要还原设计稿,但边缘菜单要贴边。
💡 我们可以学到什么?
- API 设计要简单 :用户只关心
designWidth和designHeight,不要把复杂的计算抛给用户。 - 善用现代 API :
ResizeObserver比resize事件更好用。 - 组件化思维 :通过
FitContainer把"定位计算"封装起来,业务代码里就全是干净的 px 值了。
总结
vfit 的源码虽然简单,但确实切中了痛点。 有时候造轮子不一定要多高深的技术,能优雅地解决一个小问题,就是一个好轮子。
推荐大家去读读它的源码,就几个文件,半小时就能看完。 👉 GitHub: v-plugin/vfit