移动端开发相关内容汇总

移动端开发相关内容汇总

最后更新于 2019-12-02

由于最近在工作当中经常遇到移动端的一些相关问题,所以打算抽些时间来重新的梳理一下,主要目的是厘清 Web 移动端开发的前前后后,一些技术的发展过程,一些问题的优化手段以及给出一些常见的兼容性问题的解决方案,主要参考的是 移动端的兼容适配 这篇文章,内容有所调整,主要是方便自己理解,如果想了解更为详细的流程可以参考原文

响应式设计

首先我们先来看看『响应式设计』,这个与移动端开发有着密切的联系,响应式设计即是 RWDResponsive Web Design

根据维基百科及其参考文献,理论上响应式界面能够适应不同的设备,描述响应式界面最著名的一句话就是 Content is like water,翻译成中文便是『如果将屏幕看作容器,那么内容就像水一样』

为什么要设计响应式界面

那么为什么要费神的尝试统一所有设备呢?简单来说主要有以下几点

  • 即便是 PCMac 用户,调查显示只有一半的人会将浏览器全屏显示,而剩下的一般人使用多大的浏览器,很难预知
  • 台式机、投影、电视、笔记本、手机、平板、手表、VR 等智能设备正在不断增加,主流设备的概念正在消失
  • 屏幕分辨率正飞速发展,同一张图片在不同设备上看起来,大小可能天差地别
  • 鼠标、触屏、笔、摄像头手势等不可预期的操控方式正在不断出现

响应式界面的四个层次

  • 同一页面在不同大小和比例上看起来都应该是舒适的
  • 同一页面在不同分辨率上看起来都应该是合理
  • 同一页面在不同操作方式(如鼠标和触屏)下,体验应该是统一的
  • 同一页面在不同类型的设备(手机、平板、电脑)上,交互方式应该是符合习惯的

响应式界面的基本规则

  • 可伸缩的内容区块,内容区块的在一定程度上能够自动调整,以确保填满整个页面

  • 可自由排布的内容区块,当页面尺寸变动较大时,能够减少或是增加排布的列数

  • 适应页面尺寸的边距,到页面尺寸发生更大变化时,区块的边距也应该变化

  • 能够适应比例变化的图片,对于常见的宽度调整,图片在隐去两侧部分时,依旧保持美观可用

  • 能够自动隐藏或是显示部分的内容,如在电脑上显示的的大段描述文本,在手机上就只能少量显示或全部隐藏

  • 能自动折叠的导航和菜单,展开还是收起,应该根据页面尺寸来判断

  • 放弃使用像素作为尺寸单位,用 dp(对于前端来说,这里可能是 em/rem)尺寸等方法来确保页面在分辨率相差很大的设备上,看起来也能保持一致,同时也要求提供的图片应该比预想的更大,才能适应高分辨率的屏幕

上面一段我觉得已经涵盖了响应式设计的绝大部分,简单总结起来,可以概括为

  • 媒体查询,边界断点的规则设定(Media queries && break point
  • 内容的可伸缩性效果(Flexibel visuals
  • 流式网格布局(Fluid grids
  • 主要内容呈现及图片的高质量(Main content and high quality

响应式和自适应

响应式设计是 Responsive Web DesignRWD),自适应设计是 Adaptive Web DesignAWD),经常有人会将两者混为一谈,或者其实根本也区分不了所谓的响应式与自适应

RWDAWD 两者都是为了适配各种不同的移动设备,致力于提升用户体验所产生的的技术,核心思想是用技术来使网页适应从小到大(现在到超大)的不同分辨率的屏幕,通常认为 RWDAWD 的子集

  • RWDEthan Marcote 的文章是大家认为 RWD 的起源,他提出的 RWD 方案是通过 HTMLCSS 的媒体查询技术,配合流体布局实现,RWD 倾向于只改变元素的外观布局,而不大幅度改变内容,Jeffrey Zeldman 总结说,我们就把 RWD 定义为一切能用来为各种分辨率和设备性能优化视觉体验的技术
  • AWDAdaptive DesignAaron Gustafson 的书的标题,他认为 AWD 在包括 RWDCSS 媒体查询技术以外,也要用 JavaScript 来操作 HTML 来更适应移动设备的能力,AWD 有可能会针对移动端用户减去内容,减去功能,AWD 可以在服务器端就进行优化,把优化过的内容送到终端上

一图胜千言

从定义上而言 RWD 是一套代码,适用于所有屏幕,而 AWD 则是多端多套代码,本文不会过多去纠结响应式与自适应区别,这两者的本质都是致力于适配不同设备,更好地提升用户体验,更多相关内容可以参考下面两个链接

渐进增强和优雅降级

  • 渐进增强(progressive enhancement),针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验
  • 优雅降级(graceful degradation),一开始就构建完整的功能,然后再针对低版本浏览器进行兼容

两者的区别是,优雅降级是从复杂的现状开始,并试图减少用户体验的供给,而渐进增强则是从一个非常基础的,能够起作用的版本开始,并不断扩充,以适应未来环境的需要,降级意味着往回看,而渐进增强则意味着朝前看,同时保证其根基处于安全地带

渐进增强和优雅降级通常是 AWD 会牵扯到的另一个技术术语,本质上而言即是随着屏幕的大小的改变,功能会一点一点增强,也通常会用在一些高级 CSS3 属性上,我们对一些 CSS 属性进行特性检测,甚至不进行特性检测直接使用,后果是在支持它的网页上该属性正常展示,而不支持它的网页该属性不生效,但也不影响用户的基本使用

典型的例子是 CSS3 逐渐被大众认可并被使用,PC 端页面开始由低版本的 IE 向兼容性更好的 IE 9+ChromeFirefox 浏览器转变的时期,我们可以对页面元素直接使用阴影,圆角等属性,对于不支持它的低版本 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)设备像素比

有了上面两个概念以后,我们就可以顺理成章引出下一个概念,DPRDevice Pixel Ratio)设备像素比,这个与我们通常说的视网膜屏(多倍屏,Retina 屏)有关

设备像素比描述的是未缩放状态下,物理像素和设备独立像素的初始比例关系,简单的计算公式是下面这样的

DPR = 物理像素 / 设备独立像素

我们套用一下上面 iPhone 7 的数据(取设备的物理像素宽度与设备独立像素宽度进行计算),可以发现

iPhone 7DPR = iPhone 7 的物理像素宽度 / iPhone 7 的设备独立像素宽度 = 2

其实也就是 750 / 375 = 2 或者是 1334 / 667 = 2,所以我们可以得到 iPhone 7dpr2,这也就是我们常说的『视网膜屏幕』,而视网膜(Retina)屏幕是苹果公司的一个营销术语,简单来说就是苹果公司将 dpr > 1 的屏幕称为视网膜屏幕

在视网膜屏幕中,以 dpr = 2 为例,把 42 x 2)个像素当 1 个像素使用,这样让屏幕看起来更精致,但是元素的大小本身不会改变

下面我们再来看看 iPhone XS Max

它的物理像素如上图是 2688 x 1242

而它的 CSS 像素是 896 x 414,所以我们可以很容易得出 iPhone XS Maxdpr3

至此,我们也算是完成了一个小的里程碑,我们通常说的 H5 手机适配其实也就是指的这两个维度

  • 适配不同屏幕大小,也就是适配不同屏幕下的 CSS 像素
  • 适配不同像素密度,也就是适配不同屏幕下 dpr 不一致导致的一些问题

当然这里还有一些其他比较重要的概念,比如 PPIDPI 等,但是我们就不详细展开了,可以自行去加深理解

适配不同屏幕大小

通常我们在移动端开发过程当中,都会添加一个移动端专用的 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 满足上面说的,可以是一个全局性的基准单位

remfont size of the root element)在 CSS Values and Units Module Level 3 中的定义就是,根据网页的根元素来设置字体大小,和 emfont 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 作为屏幕宽度适配解决方案,是存在一些问题的

  1. 动态修改 viewport 是存在一定的风险的,譬如通过 viewport 改变了页面的缩放之后,获取到的 innerWidth/innerHeight 也会随之发生变化,如果业务逻辑有获取此类高宽进行其他计算的,可能会导致意想不到的错误(其实到今天,其实存在很多在 flexible 基础上演化而来的各种 rem 解决方案,有的不会对 viewport 进行缩放处理,自行处理 1px 边框问题)
  2. flexible/hotcss 都并非纯 CSS 方案,需要引入一定的 JavaScript 代码
  3. rem 的设计初衷并非是用于解决此类问题,用 rem 进行页面的宽度适配多少有一种 hack 的感觉
  4. 存在一定的兼容性问题,对于安卓 4.4 以下版本系统不支持 viewport 缩放(当然 flexible 处理 Android 系列时,始终认为其 dpr1,没有进行 viewport 缩放)

vw 适配方案

严格来说,使用 rem 进行页面适配其实是一种 hack 手段,rem 单位的初衷本身并不是用来进行移动端页面宽度适配的,到了今天有了一种更好的替代方案,那就是使用 vw 进行适配,也就是『百分比适配』

百分比适配方案的核心需要一个全局通用的基准单位,rem 是不错,但是需要借助 JavaScript 进行动态修改根元素的 font-size,而 vw/vhvmax/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
2
3
4
5
6
// 假设设计稿的宽度是 375px,假设取设计稿宽度下 1rem = 100px
$baseFontSize: 100;

@function px2rem($px) {
@return $px / $baseFontSize * 1rem;
}

同理,在 vw 方案下,我们只需要去改写这个方法

1
2
3
4
// 假设设计稿的宽度是 375px
@function px2vw($px) {
@return $px / 375 * 100vw;
}

当然,我们还可以借助一些插件包去实现这个自动转换,提高效率,譬如 postcss-px-to-viewport

vw polyfill

但是 vw 目前毕竟还是存在兼容问题的,如下

看上去其实已经覆盖了绝大部分设备,但是如果在业务使用中又真的出现了兼容问题,应该怎么处理呢?通常来说,有下面两种方式可以进行降级处理

  • CSS Houdini,通过 CSS Houdini 针对 vw 做处理,调用 CSS Typed OM Level1 提供的 CSSUnitValue API
  • CSS Polyfill,通过相应的 Polyfill 做相应的处理,目前针对于 vw 单位的 Polyfill 主要有 vminpolyViewport Units Buggyfillvunits.jsModernizr
对于 vw 方案的一些总结

vw 确实看上去很不错,但是也是存在它的一些问题

  • 也没能很好的解决 1px 边框在高清屏下的显示问题,需要自行处理
  • 由于 vw 方案是完全的等比缩放,在完全等比还原设计稿的同时带来的一个问题是无法很好的限定一个最大最小宽度值,由于 rem 方案是借助 JavaScript 的,所以这一点 remvw 会更加的灵活

当然,两个方案现阶段其实都可以使用甚至一起搭配使用,更多详情可以参考下面几个链接

1px 边框

上面我们说到使用 vw 适配屏幕大小方案,其中有一个缺点就是在 Retina 屏下,无法很好的展示真正的 1px 物理像素线条,设计师想要的 Retinaborder: 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.5px0.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 = 1dpr = 2 的设备上,也能非常好的展示图片

当然这样并不可取,会造成大量带宽的浪费,并且现代浏览器,提供了更好的方式,让我们能够根据设备 dpr 的不同,提供不同尺寸的图片

srcset 配合 1x 2x 像素密度描述符

简单来说,srcset 可以根据不同的 dpr 拉取对应尺寸的图片

1
2
3
4
5
6
7
<div class='illustration'>
<img
src='illustration-small.png'
srcset='images/illustration-small.png 1x, images/illustration-big.png 2x'
style='max-width: 500px'
/>
</div>

上面 srcset 里的 1x2x 表示『像素密度描述符』,简单来说就是

  • 当屏幕的 dpr = 1 时,使用 images/illustration-small.png 这张图
  • 当屏幕的 dpr = 2 时,使用 images/illustration-big.png 这张图

srcset 属性配合 sizes 属性 w 宽度描述符

上面的 1x2x 的写法比较容易接受易于理解,但是除此之外,srcset 属性还有一个 w 宽度描述符,配合 sizes 属性一起使用可以覆盖更多的面,比如下面这个示例

1
2
3
4
5
<img
sizes="(min-width: 600px) 600px, 300px"
src="photo.png"
srcset="photo@1x.png 300w, photo@2x.png 600w, photo@3x.png 1200w"
/>

其中 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" 里面的 300w600w900w 叫宽度描述符,那么我们该如何确定当前场景会选取哪张图片呢?

当前屏幕 dpr = 2,CSS 宽度为 375px

当前屏幕 CSS 宽度为 375px,则图片 CSS 宽度为 300px,分别用上述 3 个宽度描述符的数值除以 300

  • 300 / 300 = 1
  • 600 / 300 = 2
  • 1200 / 300 = 4

上面得到的 124 即是算出的有效的像素密度,换算成和 x 描述符等价的值,这里 600w 算出的 2 即满足 dpr = 2 的情况选择此张图

当前屏幕 dpr = 3,CSS 宽度为 414px

当前屏幕 CSS 宽度为 414px,则图片 CSS 宽度仍为 300px,再计算一次

  • 300 / 300 = 1
  • 600 / 300 = 2
  • 1200 / 300 = 4

因为 dpr = 32 已经不满足了,则此时会选择 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

如果小于最小字体,那么字体默认就是最小字体,其次很多早期的文章规范都建议不要使用奇数级单位来定义字体大小(如 13px15px 等),容易在一些低端设备上造成字体模糊,出现锯齿

字体的选择展示

在字体适配上面,我们需要从『性能』和『展示效果』两个维度去考虑,完整的一个字体资源实在太大了,所以我们应该尽可能的使用用户设备上已有的字体,而不是额外去下载字体资源,从而使加载时间明显加快

而从展示效果层面来说,使用系统字体能更好的与当前操作系统使用的相匹配,得到最佳的展示效果,所以我们在字体使用方面,有一个应该尽量去遵循的原则,也是现在大部分网站在字体适配上使用的策略,那就是『尽量去使用各个支持平台上的默认系统字体』

兼顾各个操作系统

我们通常比较常见的操作系统有 WindowsWindows PhoneMac OS XiPhoneAndroid PhoneLinux 等,当然对于普通用户而言,无须关注 Linux 系统,我们下面就以 CSS-Trick 网站最新的 font-family 为例,看看是如何在字体选择上做到适配各个操作系统的

1
2
3
4
{
font-family: system-ui, -apple-system, BlinkMacSystemFont, segoe ui, Roboto,
Helvetica, Arial, sans-serif, apple color emoji, segoe ui emoji, segoe ui symbol;
}

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 个,也就是我们熟知的几个通用字体族名

  1. serif,衬线字体族
  2. sans-serif,非衬线字体族
  3. monospace,等宽字体,即字体中每个字宽度相同
  4. cursive,草书字体
  5. fantasy,主要是那些具有特殊艺术效果的字体

新增通用字体族关键字

而在 CSS Fonts Module Level 4 – Basic Font Properties 中,新增了几个关键字

  1. system-ui,系统默认字体
  2. emoji,用于兼容 emoji 表情符号字符
  3. math,适用于数学表达式
  4. fangsong,此字体系列用于中文的(仿宋)字体

下面我们还是以上面的 font-family 为例,看看它们几者之间的区别

system-ui

简单而言,font-family: system-ui 的目的就是在不同的操作系统的 Web 页面下,自动选择本操作系统下的默认系统字体

默认使用特定操作系统的系统字体可以提高性能,因为浏览器或者 WebView 不必去下载任何字体文件,而是使用已有的字体文件,font-family: system-ui 字体设置的优势之处在于它与当前操作系统使用的字体相匹配,对于文本内容而言,它可以得到最恰当的展示

San Francisco Fonts

我们在上面简单的了解了 system-ui 字体族,但是像 -apple-systemBlinkMacSystemFont 并没有在最新的标准里出现,那它们又代表什么意思呢?

在此之前,我们需要先了解一下 San Francisco FontsSan Francisco Fonts 又叫旧金山字体,是一款西文字体,随着 iOS 9 更新面世,在 WatchOS 中随 Apple Watch 一起悄然发售,并且还将在 Apple TV 上的新 tvOS 中使用

San Francisco FontsiOS 系统上用于替代升级另外一款西文字体 Helvetica NeueApple 做了一些重要的改变,使其成为平台上更好的,甚至是完美的西文字体

-apple-system/BlinkMacSystemFont

话说回来,正如每个前端开发人员都知道的那样,将一个功能纳入规范是一回事,将其纳入浏览器又是另一回事,幸运的是 system-ui 的普及很快,ChromeSafari 都可以在各种平台上完全支持它,只有 MozillaWindows 相对落后,我们来看看 system-ui 的兼容性

仔细看上图的最后两行

  • Supported as the -apple-system valueonly on macOS and iOS
  • Supported as the BlinkMacSystemFont valueonly on macOS

考虑到不同平台及向后兼容,在 macOSiOS 上,我们需要使用 -apple-systemBlinkMacSystemFont 来兼容适配 system-ui 标准

Segoe UI

Segoe UIWindowsVista 开始的默认西文字体族,只有西文,不支持汉字,属于无衬线体,它也表示一个系列而不是某一款字体

使用 font-family: Segoe UI 可以在 Windows 平台及 Windows Phone 上选取最佳的西文字体展示

Roboto

Roboto 是为 Android 操作系统设计的一个无衬线字体家族,Google 描述该字体为现代的、但平易近人和有感情的

这个字体家族包含 ThinLightRegularMediumBoldBlack 六种粗细及相配的斜体

总结

到此,我们可以总结一下了,还是以上面 CSS-Trick 网站的 font-family 定义为例子

1
2
3
4
{
font-family: system-ui, -apple-system, BlinkMacSystemFont, segoe ui, Roboto,
Helvetica, Arial, sans-serif, apple color emoji, segoe ui emoji, segoe ui symbol;
}
  1. system-ui,使用各个支持平台上的默认系统字体
  2. -apple-system, 在一些稍低版本 Mac OS XiOS 上,它针对旧版上的 Neue HelveticaLucida Grande 字体,升级使用更为合适的 San Francisco Fonts
  3. BlinkMacSystemFont,针对一些 Mac OS X 上的 Chrome 浏览器,使用系统默认字体
  4. segoe ui,在 WindowsWindows Phone 上选取系统默认字体
  5. Roboto,面向 Android 和一些新版的的 Chrome OS
  6. Helvetica/Arial,在针对不同操作系统不同平台设定采用默认系统字体后,针对一些低版本浏览器的降级方案
  7. sans-serif,兜底方案,保证字体风格统一,至少也得是无衬线字体

上述 5 个字体族定义,优先级由高到底,可以看到,它们 5 个都并非某个特定字体,基本的核心思想都是选择对应平台上的默认系统字体,涵盖了 iOSMAC OS XAndroidWindowsWindows 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;

对比可以发现,它们总体遵循了这样一个基本原则

  1. 尽量使用系统默认字体

使用系统默认字体的主要原因是性能,并且系统字体的优点在于它与当前操作系统使用的相匹配,因此它的文本展示必然也是一个让人舒适展示效果

  1. 兼顾中西,西文在前,中文在后

中文或者西文(英文)都要考虑到,由于大部分中文字体也是带有英文部分的,但是英文部分又不怎么好看,但是英文字体中大多不包含中文,通常会先进行英文字体的声明,选择最优的英文字体,这样
不会影响到中文字体的选择,中文字体声明则紧随其次

  1. 兼顾多操作系统

选择字体的时候要考虑多操作系统,例如 MAC OS 下的很多中文字体在 Windows 都没有预装,为了保证 MAC 用户的体验,在定义中文字体的时候,先定义 MAC 用户的中文字体,再定义 Windows 用户的中文字体

  1. 兼顾旧操作系统,以字体族系列 serifsans-serif 结尾

当使用一些非常新的字体时,要考虑向下兼容,兼顾到一些极旧的操作系统,使用字体族系列 serifsans-serif 结尾总归是不错的选择

暂时就总结这么多,更多的关于字体方面的细节知识,可以参考下面这几个链接

前端布局的兼容适配

最后我们再来简单的了解一下前端布局的兼容适配,因为前端工程师的一大工作内容就是页面布局,无论在 PC 端还是移动端,页面布局的兼容适配都是重中之重,在整个前端发展的历程中,布局的方法也在不断的推陈出新

布局发展历程

简单来说,前端的布局发展历程经历了下面几个过程

表格布局 ==> 定位布局 ==> 浮动布局 ==> flexbox 布局 ==> gridbox 布局

每一种布局在特定时期都发挥了重要的作用,而每一种新的布局方式的出现,往往都是因为现有的布局方式已经在该时期已经无法很好的满足开发者的需求,无法满足越来越潮流的页面布局的方式,以 Flexbox 的出现为例子,在 Flexbox 被大家广为接受使用之前,我们一直在使用定位+浮动的布局方式,像下面这个布局

容器宽度不定,内部三个元素,均分排列且占满整个空间,并且垂直居中,如果使用定位加浮动的布局方式,你无法很快想到最佳的解决方式,三个元素并排那么必然需要浮动或者绝对定位,容器宽度不定且中间元素始终居中,需要顾虑的方面就很多了,也许使用 text-align: justufy 可以 hack 实现等等,然而使用 flexbox 布局的话,只需要下面三行代码就可以了

1
2
3
4
5
.container {
display: flex;
justify-content: space-between;
align-items: center;
}

flexbox 的出现,一次性解决了流动布局,弹性布局,排列方式等多个问题,并且它是简洁的,可控的,再来看一个例子,水平垂直居中一个元素,使用 flexbox 也许是最便捷的,线上预览地址可见 CodePen

1
2
3
4
5
6
7
.container {
display: flex;
}

.item {
margin: auto;
}

CSS Grid Layout

flexbox 已经足够优秀了,为什么 gird 网格布局的出现又是为什么?它又解决了哪些 flex 布局无法很好解决的问题呢?先来看看下面这张图

flexbox 是一维布局,他只能在一条直线上放置你的内容区块,而 grid 是一个二维布局,它除了可以灵活的控制水平方向之外,还能轻易的控制垂直方向的布局模式,对于上图那样的九宫格布局,它就可以轻而易举的完成,如果用图片来进行表示的话,flexbox 是下面这样的

gridbox 则是

在现阶段,移动端布局应当更多使用 flexbox 去完成(相对那些还在使用 float 布局的),而考虑到未来页面布局的推陈出新,对于 Grid 布局我们应当像前几年对待 flexbox 一样,重视起来,随着兼容性的普及,Grid 布局也会慢慢成为主流

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×