移动端相关 BFC CSS原子化 ✅
移动端相关
设备宽度&视口
设备宽度是指设备屏幕的实际物理宽度,通常以像素(px)表示。它是固定的,取决于设备的硬件。不同设备(如手机、平板、桌面等)有不同的设备宽度。
常被提及的视口可被分为3种:布局视口、视觉视口和理想视口。
- 布局视口是指网页布局计算和呈现的区域。在大移动端部分设备上,布局视口的宽度和设备宽度是相等的(如375px)。
- 视觉视口是用户在屏幕上实际看到的网页区域,包括布局视口中可见的部分。通过缩放用户可以改变视觉视口的大小,但不会影响到布局视口。
- 理想视口
说这种视口其实并不存在都不过分是设计师所期望的视口大小,它是设计时的一个目标,或者说他就是布局视口的目标。我们可以通过HTML文件里面的meta标签来设置布局视口的宽度,比如360px,然后让这个网页在不同的移动端设备上运行,此时360px宽度的布局视口就是我们的理想视口。
如下:
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximun-scale=1.0, minimun-scale=1.0">
width=device-width即布局视口宽度和设备保持一致,user-scalable=no即不允许用户缩放,initial-scale=1.0即初始缩放比例为1,maximun-scale和minimun-scale分别为最大缩放比例和最小缩放比例。
<!-- 这里的demo也很容易实现: -->
<meta name="viewport" content="width=2000, initial-scale=1.0" />
此时你可以在控制台里观察到body的宽度被改为了2000px,但是这种方法和直接修改body的width不同。因为body的宽度默认是100%,应该说因为布局视口宽度的改变,才成功影响了body的宽度以及整个页面的布局。
物理像素比(DPR)
物理像素比是一个用来描述设备的物理像素(设备屏幕上实际的像素点,由硬件决定,即所谓的分辨率)与逻辑像素(你在CSS里面写的1px)之间关系的概念。假设你那部屏幕是正方形的手机的分辨率是200✖️200,而他的逻辑分辨率是100✖️100,他的物理像素比就是40000/10000=4,即你在CSS里写的1px映射到你的屏幕上占据的就是4个物理像素。假设一个设备的物理像素为 1920x1080,而其逻辑分辨率为 960x540。
与物理像素比相关的移动端优化方案有很多 (应该吧哈哈,感觉多数是为了解决图片在移动端设备屏幕上显示模糊的问题) ,比较经典的是「二倍图」。
二倍图的分辨率是常规图像的两倍,使用它是为了确保在高DPR设备上图像显示的清晰度和细腻度,减少锯齿和模糊现象。例如,如果一个标准图像的尺寸是100x100像素,那么其对应的二倍图将是200x200像素,然后我准备一个height和width都为100px的img,在高DPR的屏幕里加载200x200那张,在低DPR的屏幕里加载100x100那张,都能得到较清晰的效果。
还有一个相关的技术就是「SVG可缩放矢量图形」 (哥们用的也不多哈哈) ,使用SVG格式的图像不受分辨率限制,可以在任何分辨率下保持清晰,因为SVG的计算基于矢量而非像素。这使得SVG图像在不同DPR的设备上都能以最佳效果呈现。 (在不同的分辨率或DPR下,SVG的逻辑像素是不同的,我觉得是这样。)
屏幕宽度如何适配?
- 给不同的设备开发不同的页面(组件),但成本较高。
- 通过link标签引入样式表时,通过设置media属性给不同宽度的设备引入不同的样式表。如
media=“(min-width: 768px)”
- 通过CSS来实现响应式布局:Grid布局、流式布局、媒体查询、em、rem等单位的使用……
em是相对于当前元素的字体大小,rem是相对于根元素的字体大小
vmax 是视口宽高中的最大值,vmin 是视口宽高中的最小值
vh和vw懒得说
顺带一提,媒体查询还可以实现横竖屏切换样式的需求。
@media (orientation: portrait) {
body {
background-color: lightblue;
/* 其他竖屏样式 */
}
}
/* 横屏样式 */
@media (orientation: landscape) {
body {
background-color: lightgreen;
/* 其他横屏样式 */
}
}
通过JS来实现也可以,只要给window绑定一个resize事件,通过innerWidth和innerHeight的大小的比较来确定当前是横屏还是竖屏即可。
BFC
BFC(块格式化上下文)是 CSS 中的一个概念,用于控制块级盒子之间的布局和排列。它是一种独立的布局环境,可以影响元素的排版和相对位置。BFC中的元素会处理自己的布局、边距、浮动等,而不会受其外部区域的干扰。在 BFC 内的元素及其布局不会影响到外部的元素。
但说来说去,BFC 主要还是用于解决一些常见的布局问题(特别是在涉及到浮动和外边距合并的时候):
- 清除浮动:当一个元素使用浮动时,它不会在父元素的正常文档流中占据空间,这可能导致父元素的高度塌陷。通过创建 BFC,包含浮动元素的父元素可以“识别”这些浮动子元素,从而自适应高度,避免塌陷现象。
- 控制外边距重叠:在默认情况下,块级元素的上下外边距可能会合并,即相邻的块级元素的边距会计算为最大的边距,而不是简单的相加。当元素处于 BFC 中时,其外边距不会与外部元素的外边距进行合并。
以下是常用的开启BFC的方法:
- 设置 overflow 属性为 hidden、auto 或 scroll,非 visible 即可(最常用)。
- 设置 position 属性为 absolute 或 fixed。
- 设置 float 属性为 left 或 right。
- 设置 display 属性为 inline-block 以及在父元素上明确设置宽度。
谈到BFC,顺便记录一下前端开发中很常见的一个Bug (我觉得不算bug,absolute用久了之后我觉得这是更符合正常逻辑的行为) ,即高度塌陷:
- 假设一个父级元素里嵌套一个子元素,然后他俩都设置了margin-top属性,但是父元素真正的margin-top在默认情况下会取这两个定义的margin-top的较大值,此时可能会呈现父元素跟随子元素一起“塌陷”的视觉效果。此时给父元素的box-sizing设置为border-box,或者添加padding或border都可以解决这个问题。
- 浮动和绝对定位都可能造成高度塌陷,因为浮动的元素或者绝对定位的元素不再占据父元素的布局(文档流),他们无法将父元素的高度给撑开。针对浮动带来的这种问题,可以通过给父元素设置clear属性为both来清除浮动元素对当前元素所产生的影响。
绝对定位造成的这种问题,老老实实给固定高度算了。
CSS原子化 (感觉难用的要命)
以Tailwindcss这个库为例子,一番体验下来感觉他的哲学就是减少对 CSS 的依赖,并直接在html里面的类名写样式。以下demo来自它的官网,效果是实现了一个花里胡哨的按钮,重点看一下他那坨又臭又长的类名。
<button
class="px-4 py-1 text-sm text-purple-600 font-semibold
rounded-full border border-purple-200 hover:text-white
hover:bg-purple-600 hover:border-transparent
focus:outline-none focus:ring-2
focus:ring-purple-600 focus:ring-offset-2">
Message
</button>
但存在即合理,不然我也不会在面试里被问到这玩意。可以“想象”到的是这东西的熟练使用可以让开发的时候效率提升,减少CSS的代码量和复杂性,而且可以确保在整个项目中样式的一致性,避免样式冲突。
不过最有意义的应该还是用这种技术来实现样式代码的复用,这种复用不仅是少写点CSS代码,还在于编译 (或者说打包产物) 的体积的见效。因为如果是了简单的逻辑复用,那Sass已经提供了很好的方案:
// 正方形
@mixin square($size) {
height: $size * $base-unit;
width: $size * $base-unit;
}
// 圆形
@mixin circle($size) {
height: $size * $base-unit;
width: $size * $base-unit;
border-radius: 50%;
}
问题是,square或者circle没被多复用一次,编译出来的东西就会多一块css代码;但是Tailwindcss不会,他的css代码永远只有一份,通过类名就可以实现里面写好的样式。
最后可以聊一下这种技术可能存在的问题 (野猪吃不了细糠,这玩意我能喷一天):
- 学习成本:较高
- 性能问题:在某些情况下,类名的碰撞可能导致性能下降,尤其是在渲染方面,因为浏览器需要处理的类较多
- 维护成本:语义化不够清晰。如果项目中有大量的类名,那么维护起来会比较麻烦,需要花费更多的时间