一、拯救移动端图标 —— SVG
常见的字体方案经历了三种:PNG、Iconfont、SVG。
(一)PNG
先说说PNG,是比较早的方案了。PNG 属于一种图片格式,颜色丰富、边缘平滑,而且还支持透明度,所以最早被设计师作为 Icon 输出的格式,从而沿用到前端代码中;但这种图片icon也存在很大的缺陷:
1、尺寸问题,必须关注图片的宽高、比例,以免导致失真、变形。
2、请求数量和体积问题,多个图片Icon需要通过雪碧图等技术来规避请求数量,而雪碧图的应用还需要关心icon的定位。
3、灵活性,比较难以使用CSS3中的一些属性,效果不佳。比如:hover、阴影、transform、filter。
(二)Iconfont
Iconfont 是将图标制作成为了字体,从而利用上了字体的诸多优点,基本上能够规避 PNG 大部分缺点:
1、请求数量,只需要考虑字体文件的加载即可。
2、矢量图形足够灵活,像字体能够使用的 CSS 属性皆可使用,从而不必关心宽高、比例等问题(只需设置字体大小即可)。
Iconfont 也存在一个致命问题:Icon 是单色的,或者仅能使用 CSS3 变色。
Iconfont 背后原理是怎样的呢?
1、将Icon制作成字体文件
2、字符是如何被计算机渲染的?通过 i 标签(比较常见),并设置 class 来添加伪元素,而伪元素的值为字体文件中的字形编码,浏览器通过字形编码找到字符并渲染出来的。
字符是如何被计算机渲染的?
绝大多数的字体都包含一个或多个Charmap,它的作用是就是把一个字符从它的字符编码印射到字形索引。
一般一个字符的渲染过程是这样的:
1、加载字体文件
2、确定要输出的字体大小
3、输入这个字符的编码值
4、根据字体文件中的 Charmap,把编码值转换成字形索引(就是这个字符对应字体文件中的第几个形状)
5、根据索引从字体中加载这个字形
6、将这个字形渲染成位图,有可能进行加粗、倾斜等变换。
(三)SVG
SVG 的优点:
1、作为普通的页面元素,更具语义化
2、矢量图形,足够灵活
3、保持图片能力,支持彩色图标
4、节省体积(Iconfont 往往需要一整套字体)
二、最新的布局方案——Grid & Flex
布局经历了几种方案:table布局、传统布局(float、position、display、盒模型)、flex布局、grid布局。
table布局已经彻底淘汰了,传统布局PC端还有一些使用,尤其是CSS响应式;相较于最新的Grid、Flex来说,兼容性最好,但是效率最低,开发起来比较麻烦。
当下用的最多的是flex布局和grid布局。
flex布局移动端用的特别多,学习成本低,开发效率高,兼容性也还不错,它依据某个轴布局,可以看做是一维布局。
grid布局在水平垂直两个方向上同时控制,可以称之为二维布局。Grid布局非常强大,但概念也最多,学习成本高一些,兼容性不如flex。
(一)flex布局
(1)核心概念
flex 容器(Flex Container)
它是所有flex项的直接父元素。
我们把一个元素的display属性值改为 flex 或者 inline-flex后,这个元素就会成为flex容器,而容器中的直接子元素就会变为 flex元素。
flex容器会有一些属性,通过它可以控制flex元素的大小、顺序、对齐以及间隔。
1、flex-direction
flex容器最典型的特征就是拥有水平和垂直方向两个轴,它决定了flex容器的主轴是哪个方向(与主轴相对的是交叉轴),也就是flex项的排列方向。
2、flex-wrap
决定flex项是否换行,默认是不换行的,当容器小于flex项所需要的面积时,flex项会等比例缩小。
3、flex-flow
上面两个属性的简写方式。
4、justify-content
决定flex项目在主轴上的对齐方式。
5、align-items
决定flex项目在交叉轴上的对其方式。
6、align-content
控制“多条主轴”的 flex 项目在交叉轴的对齐。所谓“多条主轴”是指存在换行。
flex 项(Flex Item)
它是flex容器的直接子元素,也有一些属性可以控制flex项:
1、order
定义flex项在排列时的顺序,数值越小优先级越高。
2、flex-grow
决定了flex项宽度或者高度的增长系数,它指定了flex容器中剩余空间的多少应该分配给项目,所谓剩余的空间是指flex容器的大小减去所有flex项的大小加起来的大小。如果所有的兄弟项目都有相同的flex-grow系数,那么所有的项目将获得相同的剩余空间,否则将根据不同的flex-grow系数定义的比例进行分配。
3、flex-shrink
决定了flex容器空间不足时,flex项目的缩小系数,与flex-grow相对。
4、flex-basis
决定了主轴方向上的初始大小,也就是主轴空间。
有以下几点需要注意:
单独使用 flex-basis,其实是定义了最小宽度,当内容撑大时,flex项目也变大。
单独使用width,正常使用。
两者同时使用,会采用较大的宽度,并且内容会溢出。(不设置break-all)
5、flex
属性2、3、4的简写方式。
6、align-self
决定了flex项目在交叉轴上的排列方向,覆盖flex容器中设置的align-items。
(二)grid布局
(1)grid 布局的核心概念
1、网格容器(Grid Container),它是所有网格项的直接父元素
2、网格项(Grid Item),它是网格容器的直接子元素
3、网格线(Grid Line),是组成网格结构的分隔线,分为水平方向和垂直方向
4、网格单元(Grid cell),由网格线将网格容器切分而成
5、网格轨迹(Grid track),可以简单理解为网个容器的行和列
6、网格区域(Grid area),由网格单元组成的块
Grid布局主要有两种元素,即网格容器和网格项,因此就有作用于这两种元素上的属性。
(2)网格容器属性
1、display:grid | inline-grid
grid 中的容器元素都是块级元素,而inline-grid中的容器元素是行内元素。
2、grid-template-columns 和 grid-template-rows
它们分别用来定义网格的行和列,它们的取值用来定义网格的大小,而之间的间隔表示网格线,网格线可以使用中括号进行命名。例如:grid-template-columns: [firstline] 40px [secondline] 40px [thirdline] 40px [endline]。
对于重复的取值可以用 repeat 函数简写,例如:grid-template-columns: 40px 40px 40px等价为repeat(3, 40px)。
再来看看有取值有哪些单位:
传统单位,例如:px、百分比、auto等等
fr,这是网格特有的单位,表示行和列中可用空间的一等份。
3、grid-template-areas,用来定义网格区域。
上述网格容器属性就是将网格分割成行列,而行列又可以组成区域。划分好区域以后,我们就可以利用网格项属性将区域分配给网格项。
4、column-gap、row-gap用来定义行列之间的间隔。
5、justify-items用来定义网格单元的水平对齐方式。
6、align-items用来定义网格单元的垂直对齐方式。
7、place-items 是上述两个属性的简写方式。
8、justify-content用来定义容器内容的水平排布方式。
9、align-content用来定义容器内容的垂直排布方式。
10、place-content是上述两个属性的简写方式。
11、grid-auto-columns为那些隐式列指明宽度。
12、grid-auto-rows为那些隐式行指明高度。
13、grid-auto-flow为那些没有分配区域的网格项指明流动方式,是按行流动还是按列流动。
(3)网格项属性
1、grid-column-start、grid-column-end、grid-row-start、grid-row-end都是描述网格线的,通过这四个属性可以画出一个区域,而这个区域即分配给了网格项。
2、grid-column、grid-row为上述四个属性的简写属性。
3、grid-area为上述四个属性的进一步简写形式,同时其值也可以直接写为区域名称。
4、justify-self指明网格项自身的水平对齐方式。
5、align-self指明网格项自身的垂直对齐方式。
6、place-self是上述两个属性的简写属性。
三、优化资源加载的顺序
1、Preload
2、Prefetch包括资源预加载、DNS预解析、http预连接和页面预渲染。
资源预加载:
DNS预解析:
http预连接: 将建立对该域名的TCP链接
页面预渲染: 将会预先加载链接文档的所有资源
那么Prefetch和Preload有什么区别呢?
具体来讲,Preload来告诉浏览器预先请求当前页需要的资源,从而提高这些资源的请求优先级。比如,对于那些本来请求优先级较低的关键请求,我们可以通过设置Preload来提升这些请求的优先级。
Prefetch来告诉浏览器用户将来可能在其他页面(非本页面)可能使用到的资源,那么浏览器会在空闲时,就去预先加载这些资源放在http缓存内,最常见的dns-prefetch。比如,当我们在浏览A页面,如果会通过A页面中的链接跳转到B页面,而B页面中我们有些资源希望尽早提前加载,那么我们就可以在A页面里添加这些资源Prefetch,那么当浏览器空闲时,就会去加载这些资源。
所以,对于那些可能在当前页面使用到的资源可以利用Preload,而对一些可能在将来的某些页面中被使用的资源可以利用Prefetch。如果从加载优先级上看,Preload会提升请求优先级;而Prefetch会把资源的优先级放在最低,当浏览器空闲时才去预加载。
资源加载优先级可以放在首屏资源优化中,通过首屏那一帧找出对应的关键请求链,然后调整这些资源的加载优先级可以提高首屏加载速度。
另外,首屏速度也可以基于关键请求链做文章,从Localstorage、缓存等角度着手。
四、预渲染优化页面首屏
大型单页应用的性能瓶颈:JS下载 + 解析 + 执行
可以通过 SSR 来优化,但存在问题:需要牺牲 TTFB 来补救 First Paint,实现起来也更加复杂。
可以通过预渲染来解决首屏问题,Pre-rendering 打包时提前渲染页面,没有服务端参与。
五、通过 windowing 优化大型列表的渲染
加载大的列表、大表单的每一行严重影响性能,Lazy loading 仍然会让 DOM 变得很大。
可以参考 react-window 的做法,它仅仅渲染大数据中的部分数据(仅仅填满视窗)的方法,从而解决一些常见的问题:
1、减少初始渲染和数据更新时的节点数量,从而获得一个更好的滚动体验
2、减少内存占用,从而避免大量DOM节点引起的内存泄露
六、使用骨架组件减少布局移动(Layout Shift)
骨架组件(Skeleton/Placeholder)的作用:
1、占位
2、提升用户感知性能
注意:Placeholder要根据要替换的组件进行定制,从而避免 Layout Shift。