最后更新于
2019-12-02
由于最近在工作当中经常遇到移动端的一些相关问题,所以打算抽些时间来重新的梳理一下,主要目的是厘清 Web
移动端开发的前前后后,一些技术的发展过程,一些问题的优化手段以及给出一些常见的兼容性问题的解决方案,主要参考的是 移动端的兼容适配 这篇文章,内容有所调整,主要是方便自己理解,如果想了解更为详细的流程可以参考原文
响应式设计
首先我们先来看看『响应式设计』,这个与移动端开发有着密切的联系,响应式设计即是 RWD
(Responsive Web Design
)
根据维基百科及其参考文献,理论上响应式界面能够适应不同的设备,描述响应式界面最著名的一句话就是 Content is like water
,翻译成中文便是『如果将屏幕看作容器,那么内容就像水一样』
为什么要设计响应式界面
那么为什么要费神的尝试统一所有设备呢?简单来说主要有以下几点
- 即便是
PC
或Mac
用户,调查显示只有一半的人会将浏览器全屏显示,而剩下的一般人使用多大的浏览器,很难预知 - 台式机、投影、电视、笔记本、手机、平板、手表、
VR
等智能设备正在不断增加,主流设备的概念正在消失 - 屏幕分辨率正飞速发展,同一张图片在不同设备上看起来,大小可能天差地别
- 鼠标、触屏、笔、摄像头手势等不可预期的操控方式正在不断出现
响应式界面的四个层次
- 同一页面在不同大小和比例上看起来都应该是舒适的
- 同一页面在不同分辨率上看起来都应该是合理
- 同一页面在不同操作方式(如鼠标和触屏)下,体验应该是统一的
- 同一页面在不同类型的设备(手机、平板、电脑)上,交互方式应该是符合习惯的
响应式界面的基本规则
- 可伸缩的内容区块,内容区块的在一定程度上能够自动调整,以确保填满整个页面
- 可自由排布的内容区块,当页面尺寸变动较大时,能够减少或是增加排布的列数
- 适应页面尺寸的边距,到页面尺寸发生更大变化时,区块的边距也应该变化
- 能够适应比例变化的图片,对于常见的宽度调整,图片在隐去两侧部分时,依旧保持美观可用
- 能够自动隐藏或是显示部分的内容,如在电脑上显示的的大段描述文本,在手机上就只能少量显示或全部隐藏
- 能自动折叠的导航和菜单,展开还是收起,应该根据页面尺寸来判断
- 放弃使用像素作为尺寸单位,用
dp
(对于前端来说,这里可能是em/rem
)尺寸等方法来确保页面在分辨率相差很大的设备上,看起来也能保持一致,同时也要求提供的图片应该比预想的更大,才能适应高分辨率的屏幕
上面一段我觉得已经涵盖了响应式设计的绝大部分,简单总结起来,可以概括为
- 媒体查询,边界断点的规则设定(
Media queries && break point
) - 内容的可伸缩性效果(
Flexibel visuals
) - 流式网格布局(
Fluid grids
) - 主要内容呈现及图片的高质量(
Main content and high quality
)
响应式和自适应
响应式设计是 Responsive Web Design
(RWD
),自适应设计是 Adaptive Web Design
(AWD
),经常有人会将两者混为一谈,或者其实根本也区分不了所谓的响应式与自适应
RWD
和 AWD
两者都是为了适配各种不同的移动设备,致力于提升用户体验所产生的的技术,核心思想是用技术来使网页适应从小到大(现在到超大)的不同分辨率的屏幕,通常认为 RWD
是 AWD
的子集
RWD
,Ethan Marcote
的文章是大家认为RWD
的起源,他提出的RWD
方案是通过HTML
和CSS
的媒体查询技术,配合流体布局实现,RWD
倾向于只改变元素的外观布局,而不大幅度改变内容,Jeffrey Zeldman
总结说,我们就把RWD
定义为一切能用来为各种分辨率和设备性能优化视觉体验的技术AWD
,Adaptive Design
是Aaron Gustafson
的书的标题,他认为AWD
在包括RWD
的CSS
媒体查询技术以外,也要用JavaScript
来操作HTML
来更适应移动设备的能力,AWD
有可能会针对移动端用户减去内容,减去功能,AWD
可以在服务器端就进行优化,把优化过的内容送到终端上
一图胜千言
从定义上而言 RWD
是一套代码,适用于所有屏幕,而 AWD
则是多端多套代码,本文不会过多去纠结响应式与自适应区别,这两者的本质都是致力于适配不同设备,更好地提升用户体验,更多相关内容可以参考下面两个链接
渐进增强和优雅降级
- 渐进增强(
progressive enhancement
),针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验 - 优雅降级(
graceful degradation
),一开始就构建完整的功能,然后再针对低版本浏览器进行兼容
两者的区别是,优雅降级是从复杂的现状开始,并试图减少用户体验的供给,而渐进增强则是从一个非常基础的,能够起作用的版本开始,并不断扩充,以适应未来环境的需要,降级意味着往回看,而渐进增强则意味着朝前看,同时保证其根基处于安全地带
渐进增强和优雅降级通常是 AWD
会牵扯到的另一个技术术语,本质上而言即是随着屏幕的大小的改变,功能会一点一点增强,也通常会用在一些高级 CSS3
属性上,我们对一些 CSS
属性进行特性检测,甚至不进行特性检测直接使用,后果是在支持它的网页上该属性正常展示,而不支持它的网页该属性不生效,但也不影响用户的基本使用
典型的例子是 CSS3
逐渐被大众认可并被使用,PC
端页面开始由低版本的 IE
向兼容性更好的 IE 9+
,Chrome
,Firefox
浏览器转变的时期,我们可以对页面元素直接使用阴影,圆角等属性,对于不支持它的低版本 IE
而言,没有什么损失,而对于支持它的高级浏览器而言,带给了用户更好的交互视觉体验,这就是渐进增强
移动端屏幕适配方案
我们在下面会针对一些具体的案例来深入的了解,首先来看高保真还原设计稿,也就是如何适配移动端繁杂的屏幕大小,通常而言设计师只会给出单一分辨率下的设计稿,而我们要做的,就是以这个设计稿为基准,去适配所有不同大小的移动端设备
在此之前,有一些基础概念需要我们先行理解
设备独立像素
这里以 iPhone 6/7/8
为例,我们打开 Chrome
开发者工具,如下图
这里的 375 * 667
表示的是设备独立像素(DIP
),也可以理解为 CSS
像素,也称为『逻辑像素』,简单来说就是
设备独立像素 =
CSS
像素 = 逻辑像素
物理像素
那么什么又是物理像素呢,我们到电商网站购买手机,都会看一看手机的参数,以 iPhone 7
为例
可以看到,iPhone 7
的分辨率是 1334 x 750
,这里描述的就是屏幕实际的物理像素,物理像素又称为『设备像素』,显示屏是由一个个物理像素点组成的,1334 x 750
表示手机分别在垂直和水平上所具有的像素点数,通过控制每个像素点的颜色,就可以使屏幕显示出不同的图像,屏幕从工厂出来那天起,它上面的物理像素点就固定不变了,单位为 pt
,简单来说就是
设备像素 = 物理像素
DPR(Device Pixel Ratio)设备像素比
有了上面两个概念以后,我们就可以顺理成章引出下一个概念,DPR
(Device Pixel Ratio
)设备像素比,这个与我们通常说的视网膜屏(多倍屏,Retina
屏)有关
设备像素比描述的是未缩放状态下,物理像素和设备独立像素的初始比例关系,简单的计算公式是下面这样的
DPR
= 物理像素 / 设备独立像素
我们套用一下上面 iPhone 7
的数据(取设备的物理像素宽度与设备独立像素宽度进行计算),可以发现
iPhone 7
的DPR
=iPhone 7
的物理像素宽度 /iPhone 7
的设备独立像素宽度 =2
其实也就是 750 / 375 = 2
或者是 1334 / 667 = 2
,所以我们可以得到 iPhone 7
的 dpr
为 2
,这也就是我们常说的『视网膜屏幕』,而视网膜(Retina
)屏幕是苹果公司的一个营销术语,简单来说就是苹果公司将 dpr > 1
的屏幕称为视网膜屏幕
在视网膜屏幕中,以 dpr = 2
为例,把 4
(2 x 2
)个像素当 1
个像素使用,这样让屏幕看起来更精致,但是元素的大小本身不会改变
下面我们再来看看 iPhone XS Max
它的物理像素如上图是 2688 x 1242
而它的 CSS
像素是 896 x 414
,所以我们可以很容易得出 iPhone XS Max
的 dpr
为 3
至此,我们也算是完成了一个小的里程碑,我们通常说的 H5
手机适配其实也就是指的这两个维度
- 适配不同屏幕大小,也就是适配不同屏幕下的
CSS
像素 - 适配不同像素密度,也就是适配不同屏幕下
dpr
不一致导致的一些问题
当然这里还有一些其他比较重要的概念,比如
PPI
、DPI
等,但是我们就不详细展开了,可以自行去加深理解
适配不同屏幕大小
通常我们在移动端开发过程当中,都会添加一个移动端专用的 meta
标签,也就是下面这样的
1 | <meta name="viewport" content="width=device-width; initial-scale=1; maximum-scale=1; minimum-scale=1; user-scalable=no;"> |
那么它的具体含义是什么样的呢?其中视口(viewport
)代表当前可见的计算机图形区域,在 Web
浏览器术语中,通常与浏览器窗口相同,但不包括浏览器的 UI
,菜单栏等,即指你正在浏览的文档的那一部分,而关于 content
参数的含义可以参考下表
属性 | 含义 | 取值 |
---|---|---|
width |
定义视口的宽度,单位为像索 | 正整数或设备宽度 device-width |
height |
定义视口的高度,单位为像索 | 正整数或 device-height |
initial-scale |
定义初始缩放值 | 整数或小数 |
minimum-scale |
定义缩小最小比例,它必须小于或等于 maximum-scale 设置 |
整数或小数 |
maximum-scale |
定义放大最大比例,它必须大于或等于 minimum-scale 设置 |
整数或小数 |
user-scalable |
定义是否允许用户手动缩放页面,默认值 yes |
yes/no |
我们在移动端视口要想视觉效果和体验好,那么我们的视口宽度必去无限接近理想视口,而所谓的理想视口,一般来讲,这个视口其实不是真是存在的,它对设备来说是一个最理想布局视口尺寸,在用户不进行手动缩放的情况下,可以将页面理想地展示,那么所谓的理想宽度就是浏览器(屏幕)的宽度了
于是上述的 meta
设置,就是我们的理想设置,它规定了我们的视口宽度为屏幕宽度,初始缩放比例为 1
,就是初始时候我们的视觉视口就是理想视口,下面我们就来看看有哪些比较常用的适配方法
通常来说,适配不同屏幕大小,也就是适配不同屏幕下的 CSS
像素,最早移动端屏幕 CSS
像素适配方案是媒体查询,但是无法做到接近 100%
的还原,适配不同屏幕大小其实只需要遵循一条原则,确保页面元素大小的与屏幕大小保持一定比例,也就是『按比例还原设计稿』
假设我们现在拿到标注为 375 * 667
的大小的设计稿,其中一个元素的标注如下
以页面宽度为基准的话,那么
- 元素的宽度为
209 / 375 = 55.73%
- 元素的高度为
80 / 375 = 21.33%
- 元素的上左右边距依次计算
这样,无论屏幕的 CSS
像素宽度是 320px
还是 375px
还是 414px
,按照等量百分比还原出来的界面总是正确的
然而,理想很丰满,现实很骨感,实现上述百分比方案的核心需要一个全局通用的基准单位,让所有百分比展示以它为基准,但是在 CSS
中,根据 CSS Values and Units Module Level 4 的定义
百分比值总要相对于另一个量,比如长度,每个允许使用百分比值的属性,同时也要定义百分比值参照的那个量,这个量可以是相同元素的另一个属性的值,也可以是祖先元素的某个属性的值,甚至是格式化上下文的一个度量(比如包含块的宽度)
具体来说就是
- 宽度(
width
)、间距(maring/padding
)支持百分比值,但默认的相对参考值是包含块的宽度 - 高度(
height
)百分比的大小是相对其父级元素高的大小 - 边框(
border
)不支持百分值 - 边框圆角半径(
border-radius
)支持百分比值,但水平方向相对参考值是盒子的宽度,垂直方向相对参考值是盒子的高度 - 文本大小(
font-size
)支持百分比值,但相对参考值是父元素的font-size
的值 - 盒阴影(
box-shadow
)和文本阴影(text-shadow
)不支持百分比值
首先,支持百分比单位的度量属性有其各自的参照基准,其次并非所有度量属性都支持百分比单位,所以我们需要另辟蹊径
rem 适配方案
在 vw
方案出来之前,最被大众接受的就是使用 rem
进行适配的方案,因为 rem
满足上面说的,可以是一个全局性的基准单位
rem
(font size of the root element
)在 CSS Values and Units Module Level 3 中的定义就是,根据网页的根元素来设置字体大小,和em
(font size of the element
)的区别是,em
是根据其父元素的字体大小来设置,而rem
是根据网页的跟元素(html
)来设置字体大小
flexible
基于此,淘宝早年推行的一套以 rem
为基准的适配方案 lib-flexible,其核心做法在于
根据设备的dpr
动态改写<meta>
标签,设置viewport
的缩放给<html>
元素添加data-dpr
属性,并且动态改写data-dpr
的值- 根据
document.documentElement.clientWidth
动态修改<html>
的font-size
,页面其他元素使用rem
作为长度单位进行布局,从而实现页面的等比缩放
只不过关于上面划掉的两点,是因为现在的 lib-flexible
库已经不再去缩放 viewport
,字体大小的设定也直接使用了 rem
hotcss
hotcss 不是一个库,也不是一个框架,它是一个移动端布局开发解决方案,使用 hotcss
可以让移动端布局开发更容易,本质的思想与 flexible
完全一致
rem 方案的一些总结
使用 flexible/hotcss
作为屏幕宽度适配解决方案,是存在一些问题的
- 动态修改
viewport
是存在一定的风险的,譬如通过viewport
改变了页面的缩放之后,获取到的innerWidth/innerHeight
也会随之发生变化,如果业务逻辑有获取此类高宽进行其他计算的,可能会导致意想不到的错误(其实到今天,其实存在很多在flexible
基础上演化而来的各种rem
解决方案,有的不会对viewport
进行缩放处理,自行处理1px
边框问题) flexible/hotcss
都并非纯CSS
方案,需要引入一定的JavaScript
代码rem
的设计初衷并非是用于解决此类问题,用rem
进行页面的宽度适配多少有一种hack
的感觉- 存在一定的兼容性问题,对于安卓
4.4
以下版本系统不支持viewport
缩放(当然flexible
处理Android
系列时,始终认为其dpr
为1
,没有进行viewport
缩放)
vw 适配方案
严格来说,使用 rem
进行页面适配其实是一种 hack
手段,rem
单位的初衷本身并不是用来进行移动端页面宽度适配的,到了今天有了一种更好的替代方案,那就是使用 vw
进行适配,也就是『百分比适配』
百分比适配方案的核心需要一个全局通用的基准单位,rem
是不错,但是需要借助 JavaScript
进行动态修改根元素的 font-size
,而 vw/vh
(vmax/vmin
)的出现则很好弥补 rem
需要 JavaScript
辅助的缺点
CSS Values and Units Module Level 4 的定义,vw
等于初始包含块(html
元素)宽度的 1%
,也就是
1vw
等于window.innerWidth
的数值的1%
1vh
等于window.innerHeight
的数值的1%
再以上面设计稿图的元素为例,那么
- 元素的宽度为
209 / 375 = 55.73% = 55.73vw
- 元素的高度为
80 / 375 = 21.33% = 21.33vw
- 元素的上左右边距依次计算
根据相关的测试,可以使用 vw
进行长度单位的有
- 容器大小适配,可以使用
vw
- 文本大小的适配,可以使用
vw
- 大于
1px
的边框、圆角、阴影都可以使用vw
- 内距和外距,可以使用
vw
简单的一个页面,看看效果,完全是等比例缩放的效果,线上预览地址可见 CodePen
自动转换插件
当我们使用 rem
作为长度单位的时,通常会有借助 Sass/Less
实现一个转换函数,像是这样
1 | // 假设设计稿的宽度是 375px,假设取设计稿宽度下 1rem = 100px |
同理,在 vw
方案下,我们只需要去改写这个方法
1 | // 假设设计稿的宽度是 375px |
当然,我们还可以借助一些插件包去实现这个自动转换,提高效率,譬如 postcss-px-to-viewport
vw polyfill
但是 vw
目前毕竟还是存在兼容问题的,如下
看上去其实已经覆盖了绝大部分设备,但是如果在业务使用中又真的出现了兼容问题,应该怎么处理呢?通常来说,有下面两种方式可以进行降级处理
CSS Houdini
,通过CSS Houdini
针对vw
做处理,调用CSS Typed OM Level1
提供的CSSUnitValue API
CSS Polyfill
,通过相应的Polyfill
做相应的处理,目前针对于vw
单位的Polyfill
主要有vminpoly
、Viewport Units Buggyfill
、vunits.js
和Modernizr
等
对于 vw 方案的一些总结
vw
确实看上去很不错,但是也是存在它的一些问题
- 也没能很好的解决
1px
边框在高清屏下的显示问题,需要自行处理 - 由于
vw
方案是完全的等比缩放,在完全等比还原设计稿的同时带来的一个问题是无法很好的限定一个最大最小宽度值,由于rem
方案是借助JavaScript
的,所以这一点rem
比vw
会更加的灵活
当然,两个方案现阶段其实都可以使用甚至一起搭配使用,更多详情可以参考下面几个链接
- 再聊移动端页面的适配
- H5 必知必会之像素级还原设计稿
- Responsive And Fluid Typography With vh And vw Units
- 使用 VH 和 VW 实现真正的流体排版
1px 边框
上面我们说到使用 vw
适配屏幕大小方案,其中有一个缺点就是在 Retina
屏下,无法很好的展示真正的 1px
物理像素线条,设计师想要的 Retina
下 border: 1px
,其实是 1
物理像素宽度,而不是 1 CSS
像素宽度,对于 CSS
而言
- 在
dpr = 1
时,此时1
物理像素等于1 CSS
像素宽度 - 在
dpr = 2
时,此时1
物理像素等于0.5 CSS
宽度像素,可以认为border-width: 1px
这里的1px
其实是1 CSS
像素宽度,等于2
像素物理宽度,设计师其实想要的是border-width: 0.5px
- 在
dpr = 3
时,此时1
物理像素等于0.33 CSS
宽度像素,设计师其实想要的是border-width: 0.333px
然而并不是所有手机浏览器都能识别 border-width: 0.5px
,在 iOS 7
以下,Android
等其他系统里,小于 1px
的单位会被当成为 0px
处理,那么如何实现这 0.5px
、0.33px
呢?
比较常见的解决方式有以下几种,线上预览地址可见 CodePen
- 渐变实现
- 使用缩放实现
- 使用图片实现(
base64
) - 使用
SVG
实现(嵌入background-url
)
图片适配及优化
图像通常占据了网页上下载资源绝的大部分,优化图像通常可以最大限度地减少从网站下载的字节数以及提高网站性能,通常我们可以采取以下这些通用的优化手段
- 消除多余的图像资源
- 尽可能利用
CSS3/SVG
矢量图像替代某些光栅图像 - 谨慎使用字体图标,使用网页字体取代在图像中进行文本编码
- 选择正确的图片格式
- 为不同
DPR
屏幕提供最适合的图片尺寸
在这里我们主要重点关注如何在不同的 dpr
屏幕下,让图片看起来都不失真,首先就是上述的第二点,尽可能利用 CSS3/SVG
矢量图像替代某些光栅图像,而像某些简单的几何图标,可以用 CSS3
快速实现的图形,都应该尽量避免使用光栅图像,这样能够保证它们在任何尺寸下都不会失真
其次,实在到了必须使用光栅图像的地步,也是有许多方式能保证图像在各种场景下都不失真
无脑多倍图
在移动端假设我们需要一张 CSS
像素为 300 x 200
的图像,考虑到现在已经有了 dpr = 3
的设备,那么要保证图片在 dpr = 3
的设备下也正常高清展示,我们最大可能需要一张 900 x 600
的原图,这样不管设备的 dpr
是否为 3
,我们统一都使用 3
倍图,这样即使在 dpr = 1
,dpr = 2
的设备上,也能非常好的展示图片
当然这样并不可取,会造成大量带宽的浪费,并且现代浏览器,提供了更好的方式,让我们能够根据设备 dpr
的不同,提供不同尺寸的图片
srcset 配合 1x 2x 像素密度描述符
简单来说,srcset
可以根据不同的 dpr
拉取对应尺寸的图片
1 | <div class='illustration'> |
上面 srcset
里的 1x
,2x
表示『像素密度描述符』,简单来说就是
- 当屏幕的
dpr = 1
时,使用images/illustration-small.png
这张图 - 当屏幕的
dpr = 2
时,使用images/illustration-big.png
这张图
srcset 属性配合 sizes 属性 w 宽度描述符
上面的 1x
,2x
的写法比较容易接受易于理解,但是除此之外,srcset
属性还有一个 w
宽度描述符,配合 sizes
属性一起使用可以覆盖更多的面,比如下面这个示例
1 | <img |
其中 sizes = "(min-width: 600px) 600px, 300px"
的意思是,如果屏幕当前的 CSS
像素宽度大于或者等于 600px
,则图片的 CSS
宽度为 600px
,反之则图片的 CSS
宽度为 300px
也就是 sizes
属性声明了在不同宽度下图片的 CSS
宽度表现,这里可以理解为,大屏幕下图片宽度为 600px
,小屏幕下图片宽度为 300px
(具体的媒体查询代码由 CSS
实现)
但是需要注意的是,这里的
sizes
属性只是声明了在不同宽度下图片的CSS
宽度表现,而具体使图片在大于600px
的屏幕上展示为600px
宽度的代码需要另外由CSS
或者JavaScript
实现
而 srcset = "photo@1x.png 300w, photo@2x.png 600w, photo@3x.png 1200w"
里面的 300w
,600w
,900w
叫宽度描述符,那么我们该如何确定当前场景会选取哪张图片呢?
当前屏幕 dpr = 2,CSS 宽度为 375px
当前屏幕 CSS
宽度为 375px
,则图片 CSS
宽度为 300px
,分别用上述 3
个宽度描述符的数值除以 300
300 / 300 = 1
600 / 300 = 2
1200 / 300 = 4
上面得到的 1
、2
、4
即是算出的有效的像素密度,换算成和 x
描述符等价的值,这里 600w
算出的 2
即满足 dpr = 2
的情况选择此张图
当前屏幕 dpr = 3,CSS 宽度为 414px
当前屏幕 CSS
宽度为 414px
,则图片 CSS
宽度仍为 300px
,再计算一次
300 / 300 = 1
600 / 300 = 2
1200 / 300 = 4
因为 dpr = 3
,2
已经不满足了,则此时会选择 1200w
这张图
当前屏幕 dpr = 1,CSS 宽度为 1920px
当前屏幕 CSS
宽度为 1920px
,则图片 CSS
宽度变为了 600px
,再计算一次
300 / 600 = .5
600 / 600 = 1
1200 / 600 = 2
因为 dpr = 1
,所以此时会选择 600w
对应的图片,具体使用方式可以参考 CodePen 这个示例
简单总结就是此方案的意义在于考虑到了响应性布局的复杂性与屏幕的多样性,利用上述规则,可以一次适配 PC
端大屏幕和移动端高清屏,一箭多雕,此外 CSS
还有个类似的属性 image-set()
,搭配使用,效果更佳,其他更多内容可以参考以下链接
字体适配方案
本小节当中我们主要来看看字体相关的适配方案
字体大小
字体是很多前端开发同学容易忽略的一个点,但是其中也是有很多小知识点,首先要知道,浏览器有最小字体限制
PC
上最小font-size = 12px
- 手机上最小
font-size = 8px
如果小于最小字体,那么字体默认就是最小字体,其次很多早期的文章规范都建议不要使用奇数级单位来定义字体大小(如 13px
,15px
等),容易在一些低端设备上造成字体模糊,出现锯齿
字体的选择展示
在字体适配上面,我们需要从『性能』和『展示效果』两个维度去考虑,完整的一个字体资源实在太大了,所以我们应该尽可能的使用用户设备上已有的字体,而不是额外去下载字体资源,从而使加载时间明显加快
而从展示效果层面来说,使用系统字体能更好的与当前操作系统使用的相匹配,得到最佳的展示效果,所以我们在字体使用方面,有一个应该尽量去遵循的原则,也是现在大部分网站在字体适配上使用的策略,那就是『尽量去使用各个支持平台上的默认系统字体』
兼顾各个操作系统
我们通常比较常见的操作系统有 Windows
、Windows Phone
、Mac OS X
、iPhone
、Android Phone
、Linux
等,当然对于普通用户而言,无须关注 Linux
系统,我们下面就以 CSS-Trick 网站最新的 font-family
为例,看看是如何在字体选择上做到适配各个操作系统的
1 | { |
font-family 关键字
对于 CSS
中的 font-family
而言,它有两类取值
- 一类是类似这样的具体的字体族名定义
font-family: Arial
这里定义了一个具体的字体样式,字体族名为Arial
- 一类是通用字体族名,它是一种备选机制,用于在指定的字体不可用时给出较好的字体,类似这样
font-family: sans-serif
其中 sans-serif
表无衬线字体族,例如 'Open Sans'
,'Arial'
,'微软雅黑'
等等,关于通用字体族名,在 CSS Fonts Module Level 3 – Basic Font Properties 中定义了 5
个,也就是我们熟知的几个通用字体族名
serif
,衬线字体族sans-serif
,非衬线字体族monospace
,等宽字体,即字体中每个字宽度相同cursive
,草书字体fantasy
,主要是那些具有特殊艺术效果的字体
新增通用字体族关键字
而在 CSS Fonts Module Level 4 – Basic Font Properties 中,新增了几个关键字
system-ui
,系统默认字体emoji
,用于兼容emoji
表情符号字符math
,适用于数学表达式fangsong
,此字体系列用于中文的(仿宋)字体
下面我们还是以上面的 font-family
为例,看看它们几者之间的区别
system-ui
简单而言,font-family: system-ui
的目的就是在不同的操作系统的 Web
页面下,自动选择本操作系统下的默认系统字体
默认使用特定操作系统的系统字体可以提高性能,因为浏览器或者 WebView
不必去下载任何字体文件,而是使用已有的字体文件,font-family: system-ui
字体设置的优势之处在于它与当前操作系统使用的字体相匹配,对于文本内容而言,它可以得到最恰当的展示
San Francisco Fonts
我们在上面简单的了解了 system-ui
字体族,但是像 -apple-system
、BlinkMacSystemFont
并没有在最新的标准里出现,那它们又代表什么意思呢?
在此之前,我们需要先了解一下 San Francisco Fonts
,San Francisco Fonts
又叫旧金山字体,是一款西文字体,随着 iOS 9
更新面世,在 WatchOS
中随 Apple Watch
一起悄然发售,并且还将在 Apple TV
上的新 tvOS
中使用
San Francisco Fonts
在 iOS
系统上用于替代升级另外一款西文字体 Helvetica Neue
,Apple
做了一些重要的改变,使其成为平台上更好的,甚至是完美的西文字体
-apple-system/BlinkMacSystemFont
话说回来,正如每个前端开发人员都知道的那样,将一个功能纳入规范是一回事,将其纳入浏览器又是另一回事,幸运的是 system-ui
的普及很快,Chrome
和 Safari
都可以在各种平台上完全支持它,只有 Mozilla
和 Windows
相对落后,我们来看看 system-ui
的兼容性
仔细看上图的最后两行
Supported as the -apple-system value
(only on macOS and iOS
)Supported as the BlinkMacSystemFont value
(only on macOS
)
考虑到不同平台及向后兼容,在 macOS
和 iOS
上,我们需要使用 -apple-system
及 BlinkMacSystemFont
来兼容适配 system-ui
标准
Segoe UI
Segoe UI
是 Windows
从 Vista
开始的默认西文字体族,只有西文,不支持汉字,属于无衬线体,它也表示一个系列而不是某一款字体
使用 font-family: Segoe UI
可以在 Windows
平台及 Windows Phone
上选取最佳的西文字体展示
Roboto
Roboto
是为 Android
操作系统设计的一个无衬线字体家族,Google
描述该字体为现代的、但平易近人和有感情的
这个字体家族包含 Thin
、Light
、Regular
、Medium
、Bold
、Black
六种粗细及相配的斜体
总结
到此,我们可以总结一下了,还是以上面 CSS-Trick 网站的 font-family
定义为例子
1 | { |
system-ui
,使用各个支持平台上的默认系统字体-apple-system
, 在一些稍低版本Mac OS X
和iOS
上,它针对旧版上的Neue Helvetica
和Lucida Grande
字体,升级使用更为合适的San Francisco Fonts
BlinkMacSystemFont
,针对一些Mac OS X
上的Chrome
浏览器,使用系统默认字体segoe ui
,在Windows
及Windows Phone
上选取系统默认字体Roboto
,面向Android
和一些新版的的Chrome OS
Helvetica/Arial
,在针对不同操作系统不同平台设定采用默认系统字体后,针对一些低版本浏览器的降级方案sans-serif
,兜底方案,保证字体风格统一,至少也得是无衬线字体
上述 5
个字体族定义,优先级由高到底,可以看到,它们 5
个都并非某个特定字体,基本的核心思想都是选择对应平台上的默认系统字体,涵盖了 iOS
、MAC OS X
、Android
、Windows
、Windows Phone
基本所有用户经常使用的主流操作系统
使用系统默认字体的主要原因是性能,字体通常是网站上加载的最大(也可能是最重)的资源之一,如果我们可以使用用户机器上已有的字体,我们就完全不需要再去获取字体资源,从而使加载时间明显加快,并且系统字体的优点在于它与当前操作系统使用的相匹配,因此它的文本展示必然也是一个让人舒适展示效果
当然,上述 font-family
的定义不一定是最佳的,譬如天猫移动端在 font-family
最前面添加了 'PingFang SC'
,'miui'
等字体,想必也是有他们业务上的考虑,这里我们就再来简单的看看天猫的字体定义与 Github
的字体定义,比较比较它们两者有什么异同
- 天猫 的定义为
font-family: "PingFang SC", miui, system-ui, -apple-system, BlinkMacSystemFont, Helvetica Neue, Helvetica, sans-serif;
- Github 的定义为
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
对比可以发现,它们总体遵循了这样一个基本原则
- 尽量使用系统默认字体
使用系统默认字体的主要原因是性能,并且系统字体的优点在于它与当前操作系统使用的相匹配,因此它的文本展示必然也是一个让人舒适展示效果
- 兼顾中西,西文在前,中文在后
中文或者西文(英文)都要考虑到,由于大部分中文字体也是带有英文部分的,但是英文部分又不怎么好看,但是英文字体中大多不包含中文,通常会先进行英文字体的声明,选择最优的英文字体,这样
不会影响到中文字体的选择,中文字体声明则紧随其次
- 兼顾多操作系统
选择字体的时候要考虑多操作系统,例如 MAC OS
下的很多中文字体在 Windows
都没有预装,为了保证 MAC
用户的体验,在定义中文字体的时候,先定义 MAC
用户的中文字体,再定义 Windows
用户的中文字体
- 兼顾旧操作系统,以字体族系列
serif
和sans-serif
结尾
当使用一些非常新的字体时,要考虑向下兼容,兼顾到一些极旧的操作系统,使用字体族系列 serif
和 sans-serif
结尾总归是不错的选择
暂时就总结这么多,更多的关于字体方面的细节知识,可以参考下面这几个链接
- Web 字体 font-family 再探秘
- 你该知道的字体 – font-family
- Web 字体 font-family 再探秘
- Using UI System Fonts In Web Design: A Quick Practical Guide
- System Fonts in CSS
- Apple’s San Francisco Font
前端布局的兼容适配
最后我们再来简单的了解一下前端布局的兼容适配,因为前端工程师的一大工作内容就是页面布局,无论在 PC
端还是移动端,页面布局的兼容适配都是重中之重,在整个前端发展的历程中,布局的方法也在不断的推陈出新
布局发展历程
简单来说,前端的布局发展历程经历了下面几个过程
表格布局 ==> 定位布局 ==> 浮动布局 ==>
flexbox
布局 ==>gridbox
布局
每一种布局在特定时期都发挥了重要的作用,而每一种新的布局方式的出现,往往都是因为现有的布局方式已经在该时期已经无法很好的满足开发者的需求,无法满足越来越潮流的页面布局的方式,以 Flexbox
的出现为例子,在 Flexbox
被大家广为接受使用之前,我们一直在使用定位+浮动的布局方式,像下面这个布局
容器宽度不定,内部三个元素,均分排列且占满整个空间,并且垂直居中,如果使用定位加浮动的布局方式,你无法很快想到最佳的解决方式,三个元素并排那么必然需要浮动或者绝对定位,容器宽度不定且中间元素始终居中,需要顾虑的方面就很多了,也许使用 text-align: justufy
可以 hack
实现等等,然而使用 flexbox
布局的话,只需要下面三行代码就可以了
1 | .container { |
flexbox
的出现,一次性解决了流动布局,弹性布局,排列方式等多个问题,并且它是简洁的,可控的,再来看一个例子,水平垂直居中一个元素,使用 flexbox
也许是最便捷的,线上预览地址可见 CodePen
1 | .container { |
CSS Grid Layout
flexbox
已经足够优秀了,为什么 gird
网格布局的出现又是为什么?它又解决了哪些 flex
布局无法很好解决的问题呢?先来看看下面这张图
flexbox
是一维布局,他只能在一条直线上放置你的内容区块,而 grid
是一个二维布局,它除了可以灵活的控制水平方向之外,还能轻易的控制垂直方向的布局模式,对于上图那样的九宫格布局,它就可以轻而易举的完成,如果用图片来进行表示的话,flexbox
是下面这样的
而 gridbox
则是
在现阶段,移动端布局应当更多使用 flexbox
去完成(相对那些还在使用 float
布局的),而考虑到未来页面布局的推陈出新,对于 Grid
布局我们应当像前几年对待 flexbox
一样,重视起来,随着兼容性的普及,Grid
布局也会慢慢成为主流