6.8 浏览器工作原理

6.8.1 渲染render

img

(1)前面介绍了浏览器工作原理的前4步,盒子树的每个节点是盒子Box,实现盒子模型。苹果公司Safari浏览器的内核是webkit,盒子Box对应其源码类RenderBox,完整实现是子类RenderBoxModelObject,这也是盒子树在经常被叫做渲染树的原因。Google公司Chrome浏览器的内核是blink,blink早期基于webkit开发,盒子Box对应其源码类LayoutBox,这是本教程称为盒子树的原因。

(2)第5步layout布局:计算所有盒子的坐标位置与大小。因为很多盒子采用流式布局等自动技术没有设置坐标位置、大小等属性,浏览器在该步骤完成所有盒子的所有属性的完整计算。术语说明。这里的“layout布局”算作专用术语,表示渲染流程的一个步骤。

(3)第6步paint绘制:画画是在白纸上一笔笔画出目标内容,同理,浏览器根据盒子位置与大小、以及其它CSS属性设置,把页面所有盒子一个个画在显示设备,最终完成页面显示。这个“画draw”的动作称为绘制(paint)。

渲染阶段包括其中3个步骤:生成盒子树(渲染树)、layout布局计算、paint绘制,广义渲染包括图示所有6个步骤。

实际渲染过程更为复杂,毕竟浏览器源码大小是G级别。不同浏览器实现也存在细节差异,而且各厂商浏览器本身也在不断升级,所以很多细节无法统一介绍,理解这些大致步骤能够帮助实现高效合理的设计、开发与调试。


6.8.2 理解paint与layout

1、一切皆是画

绘制paint就是“画draw”,更加专业的术语。页面经常包含文本、图片,文本与图片一样也是画出来的,白纸上写纸与画图并没有本质区别。白纸相当于一块画布,字也是在画布作出的一种画。字体文件相当于字的图库,通过工具FontForge查看Arial字体的字符“x”的设计图。

img

程序在显示设备最终显示文本、边框、图片等所有元素,都是在画布画出各种元素。这个过程称之绘制。


2、Java绘制示例

浏览器是C++编写的程序,包含绘制模块。为方便理解,这里通过Java语言模拟一个玩具浏览器程序界面,说明绘制过程。

运行效果

img

源码包含两个文件。BrowserFrame类:模拟一个浏览器窗口,含有标题与地址题,该类不重要;PaintPanel类:模拟页面内容区,对应画板,画出文本、边框,关注重点该类。

BrowserFrame.java代码

import javax.swing.*;
import java.awt.*;

public class BrowserFrame extends JFrame {
    public static void main(String[] args) {
        BrowserFrame browserFrame = new BrowserFrame();
    }
    public BrowserFrame() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Container contentPane = this.getContentPane();
        contentPane.setLayout(null);

        JTextField textFieldURL = new JTextField();
        textFieldURL.setText("https://www.xiaobuteach.com/index.html");
        textFieldURL.setBounds(5, 5, 400, 30);
        contentPane.add(textFieldURL);

        JButton buttonGO = new JButton();
        buttonGO.setText("跳转");
        buttonGO.setBounds(410, 5, 60, 30);
        contentPane.add(buttonGO);

        JPanel paintPanel = new PaintPanel();
        paintPanel.setBounds(0, 40, 500, 160);
        contentPane.add(paintPanel);

        this.setTitle("小步浏览器");
        this.setLocationRelativeTo(null);
        this.setSize(500, 200);
        this.setVisible(true);
    }
}

JFrame表示窗口,内部包含组件:地址输入框textFieldURL、跳转按钮buttonGO、画板paintPanel。

PaintPanel.java源代码

import javax.swing.*;
import java.awt.*;

public class PaintPanel extends JPanel {
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        Font font = new Font("微软雅黑", Font.BOLD, 12);
        g.setFont(font);
        g.setColor(Color.white);
        g.fillRect(0 , 0 ,this.getWidth() , this.getHeight());

        g.setColor(new Color(10, 170, 118));

        g.drawString("小步教程" , 15 , 25);
        g.drawRect(5 ,5 , 100 , 30);

        g.setColor(new Color(74, 166, 252));
        g.fillRect(5 ,50 , 300 , 50);

        g.setColor(Color.white);
        g.drawString("https://www.xiaobuteach.com" , 15,80);
    }
}

对象Graphics g可简单理解成画笔,设置画笔的字体以及颜色,然后通过 drawString画字符串,通过drawRect画边框,fillRect画区域。前面所说“画”即对应draw、fill等系列方法。页面显示每项内容是浏览器一笔笔画出来的,这就是绘制paint。


3、理解第5步layout布局

绘制每个元素需要确定它的绝对位置与大小,述代码才画两个元素就出现大量的坐标值与宽高值。开发HTML页面时,通常只设置少量元素的位置与大小,大部分由浏览器自动计算,这个自动计算的过程即对应浏览器工作原理图的第5步layout布局。


4、开发者工具查看绘制

示例运行效果

img

示例代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>小步教程</title>
    <style>
        .div1{
            border: 1px solid #0aaa76;
            margin-bottom: 20px;
        }

        .div2{
            background-color: #4aa6fc;
        }
    </style>
</head>
<body>
    <div class="div1">小步教程</div>
    <div class="div2">https://xiaobuteach.com</div>
</body>
</html>

开发者工具点击“更多工具”,打开“图层Layer”,再次刷新页面,如下图如示。

img

点击下方“绘制性能剖析器”。

img

可以看出浏览器最终如何绘制图形。drawTextBlob绘制文本,drawRect绘制矩形区域。Chrome浏览器当前考虑弃用该面板功能,正在进行投票,无论以后是否存在该工具面板,不妨碍这里通过它理解绘制。


6.8.3 CSS调试整体思路

按照页面渲染过程的6个步骤整理对应5项调试内容。

img

调试1:网络面板查看请求响应。对应访问页面、页面或CSS等文件引用其它文件。

调试2:元素面板查看元素结构。对应浏览器解析HTML代码。

调试3:样式面板查看元素匹配的选择器以及选择器的样式。对应生成盒子树。

调试4:计算样式面板查看最终实际值,包括盒子模型。对应CSS属性值计算。

调试5:页面查看盒子的显示区域,并结合周围盒子的显示区域分析它的占据区域。对应整个渲染结果。

当页面出现bug无法直接定位时,可以按倒序一步步进行排查。

调试面板与层叠处理步骤的大致对照关系。“样式面板”大致对应声明值declared value,“计算样式”面板大致对应最终实际值actual value。