现象
分析 WEB用到了自定义字体,DOM解析渲染时,字体还未加载完全,立即使用缺省字体显示,字体加载完成后,重新渲染替换
手段 明确了问题,就着手优化解决,改进点有以下几方面
样式抽离 当前WEB样式是打包进JS,每次是动态插入Style到header部分,所以较慢。另外JS变动实际上造成指纹【即内容hash】变化,样式就连代受影响,无法缓存,所以抽离还是有益于性能提升。
Webpack loader及plugins需要增加以下配置
1 2 3 4 5 6 7 8 { test : /\.less$/ , use : [ { loader : MiniCssExtractPlugin .loader }] }
1 2 3 4 5 new MiniCssExtractPlugin ({ filename : 'static/css/[name].[hash].css' , chunkFilename : 'static/css/[name].[hash].css' })
预加载 注意
预加载,使字体不至于在CSS解析时才开始加载,提升字体资源加载的优先级
考虑当前开发的WEB,目标浏览器为Chrome,IE11+,Safari等,这里我只加载woff2,和woff,要知道体积大小上,woff2 < woff < tff,
因为woff2
在IE下不支持,所以需要配置woff
贴下配置 1 2 3 <link rel ="preload" as ="font" type ="font/woff2" href ="static/font/Lato-Regular.woff2" > <link rel ="preload" as ="font" type ="font/woff2" href ="static/font/Lato-Bold.woff2" >
1 2 3 4 5 6 7 8 9 10 11 @font-face { font-family : 'Lato-Regular' ; src : url('../static/fonts/Lato-Regular.woff2' ) format ('woff2' ), url('../static/fonts/Lato-Regular.woff' ) format ('woff' ); font-display : auto; } @font-face { font-family : 'Lato-Bold' ; src : url('../static/fonts/Lato-Bold.woff2' ) format ('woff2' ), url('../static/fonts/Lato-Bold.woff' ) format ('woff' ); font-display : auto; }
注意如果字体加载跨域Link标签需要配置 crossorigin
,比如crossorigin=”anonymous”,否则预加载即使成功,但安全考虑因此字体并不会真正应用,因此最终字体会因为预加载及CSS中的font-face加载两次。
字体文件体积 如上预加载可以分离CSS的解析与字体资源的加载,但字体文件过大也是个问题,为此进行如上配置,优先下载woff2,对IE才会下载woff,ttf不考虑。
关于不同格式字体的体积差异,比如labo-regular
woff-309KB
woff2-183KB
ttf-608KB
此外,还有2个手段可进一步缩小字体体积
base编码字体文件,也就是合并了字体与样式,减少请求。这里我并采用,但是个手段
采用子集内嵌,因为字体文件中包含的字符编码并不是都用的上
字体显示策略 假如字体文件加载耗时在3秒可接受范围内,浏览器在字体加载还没完成时,可以以缺省字体显示内容,也可以先隐藏,3秒后,字体文件如果下载完成,则自定义字体显示文本,如果没有则,使用后备字体显示,等字体文件下载完成,再交换。
字体切换会出现闪烁效果,所以这里我将显示策略改为auto,,即走默认策略,3秒内还没加载完字体,文本不可见。
如使用swap,则字体加载的同时,会立即以默认字体展示,进而会有可视化看到字体变化的效果,一开始的问题便是这个原因。
但并不是swap就一定不好,具体使用哪个策略,视情况而定。
auto,block确保了不会出现字体变化的问题,但文本不可见的时间越长体验越差,所以要持续优化加载体验,比如CDN等。
相关术语 在网页性能这块实际上也是个重要的课题,比如就有以下这样的术语
FOUC (flash of unstyled text)
网页渲染时,外部样式还没加载好,就以浏览器默认样式短暂地展示了部分内容,等到外部样式加载完成,又恢复正常的这个页面闪烁的过程
今天的WEB如果处理好资源加载顺序,这个问题是完全可以避免的。
浏览器加载HTML,CSS,JS顺序
如上一开始的问题直接原因只是字体的显示策略需要调整,同时提升字体下载速度,但背后却牵引出浏览器在加载资源时的流程处理,于是,这里梳理下。
这里以一个实际的SPA index页面为例,浏览器加载到解析渲染的流程如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <!doctype html > <html class ="no-js" lang ="en" dir ="ltr" > <head > <base href ="/" /> <meta charset ="utf-8" > <meta http-equiv ="X-UA-Compatible" content ="IE=edge" > <meta http-equiv ="Cache-Control" content ="no-cache" /> <meta http-equiv ="Pragma" content ="no-cache" > <meta http-equiv ="Expires" content ="0" > <title > loading</title > <meta name ="description" content ="Description for quotation" > <meta name ="google" value ="notranslate" > <meta name ="viewport" content ="width=device-width, initial-scale=1, shrink-to-fit=no" > <meta name ="theme-color" content ="#000000" > <link rel ="preload" as ="font" href ="static/font/Lato-Regular.ttf" > <link rel ="preload" as ="font" href ="static/font/Lato-Bold.ttf" > <link rel ="shortcut icon" href ="favicon.ico" /> <link rel ="manifest" href ="manifest.webapp" /> <link rel ="stylesheet" href ="static/css/antd.min.css" > <style type ="text/css" > .browser-upgrade { margin : 0.2em 0 ; background : #ccc ; color : #000 ; padding : 0.2em 0 ; } </style > <link href ="static/css/vendors.2a1bd1c1e210b6a1697e.css" rel ="stylesheet" > <link href ="static/css/main.2a1bd1c1e210b6a1697e.css" rel ="stylesheet" > </head > <body > <div id ="root" > </div > <script type ="text/javascript" src ="app/vendors.2a1bd1c1e210b6a1697e.chunk.js" > </script > <script type ="text/javascript" src ="app/main.2a1bd1c1e210b6a1697e.bundle.js" > </script > <script src ="static/js/stomp.min.js" defer > </script > <script src ="static/js/sockjs-1.0.0.min.js" defer > </script > </body > </html >
有错,欢迎指出
补充 浏览器对CSS阻塞渲染有两种处理方式,要么等css解析完一起渲染,Chrome就是这么做的,但是会造成白屏;要么先把无样式的dom渲染出来等css解析好了再渲染一次,Firefox就是这么做的,但是会造成无样式内容闪烁
写在最后 不得不说浏览器相关的知识点还是挺琐碎,复杂的。。。共勉。
参考文档