H5梳理之核心概念(2)

4、浏览器渲染
这类问题涉及面比较广,很具有梳理的价值。

主流程 (核心脉络)
第一步:根据URL获取到需要渲染的HTML文档
第二步:浏览器解析HTML文档 生成 DOM 树 CSSOM树
第三步:浏览器引擎内部基于DOM 树 CSSOM树生成render Tree
第四步:浏览器通知GPU进行绘制渲染

拓展流程(细节,核心流程中的细节,特殊场景的分叉)
1)阻塞渲染(渲染过程中)
解析html 解析CSS JS即<script>标签的内容的过程中会阻塞掉DOM渲染
所以我们如果希望,渲染速度足够快我们就需要优化 <script> 标签的加载

优化<script>标签的加载
defer 属性(延时执行的任务,渲染完再执行):
当使用 defer 属性时,脚本会并行下载,但会在 DOM 完全构建后顺序执行。这意味着在页面加载时不会阻塞渲染。
可以安全地将 <script> 标签放在 <head> 中。
适用于依赖dom的脚本

async 属性(异步执行的任务,下载完即执行):
使用 async 属性时,脚本会并行下载并立即执行。执行顺序不是固定的,这意味着可能会在 DOM 完全构建之前执行。
适用于不依赖于 DOM 的脚本。

默认情况(阻塞DOM渲染)
TIPs: 这里的情况 简而言之并不会影响最终形态的时间点,任务该用多久还是要用多久渲染。不阻塞DOM渲染,就可以让用户快一点看到页面,提升用户的体验。不是核武器,但是是一种优化规范。

2)重绘repaint与回流reflow(已经渲染完了)
重绘:更改外观,颜色变化了(重绘成本小)
回流:更改布局,盒子大小变化了(回流成本大)

减少触发回流的操作,并用一些操作进行替代
场景1:元素位置改变,例如下移100px,通过top属性会触发回流,transform则不会

document.getElementById('moveTop').addEventListener('click', function() {
    const box = document.getElementById('box');
    box.style.top = '50px'; // 改变 top 属性
});

document.getElementById('moveTransform').addEventListener('click', function() {
    const box = document.getElementById('box');
    box.style.transform = 'translateY(50px)'; // 改变 transform 属性
});

场景2:元素隐藏展示,通过display会触发回流,visibility则不会

const box = document.getElementById('box');

document.getElementById('toggleDisplay').addEventListener('click', function() {
    if (box.style.display === 'none') {
        box.style.display = 'block'; // 显示元素
    } else {
        box.style.display = 'none'; // 隐藏元素
    }
});

document.getElementById('toggleVisibility').addEventListener('click', function() {
    if (box.style.visibility === 'hidden') {
        box.style.visibility = 'visible'; // 显示元素
        box.style.opacity = '1'; // 恢复不透明度
    } else {
        box.style.visibility = 'hidden'; // 隐藏元素
        box.style.opacity = '0'; // 设置不透明度为0
    }
});

场景3:避免频繁获取offsetTop属性,offsetTop获取时,浏览器会执行reflow从而获取到最正确的位置
场景4:经常变更的数据,避免table布局,table改动时,浏览器会执行reflow
场景5:动画速度过快每一帧可能会引发计算布局和样式的回流(reflow),浏览器提供requestAnimationFrame API进行优化

const box = document.getElementById('box');
let startTime;

function animate(time) {
    if (!startTime) startTime = time;
    const progress = time - startTime;

    // 计算新的位置
    const newPosition = Math.min(progress / 10, 200); // 动画速度控制

    box.style.transform = `translateY(${newPosition}px)`;

    if (newPosition < 200) {
        requestAnimationFrame(animate); // 继续动画
    }
}

document.getElementById('startAnimation').addEventListener('click', function() {
    startTime = null; // 重置开始时间
    requestAnimationFrame(animate); // 启动动画
});

场景6: 频繁触发回流,设置图层,避免影响到其他的图层(layer)
利用will-change属性 或者自动图层化的元素<video>、<canvas>

.box {
    position: absolute;
    width: 100px;
    height: 100px;
    background-color: coral;
    top: 0;
    left: 0;
    will-change: transform; /* 提升为图层 */
    transition: transform 0.5s; /* 过渡效果 */
}

3)海量数据列表 之 虚拟滚动(virtualized scroller)
实现思路
第一步:计算出父元素容器的总高度
第二步:根据产品需要展示的条数渲染初始数据
第三步:监听用户的滚动事件,并根据用户的滚动位置进行判断
第四步:实现动画

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Virtual Scrolling Example</title>
    <style>
        .container {
            height: 200px;
            /* 设置容器高度 */
            overflow-y: auto;
            /* 启用垂直滚动 */
            border: 1px solid #ccc;
            /* 边框样式 */
            position: relative;
            /* 相对定位 */
        }

        .item {
            height: 30px;
            /* 每个条目的高度 */
            display: flex;
            align-items: center;
            padding: 0 10px;
            box-sizing: border-box;
            /* 包含内边距和边框 */
        }

        .spacer {
            height: 30px;
            /* 占位符高度 */
        }
    </style>
</head>

<body>
    <!-- 容器展示层 -->
    <div class="container" id="scrollContainer">
        <!-- 模仿滚动层 -->
        <div class="scroll-layer" id="scrollLayer"></div>
    </div>


    <script>
        const container = document.getElementById('scrollContainer');
        const totalItems = 1000; // 总条目数
        const itemHeight = 30; // 每个条目的高度
        const visibleCount = Math.ceil(container.clientHeight / itemHeight); // 可见条目数
        console.log(visibleCount)
        // 设置容器的整体高度
        const scrollLayer = document.getElementById('scrollLayer');
        console.log
        scrollLayer.style.height = `${totalItems * itemHeight}px`;

        function renderItems(startIndex) {
            scrollLayer.innerHTML = ''; // 清空当前内容

            // 添加占位符以保持布局
            const spacer = document.createElement('div');
            spacer.style.height = `${startIndex * itemHeight}px`; // 占位符高度
            scrollLayer.appendChild(spacer);

            // 渲染可见项目
            for (let i = startIndex; i < startIndex + visibleCount; i++) {
                if (i >= totalItems) break; // 超出范围则停止
                const div = document.createElement('div');
                div.className = 'item';
                div.textContent = `Item ${i + 1}`;
                scrollLayer.appendChild(div);
            }
        }

        container.addEventListener('scroll', () => {
            console.log('我在滚动')
            const scrollTop = container.scrollTop;
            const startIndex = Math.floor(scrollTop / itemHeight); // 计算起始索引
            renderItems(startIndex); // 渲染新的项目
        });

        // 初始渲染
        renderItems(0);
    </script>
</body>

</html>

参考文章 https://developer.mozilla.org/zh-CN/docs/Web/Performance/Guides/How_browsers_work
参考文章 https://juejin.cn/book/6844733763675488269/section/6844733763771957256
参考文章 https://coolshell.cn/articles/9666.html

渲染流程补充
FOUC:主要指的是样式闪烁的问题,由于浏览器渲染机制(比如 firefox),在 CSS 加载之前,先呈现了 HTML,就会导致展示出无样式内容,然后样式突然呈现的现象。会出现这个问题的原因主要是 css 加载时间过长,或者 css 被放在了文档底部。
白屏:有些浏览器渲染机制(比如 chrome)要先构建 DOM 树和 CSSOM 树,构建完成后再进行渲染,如果 CSS 部分放在 HTML 尾部,由于 CSS 未加载完成,浏览器迟迟未渲染,从而导致白屏;也可能是把 js 文件放在头部,脚本的加载会阻塞后面文档内容的解析,从而页面迟迟未渲染出来,出现白屏问题。

关键渲染路径(Critical Rendering Path)
一、文档对象模型(DOM)构建
浏览器解析 HTML,构建 DOM,表示页面的结构。
二、CSS 对象模型(CSSOM)构建
浏览器解析 CSS,创建 CSSOM,定义 DOM 中元素的样式。
三、渲染树构建
浏览器将 DOM 和 CSSOM 结合,创建渲染树,包含可见元素及其样式。
四、布局
浏览器计算渲染树中元素的尺寸和位置,确定它们在屏幕上的显示方式。
五、绘制
浏览器根据布局将像素绘制到屏幕上。

优化策略
策略1:资源数,关键路径长度 (合并html JS CSS文件)
策略2:字节数,关键路径文件资源大小
策略3:优先级,异步加载延时加载

DOMContentLoaded:dom已经加载完成
load:所有渲染步骤已经完成

// 使用 DOMContentLoaded
document.addEventListener('DOMContentLoaded', function() {
    console.log('DOM fully loaded and parsed');
});

// 使用 load
window.addEventListener('load', function() {
    console.log('All resources finished loading!');
});

5、浏览器存储
浏览器常见的存储技术有 cookie、localStorage 和 sessionStorage。
还有两种存储技术用于大规模数据存储,webSQL(已被废除)和 indexDB

SessionStorage, LocalStorage, Cookie 这三者都可以被用来在浏览器端存储数据,而且都是字符串类型的键值对。区别在于前两者属于 HTML5 WebStorage,创建它们的目的便于客户端存储数据。而 cookie 是网站为了标示用户身份而储存在用户本地终端上的数据(通常经过加密)。cookie 数据始终在同源(协议、主机、端口相同)的 http 请求中携带(即使不需要),会在浏览器和服务器间来回传递。

存储大小:
cookie 数据大小不能超过 4 k 。
sessionStorage 和 localStorage 虽然也有存储大小的限制,但比 cookie 大得多,可以达到 5M 或更大。

有期时间:
localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据。
sessionStorage 数据在页面会话结束时会被清除。页面会话在浏览器打开期间一直保持,并且重新加载或恢复页面仍会保持原来的页面会话。在新标签或窗口打开一个页面时会在顶级浏览上下文中初始化一个新的会话。
cookie 设置的 cookie 过期时间之前一直有效,即使窗口或浏览器关闭。

作用域:
sessionStorage 只在同源的同窗口(或标签页)中共享数据,也就是只在当前会话中共享。
localStorage 在所有同源窗口中都是共享的。
cookie 在所有同源窗口中都是共享的。

浏览器端常用的存储技术是 cookie 、localStorage 和 sessionStorage。
cookie 其实最开始是服务器端用于记录用户状态的一种方式,由服务器设置,在客户端存储,然后每次发起同源请求时,发送给服务器端。cookie 最多能存储 4 k 数据,它的生存时间由 expires 属性指定,并且 cookie 只能被同源的页面访问共享。
sessionStorage 是 html5 提供的一种浏览器本地存储的方法,它借鉴了服务器端 session的概念,代表的是一次会话中所保存的数据。它一般能够存储 5M 或者更大的数据,它在当前窗口关闭后就失效了,并且 sessionStorage 只能被同一个窗口的同源页面所访问共享。
localStorage 也是 html5 提供的一种浏览器本地存储的方法,它一般也能够存储 5M 或者更大的数据。它和 sessionStorage不同的是,除非手动删除它,否则它不会失效,并且 localStorage 也只能被同源页面所访问共享。
上面几种方式都是存储少量数据的时候的存储方式,当我们需要在本地存储大量数据的时候,我们可以使用浏览器的 indexDB 这是浏览器提供的一种本地的数据库存储机制。它不是关系型数据库,它内部采用对象仓库的形式存储数据,它更接近 NoSQL 数据库。

6、H5新特性

绘画 canvas;
用于媒介回放的 video 和 audio 元素;
本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;
sessionStorage 的数据在浏览器关闭后自动删除;
语意化更好的内容元素,比如 article、footer、header、nav、section;
表单控件,calendar、date、time、email、url、search;
新的技术 webworker(主线程之外的线程,但是核心资源DOM操作相关不共享,控制台也不共享), websocket(长连接);
新的文档属性 document.visibilityState (使用场景:当页面不可以见停止动画,播放类的性能优化)
移除的元素有:无,但是不推荐使用 <basefont>、<big>、<center>、<font>、<s>、<strike>、<tt>、<u>。
纯表现的元素:basefont,big,center,font, s,strike,tt,u;
对可用性产生负面影响的元素:frame,frameset,noframes;
  1. SEO

title、description、keywords
语义化的 HTML 代码,符合 W3C 规范:语义化代码让搜索引擎容易理解网页
重要内容 HTML 代码放在最前:搜索引擎抓取 HTML 顺序是从上到下,有的搜索引擎对抓取长度有限制,保证重要内容肯定被抓取
重要内容不用JS输出:爬虫不会执行 js 获取内容
少用 iframe:搜索引擎不会抓取 iframe 中的内容
非装饰性图片必须加 alt
提高网站速度:网站速度是搜索引擎排序的一个重要指标

  1. iframe 缺点
    (1) iframe 会阻塞主页面的 onload 事件。window 的 onload 事件需要在所有 iframe
    加载完毕后(包含里面的元素)才会触发。在 Safari 和 Chrome 里,通过 JavaScript 动态
    设置 iframe 的 src 可以避免这种阻塞情况。
    (2) 搜索引擎的检索程序无法解读这种页面,不利于网页的 SEO 。
    (3) iframe 和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的
    并行加载。
    (4) 浏览器的后退按钮失效。
    (5) 小型的移动设备无法完全显示框架。
posted @ 2025-03-20 11:31  张正1998  阅读(13)  评论(0)    收藏  举报