前端魔法堂:解秘FOUC

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介:

 对于问题多多的IE678,FOUC(flash of unstyled content)——浏览器样式闪烁是一个不可忽视的话题,但对于ever green的浏览器就不用理会了吗?下面尝试较全面地解密FOUC。

到底什么是FOUC?

 页面加载解析时,页面以样式A渲染;当页面加载解析完成后,页面突然以样式B渲染,导致出现页面样式闪烁。
样式A,浏览器默认样式 或 浏览器默认样式 层叠 部分已加载的页面样式;
样式B,浏览器默认样式 叠加 全部页面样式。

为什么会出现FOUC

 我们了解当输入网址按回车后浏览器会向服务器发送请求,然后服务器返回页面给浏览器,浏览器边下载页面边解析边渲染。
下面我们解剖一下边下载页面边解析边渲染的过程:

  1. 边下载边解析就是边下载html边构建DOM Tree;
  2. 浏览器以user agent stylesheet(浏览器内置样式)为原料构建CSSOM Tree;
  3. DOM Tree+CSSOM Tree构建出Render Tree,然后页面内容渲染出来;
  4. 当解析到inline stylesheet 或 internal stylesheet时,马上刷新CSSOM Tree,CSSOM Tree或DOM Tree发生变化时会引起Render Tree变化;
  5. 当解析到external stylesheet时就先加载,然后如internal stylesheet那样解析和刷新CSSOM Tree和Render Tree了。
    上述步骤5中由于样式文件存在下载这个延时不确定的阶段,因此网络环境不好或样式资源体积大的情况下我们可以看到样式闪烁明显。
    这就是为什么我们将external stylesheet的引入放在head标签中的原因,在body渲染前先把相对完整的CSSOM Tree构建好。但大家都听说过script会阻塞html页面解析(block parsing),而link不会,那假如网络环境不好或样式资源体积大时,body已经解析并加入到DOM Tree后,external stylesheet才加载完成,不是也会造成FOUC吗?

    style,link等样式资源的下载、解析确实不会阻塞页面的解析,但它们会阻塞页面的渲染(block rendering)。

Block Parsing 和 Block Rendering的区别

Block Parsing: 阻塞HTML页面解析,HTML页面会被继续下载,但阻塞点后面的标签不会被解析,img,link等不会发请求获取外部资源。
Block Rendering:阻塞HTML页面渲染,HTML页面会被继续下载,阻塞点后面的标签会继续被解析,img,link等会继续发送请求获取外部资源,但不会合成Rendering Tree或不会触发页面渲染,也不会执行JavaScript代码。
各浏览器这方面还有一点差异:

对于Chrome

<link rel="stylesheet">,<link rel="import"> and @import url("<url>")会阻塞渲染。
示例1:阻塞解析

<html>
  <body>
    <script>
            // 打印出 null
      console.log(document.getElementById('hi'))
    </script>
    <script src="./longtime.js"></script>
    <div id="hi">Hi</div>
  </body>
</html>

示例2:阻塞渲染

<html>
  <body>
    <script>
            // 打印出 <div id="hi">Hi</div>
      console.log(document.getElementById('hi'))
    </script>
    <link rel="stylesheet" href="./longtime.css">
    <div id="hi">Hi</div>
  </body>
</html>

示例3:阻塞渲染

<html>
  <head>
    <script>
            // 打印出 hinull
      console.log('hi' + document.getElementById('hi'))
            // 打印出 hiscript#s
      console.log('s' + document.getElementById('s'))
    </script>
    <link rel="stylesheet" href="./longtime.css">
    <script id="s"></script>
  </head>
  <body>
    <div id="hi">Hi</div>
  </body>
</html>

示例4:阻塞渲染

<html>
  <body>
        <!-- div#hi在 ./longtime.css下载完前不会被渲染 -->
    <style>#hi{color:red;}</style>
    <link rel="stylesheet" href="./longtime.css">
    <div id="hi">Hi</div>
  </body>
</html>

示例2说明,如果阻塞渲染发生在body标签内,那么body及其子元素会继续解析并追加到DOM Tree中;
示例3说明,如果阻塞渲染发生在head标签内,那么body及其子元素不会被追加到DOM Tree中。
示例4说明,不管external stylesheet在哪里引入,在页面的所有external stylesheets下载完成前(DOMContentLoaded后才渲染),整个页面将不会被渲染。(估计Chrome会预先统计external stylesheet的数量)

对于FireFox

示例1:阻塞渲染

<html>
  <body>
        <!-- div#hi的文字显示为红色,待./longtime.css下载完后又渲染为其他颜色 -->
    <style>#hi{color:red;}</style>
    <link rel="stylesheet" href="./longtime.css">
    <div id="hi">Hi</div>
  </body>
</html>

示例2:阻塞渲染

<html>
  <head>
        <!-- div#hi不显示,直到./longtime.css下载完后 -->
    <style>#hi{color:red;}</style>
    <link rel="stylesheet" href="./longtime.css">
  </head>
  <body>
    <div id="hi">Hi</div>
  </body>
</html>

对于IE9

示例1:

<html>
  <body>
        <!-- div#hi没有渲染,也没有加入到DOM Tree中 -->
    <style>#hi{color:red;}</style>
    <link rel="stylesheet" href="./longtime.css">
    <div id="hi">Hi</div>
  </body>
</html>

示例2:

<html>
  <body>
        <!-- div#hi渲染了,加入到DOM Tree中 -->
    <style>#hi{color:red;}</style>
    <div id="hi">Hi</div>
    <link rel="stylesheet" href="./longtime.css">
  </body>
</html>

上面的示例表明,IE下block rendering等价于block parsing,因为连img,script,link,@import url()资源请求都会被阻塞。

解决方法

 现在我们知道FOUC时由于页面采用临时样式来渲染页面而导致的,其中仅有chrome能好的屏蔽了这一点,而其他浏览器就呵呵了。那有什么方案可以解决呢?其实我们的目的就是不要让用户看到临时样式,那么我们可以隐藏body,当样式资源加载完成后再显示body

<html class="no-js">
    <style>
        /*modernizr会将html的no-js替换为js,并将modernizr代码在最后时加载,那么就能保证所有样式文件已经加载完成*/
        .no-js body{display: none!important;}
    </style>
    <body>
        <script src="modernizr.js"></script>
    </body>
</html>

(编译modernizr时记得勾setClasses哦,否则不会替换no-js的!)

总结

 上述方案虽然解决了FOUC的问题,但很明显地延长了首屏白屏时间,当前较流行的App Shell(可以理解为先显示页面布局的骨架或一幅图片)也会失效,所以对于2C的应用仅仅采用上述的方案效果并不理想。后续待我研究好后再追加一篇吧^^
尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/6739064.html ^
^肥仔John

感谢

Flash of unstyled content
The FOUC Problem
Critical rendering path

如果您觉得本文的内容有趣就扫一下吧!捐赠互勉!

分类: CSS, JavaScript
0
0
« 上一篇: JS魔法堂:深究JS异步编程模型
» 下一篇: 前端魔法堂:onsubmit和submit事件处理函数怎么不生效呢?
posted @ 2017-04-20 15:37 ^_^肥仔John 阅读( 322) 评论( 0) 编辑 收藏
 
相关文章
|
2月前
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
141 2
|
2月前
|
JavaScript 前端开发 程序员
前端学习笔记——node.js
前端学习笔记——node.js
44 0
|
2月前
|
人工智能 自然语言处理 运维
前端大模型应用笔记(一):两个指令反过来说大模型就理解不了啦?或许该让第三者插足啦 -通过引入中间LLM预处理用户输入以提高多任务处理能力
本文探讨了在多任务处理场景下,自然语言指令解析的困境及解决方案。通过增加一个LLM解析层,将复杂的指令拆解为多个明确的步骤,明确操作类型与对象识别,处理任务依赖关系,并将自然语言转化为具体的工具命令,从而提高指令解析的准确性和执行效率。
|
2月前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
2月前
|
机器学习/深度学习 弹性计算 自然语言处理
前端大模型应用笔记(二):最新llama3.2小参数版本1B的古董机测试 - 支持128K上下文,表现优异,和移动端更配
llama3.1支持128K上下文,6万字+输入,适用于多种场景。模型能力超出预期,但处理中文时需加中英翻译。测试显示,其英文支持较好,中文则需改进。llama3.2 1B参数量小,适合移动端和资源受限环境,可在阿里云2vCPU和4G ECS上运行。
|
2月前
|
前端开发 算法 测试技术
前端大模型应用笔记(五):大模型基础能力大比拼-计数篇-通义千文 vs 文心一言 vs 智谱 vs 讯飞vsGPT
本文对比测试了通义千文、文心一言、智谱和讯飞等多个国产大模型在处理基础计数问题上的表现,特别是通过链式推理(COT)提示的效果。结果显示,GPTo1-mini、文心一言3.5和讯飞4.0Ultra在首轮测试中表现优秀,而其他模型在COT提示后也能显著提升正确率,唯有讯飞4.0-Lite表现不佳。测试强调了COT在提升模型逻辑推理能力中的重要性,并指出免费版本中智谱GLM较为可靠。
前端大模型应用笔记(五):大模型基础能力大比拼-计数篇-通义千文 vs 文心一言 vs 智谱 vs 讯飞vsGPT
|
3月前
|
SpringCloudAlibaba JavaScript 前端开发
谷粒商城笔记+踩坑(2)——分布式组件、前端基础,nacos+feign+gateway+ES6+vue脚手架
分布式组件、nacos注册配置中心、openfegin远程调用、网关gateway、ES6脚本语言规范、vue、elementUI
谷粒商城笔记+踩坑(2)——分布式组件、前端基础,nacos+feign+gateway+ES6+vue脚手架
|
4月前
|
存储 前端开发 JavaScript
前端语言串讲 | 青训营笔记
前端语言串讲 | 青训营笔记
42 0
|
6月前
|
JSON 前端开发 JavaScript
前端Ajax、Axios和Fetch的用法和区别笔记
前端Ajax、Axios和Fetch的用法和区别笔记
99 2
|
6月前
|
前端开发 JavaScript 数据库
如何实现前后端分离-----前端笔记
如何实现前后端分离-----前端笔记
下一篇
无影云桌面