浏览器工作原理
约 3729 字大约 12 分钟
浏览器工作原理
浏览器架构详解 🟢
1. 多进程架构
1.1 主要进程及其职责
- 浏览器主进程(Browser Process)
- 负责界面显示、用户交互
- 管理其他进程的创建和销毁
- 网络资源的下载管理
- 文件访问等权限管理
- 渲染进程(Renderer Process)
- 解析HTML、CSS,构建DOM树和CSSOM树
- 执行JavaScript代码
- 处理事件循环
- 每个标签页都有独立的渲染进程
- 运行在沙箱中,提高安全性
- GPU进程(GPU Process)
- 处理来自其他进程的GPU任务
- 负责3D绘制
- 处理硬件加速的视频解码
- 整个浏览器只有一个GPU进程
- 网络进程(Network Process)
- 处理网络请求
- 实现网络协议
- 解析DNS
- 建立连接
- 数据传输
- 插件进程(Plugin Process)
- 运行浏览器插件
- 独立进程,防止插件崩溃影响浏览器
- 每个插件一个进程
1.2 进程通信机制
- IPC(Inter-Process Communication)
渲染原理 🟡
1. 渲染流水线
浏览器渲染遵循以下流程:
- 构建DOM树
- 解析HTML文档
- 生成DOM节点
- 构建节点间的关系
- 构建CSSOM树
- 解析CSS文件和样式标签
- 计算继承和层叠关系
- 生成完整的样式表
- 生成渲染树(Render Tree)
- 将DOM树和CSSOM树合并
- 计算元素的可见性
- 确定元素的布局信息
- 布局(Layout)
- 计算元素的几何信息
- 确定元素的具体位置
- 处理相对定位等
- 绘制(Paint)
- 将渲染树转换为屏幕像素
- 处理层叠关系
- 应用视觉效果
// 性能监控示例
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// 首次内容绘制时间
if (entry.name === 'first-contentful-paint') {
console.log('FCP:', entry.startTime);
}
// 最大内容绘制时间
if (entry.name === 'largest-contentful-paint') {
console.log('LCP:', entry.startTime);
}
}
});
observer.observe({
entryTypes: ['paint', 'largest-contentful-paint']
});
2. 回流和重绘
回流(Reflow)
发生条件:
- 元素的几何属性改变
- DOM树的结构变化
- 获取特定属性值
优化策略:
- 批量修改DOM
- 使用文档片段
- 避免频繁读取几何属性
// 优化前
const element = document.getElementById('example');
element.style.width = '100px';
element.style.height = '100px';
element.style.margin = '10px';
// 优化后
const element = document.getElementById('example');
element.style.cssText = 'width: 100px; height: 100px; margin: 10px;';
// 使用文档片段
const fragment = document.createDocumentFragment();
for (let i = 0; i < 10; i++) {
const el = document.createElement('div');
fragment.appendChild(el);
}
document.body.appendChild(fragment);
重绘(Repaint)
发生条件:
- 元素外观改变
- 不影响布局的样式变化
- 可见性变化
优化策略:
- 使用CSS3硬件加速
- 合理使用图层
- 避免不必要的重绘
// 使用transform代替top/left
const element = document.getElementById('example');
// 不推荐
element.style.left = '100px';
// 推荐
element.style.transform = 'translateX(100px)';
// 创建新的图层
element.style.willChange = 'transform';
element.style.transform = 'translateZ(0)';
浏览器存储 🟡
1. Cookie详解
特性和限制
- 大小限制4KB
- 随请求发送到服务器
- 可设置过期时间
- 支持域名和路径限制
安全相关属性
- HttpOnly: 防止XSS攻击
- Secure: 只通过HTTPS发送
- SameSite: 防止CSRF攻击
// Cookie操作示例
class CookieManager {
static set(name, value, options = {}) {
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
if (options.expires) {
cookie += `; expires=${options.expires.toUTCString()}`;
}
if (options.path) {
cookie += `; path=${options.path}`;
}
if (options.domain) {
cookie += `; domain=${options.domain}`;
}
if (options.secure) {
cookie += '; secure';
}
if (options.httpOnly) {
cookie += '; httpOnly';
}
if (options.sameSite) {
cookie += `; sameSite=${options.sameSite}`;
}
document.cookie = cookie;
}
static get(name) {
const cookies = document.cookie.split('; ');
const cookie = cookies.find(c => c.startsWith(name + '='));
return cookie ? decodeURIComponent(cookie.split('=')[1]) : null;
}
static delete(name) {
this.set(name, '', { expires: new Date(0) });
}
}
2. Web Storage
localStorage
特点:
- 永久存储
- 同源策略限制
- 容量较大(5-10MB)
- 同步操作
sessionStorage
特点:
- 会话期间有效
- 标签页隔离
- 容量同localStorage
- 同步操作
// Storage封装
class StorageManager {
constructor(storage) {
this.storage = storage;
}
set(key, value, expires = null) {
const item = {
value,
expires: expires ? new Date().getTime() + expires * 1000 : null
};
this.storage.setItem(key, JSON.stringify(item));
}
get(key) {
const item = JSON.parse(this.storage.getItem(key));
if (!item) return null;
if (item.expires && item.expires < new Date().getTime()) {
this.storage.removeItem(key);
return null;
}
return item.value;
}
remove(key) {
this.storage.removeItem(key);
}
clear() {
this.storage.clear();
}
}
// 使用示例
const localStore = new StorageManager(localStorage);
const sessionStore = new StorageManager(sessionStorage);
3. IndexedDB
特点:
- 支持大容量存储
- 支持索引和事务
- 异步API
- 支持二进制存储
// IndexedDB封装
class IndexedDB {
constructor(dbName, version) {
this.dbName = dbName;
this.version = version;
}
async open() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, this.version);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains('items')) {
db.createObjectStore('items', { keyPath: 'id' });
}
};
});
}
async add(item) {
const db = await this.open();
return new Promise((resolve, reject) => {
const transaction = db.transaction(['items'], 'readwrite');
const store = transaction.objectStore('items');
const request = store.add(item);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async get(id) {
const db = await this.open();
return new Promise((resolve, reject) => {
const transaction = db.transaction(['items'], 'readonly');
const store = transaction.objectStore('items');
const request = store.get(id);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
}
浏览器缓存机制 🔴
1. HTTP缓存
强缓存
控制字段:
- Cache-Control
- Expires
策略选择:
- 频繁变动资源: no-cache
- 长期不变资源: max-age
- 永不缓存: no-store
// 缓存控制示例
function setCacheControl(res, type) {
switch (type) {
case 'static':
// 静态资源缓存一年
res.setHeader('Cache-Control', 'public, max-age=31536000');
break;
case 'api':
// API响应不缓存
res.setHeader('Cache-Control', 'no-store');
break;
case 'html':
// HTML文档协商缓存
res.setHeader('Cache-Control', 'no-cache');
break;
}
}
协商缓存
控制字段:
- Last-Modified/If-Modified-Since
- ETag/If-None-Match
使用建议:
- 精确度要求高用ETag
- 性能要求高用Last-Modified
- 两者结合使用更安全
// ETag生成示例
function generateETag(content) {
const hash = crypto.createHash('sha256');
hash.update(content);
return `"${hash.digest('hex')}"`;
}
// 协商缓存处理
function handleConditionalRequest(req, res) {
const etag = generateETag(content);
if (req.headers['if-none-match'] === etag) {
res.statusCode = 304;
res.end();
return;
}
res.setHeader('ETag', etag);
res.end(content);
}
2. 浏览器缓存位置
Memory Cache
特点:
- 内存中的缓存
- 快速读取
- 时效性短
- 关闭页面后清除
Disk Cache
特点:
- 磁盘中的缓存
- 读取较慢
- 持久存储
- 容量较大
Service Worker Cache
特点:
- 可编程的缓存
- 离线访问
- 网络优先或缓存优先
- 精确的缓存控制
// Service Worker缓存示例
// sw.js
const CACHE_NAME = 'my-site-cache-v1';
const urlsToCache = [
'/',
'/styles/main.css',
'/scripts/main.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(urlsToCache))
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then(response => {
if (response) {
return response;
}
return fetch(event.request);
})
);
});
浏览器安全机制 🔴
1. 同源策略
定义和限制
- 协议相同
- 域名相同
- 端口相同
限制范围:
- Cookie、LocalStorage和IndexedDB访问
- DOM和JS对象访问
- AJAX请求发送
跨域解决方案
- CORS
- JSONP
- 代理服务器
- postMessage
- WebSocket
// CORS配置示例
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://example.com');
res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type,Authorization');
res.header('Access-Control-Allow-Credentials', 'true');
if (req.method === 'OPTIONS') {
res.sendStatus(200);
} else {
next();
}
});
2. 沙箱机制
渲染进程沙箱
限制:
- 文件系统访问
- 网络访问
- 进程通信
- 系统API调用
iframe沙箱
属性控制:
- allow-scripts
- allow-same-origin
- allow-forms
- allow-popups
<!-- iframe沙箱配置 -->
<iframe
src="https://example.com"
sandbox="allow-scripts allow-same-origin"
loading="lazy"
referrerpolicy="no-referrer"
></iframe>
浏览器渲染原理深入 🔴
1. 渲染流水线详解
1.1 构建DOM树的完整过程
- 字节流解码
- 浏览器从网络或磁盘读取HTML原始字节
- 根据文件指定编码(如UTF-8)将字节转换成字符
- 输入令牌化
- 将字符串转换成令牌(Token)
- 识别HTML标签、属性、文本等
- 处理特殊字符和实体引用
- 令牌转节点
- 将令牌转换为节点对象
- 设置节点的标签名、属性等信息
- 建立节点之间的父子关系
- DOM树构建
- 将节点连接成树形结构
- 处理特殊标签(如script、style)
- 执行JavaScript可能修改DOM结构
// DOM树构建过程示例
class DOMBuilder {
constructor() {
this.currentNode = null;
this.document = new Document();
}
parse(html) {
// 1. 字节流解码
const decodedHTML = this.decodeBytes(html);
// 2. 输入令牌化
const tokens = this.tokenize(decodedHTML);
// 3. 令牌转节点并构建DOM树
tokens.forEach(token => {
if (token.type === 'startTag') {
const element = document.createElement(token.name);
// 处理属性
token.attributes.forEach(attr => {
element.setAttribute(attr.name, attr.value);
});
// 建立父子关系
if (this.currentNode) {
this.currentNode.appendChild(element);
}
if (!token.isSelfClosing) {
this.currentNode = element;
}
} else if (token.type === 'endTag') {
this.currentNode = this.currentNode.parentNode;
} else if (token.type === 'text') {
const textNode = document.createTextNode(token.content);
this.currentNode.appendChild(textNode);
}
});
}
// 字节流解码
decodeBytes(bytes) {
const decoder = new TextDecoder('utf-8');
return decoder.decode(bytes);
}
// 令牌化
tokenize(html) {
const tokens = [];
// 实现词法分析器
// 返回token数组
return tokens;
}
}
1.2 CSSOM树构建过程
- 样式表处理
- 加载外部样式表
- 解析内联样式
- 处理style标签内容
- 规则解析
- 解析选择器
- 解析声明块
- 处理@规则(如@media、@import)
- 层叠和继承
- 计算选择器优先级
- 应用层叠规则
- 处理继承属性
- CSSOM树形成
- 构建树形结构
- 计算计算值
- 处理简写属性
// CSSOM构建示例
class CSSOMBuilder {
constructor() {
this.styleSheet = {
rules: [],
mediaQueries: new Map()
};
}
parse(css) {
// 1. 分词和语法分析
const rules = this.parseRules(css);
// 2. 处理规则
rules.forEach(rule => {
if (rule.type === 'style') {
// 处理样式规则
const selector = this.parseSelector(rule.selector);
const declarations = this.parseDeclarations(rule.declarations);
this.addStyleRule(selector, declarations);
} else if (rule.type === 'media') {
// 处理媒体查询
this.handleMediaQuery(rule);
}
});
// 3. 计算继承和层叠
this.computeInheritance();
this.computeCascade();
}
// 计算选择器优先级
calculateSpecificity(selector) {
let specificity = [0, 0, 0, 0]; // [inline, id, class, tag]
// ID选择器
specificity[1] = (selector.match(/#[a-zA-Z0-9_-]+/g) || []).length;
// 类选择器、属性选择器、伪类
specificity[2] = (selector.match(/\.[a-zA-Z0-9_-]+|\[[^\]]+\]|:[a-zA-Z]+/g) || []).length;
// 标签选择器、伪元素
specificity[3] = (selector.match(/[a-zA-Z0-9_-]+|::[a-zA-Z]+/g) || []).length;
return specificity;
}
// 处理继承
computeInheritance() {
const inheritableProperties = [
'color',
'font-family',
'font-size',
'font-weight',
'line-height',
// ... 其他可继承属性
];
// 遍历DOM树,应用继承规则
function processNode(node) {
const parent = node.parentElement;
if (parent) {
inheritableProperties.forEach(prop => {
if (!node.style[prop]) {
node.style[prop] = parent.style[prop];
}
});
}
node.children.forEach(processNode);
}
}
}
1.3 渲染树构建过程
- 合并DOM和CSSOM
- 遍历DOM树
- 匹配CSS规则
- 计算最终样式
- 布局计算
- 计算视口大小
- 确定包含块
- 处理盒模型
- 计算位置和大小
- 分层处理
- 创建图层树
- 确定合成层
- 处理z-index堆叠
// 渲染树构建示例
class RenderTreeBuilder {
constructor(domTree, cssom) {
this.domTree = domTree;
this.cssom = cssom;
this.renderTree = null;
}
build() {
// 1. 遍历DOM树并匹配样式
this.renderTree = this.createRenderTree(this.domTree.root);
// 2. 计算布局
this.performLayout(this.renderTree);
// 3. 创建图层树
this.createLayerTree(this.renderTree);
}
createRenderTree(domNode) {
// 检查节点是否需要渲染
if (!this.isRenderable(domNode)) {
return null;
}
// 创建渲染节点
const renderNode = {
type: domNode.type,
styles: this.computeStyles(domNode),
layout: null,
children: []
};
// 处理子节点
domNode.children.forEach(child => {
const childRenderNode = this.createRenderTree(child);
if (childRenderNode) {
renderNode.children.push(childRenderNode);
}
});
return renderNode;
}
// 检查节点是否需要渲染
isRenderable(node) {
// 检查display: none
if (this.getComputedStyle(node).display === 'none') {
return false;
}
// 检查visibility: hidden
if (this.getComputedStyle(node).visibility === 'hidden') {
return false;
}
// 检查特殊标签
const nonRenderableTags = ['script', 'style', 'meta', 'link'];
if (nonRenderableTags.includes(node.tagName.toLowerCase())) {
return false;
}
return true;
}
// 计算节点的最终样式
computeStyles(node) {
const styles = {};
// 1. 获取匹配的样式规则
const matchedRules = this.cssom.findMatchingRules(node);
// 2. 按优先级排序
matchedRules.sort((a, b) =>
this.compareSpecificity(a.specificity, b.specificity)
);
// 3. 应用样式
matchedRules.forEach(rule => {
Object.assign(styles, rule.declarations);
});
// 4. 处理继承
this.handleInheritance(node, styles);
return styles;
}
// 执行布局计算
performLayout(renderNode) {
// 1. 计算盒模型
this.computeBoxModel(renderNode);
// 2. 计算位置
this.computePosition(renderNode);
// 3. 处理子节点
renderNode.children.forEach(child => {
this.performLayout(child);
});
}
// 创建图层树
createLayerTree(renderNode) {
// 1. 检查是否需要创建新的图层
if (this.needsNewLayer(renderNode)) {
renderNode.layer = this.createLayer(renderNode);
}
// 2. 处理子节点
renderNode.children.forEach(child => {
this.createLayerTree(child);
});
}
// 检查是否需要创建新的图层
needsNewLayer(renderNode) {
const styles = renderNode.styles;
// 检查transform
if (styles.transform !== 'none') return true;
// 检查opacity
if (styles.opacity < 1) return true;
// 检查position: fixed
if (styles.position === 'fixed') return true;
// 检查will-change
if (styles.willChange === 'transform' ||
styles.willChange === 'opacity') {
return true;
}
return false;
}
}
2. 回流和重绘优化
2.1 触发条件详解
- 回流触发条件
- 元素尺寸改变
- 元素位置改变
- 元素内容改变
- 页面初始渲染
- 浏览器窗口改变
- 重绘触发条件
- 颜色改变
- 背景改变
- 阴影改变
- 可见性改变
// 性能监控示例
class LayoutThrashing {
constructor() {
this.pendingReads = new Set();
this.pendingWrites = new Set();
}
measure(element, property) {
// 检查是否有待处理的写操作
if (this.pendingWrites.size > 0) {
console.warn('强制回流警告:在写操作后立即读取');
}
this.pendingReads.add({ element, property });
return element.getBoundingClientRect()[property];
}
mutate(element, property, value) {
this.pendingWrites.add({ element, property, value });
requestAnimationFrame(() => this.flushWrites());
}
flushWrites() {
this.pendingWrites.forEach(({ element, property, value }) => {
element.style[property] = value;
});
this.pendingWrites.clear();
this.pendingReads.clear();
}
}
2.2 优化策略
- 批量修改
- 使用DocumentFragment
- 使用display: none
- 使用class替换style
- 避免强制同步布局
- 缓存布局信息
- 使用FastDOM
- 使用虚拟滚动
// 优化示例
class LayoutOptimizer {
static batchUpdate(elements, updates) {
// 创建文档片段
const fragment = document.createDocumentFragment();
elements.forEach(element => {
// 克隆元素
const clone = element.cloneNode(true);
// 应用更新
updates(clone);
// 添加到文档片段
fragment.appendChild(clone);
});
// 一次性更新DOM
elements[0].parentNode.replaceChildren(fragment);
}
static createVirtualScroller(container, items, itemHeight) {
let scrollTop = 0;
let visibleItems = [];
function render() {
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = startIndex + Math.ceil(container.clientHeight / itemHeight);
visibleItems = items.slice(startIndex, endIndex).map((item, index) => ({
...item,
style: {
position: 'absolute',
top: (startIndex + index) * itemHeight + 'px',
height: itemHeight + 'px'
}
}));
// 更新DOM
requestAnimationFrame(() => {
container.innerHTML = '';
visibleItems.forEach(item => {
const div = document.createElement('div');
Object.assign(div.style, item.style);
div.textContent = item.content;
container.appendChild(div);
});
});
}
container.addEventListener('scroll', () => {
scrollTop = container.scrollTop;
render();
});
render();
}
}
3. 合成层优化
3.1 图层创建条件
- 显式条件
- transform: translateZ(0)
- will-change: transform
- position: fixed
- filter
- opacity动画
- 隐式条件
- 具有3D变换
- 加速视频解码
- WebGL或Canvas上下文
- CSS滤镜
// 图层监控示例
class LayerMonitor {
static getCompositingReasons(element) {
const reasons = [];
const style = window.getComputedStyle(element);
// 检查变换
if (style.transform !== 'none') {
reasons.push('transform');
}
// 检查will-change
if (style.willChange !== 'auto') {
reasons.push('will-change');
}
// 检查position: fixed
if (style.position === 'fixed') {
reasons.push('fixed position');
}
// 检查opacity动画
if (element.classList.contains('animated')) {
reasons.push('animation');
}
return reasons;
}
static optimizeLayer(element) {
// 强制创建新的层
element.style.transform = 'translateZ(0)';
element.style.willChange = 'transform';
// 监控层的数量
requestAnimationFrame(() => {
const layers = document.getElementsByClassName('layer');
if (layers.length > 10) {
console.warn('图层数量过多警告');
}
});
}
}
3.2 层压缩
- 层压缩条件
- 相邻的合成层
- 没有3D变换
- 没有交叉重叠
- 相同的合成原因
- 优化策略
- 控制层数量
- 合理使用will-change
- 避免层爆炸
// 层压缩优化示例
class LayerCompression {
static analyzeLayerTree(root) {
const layers = [];
function traverse(element) {
if (this.isComposited(element)) {
layers.push(element);
}
Array.from(element.children).forEach(traverse);
}
traverse(root);
return this.optimizeLayers(layers);
}
static isComposited(element) {
const style = window.getComputedStyle(element);
return style.transform !== 'none' ||
style.willChange !== 'auto' ||
style.position === 'fixed';
}
static optimizeLayers(layers) {
// 分析层的重叠关系
const overlapping = new Set();
layers.forEach((layer, i) => {
const rect1 = layer.getBoundingClientRect();
layers.slice(i + 1).forEach(other => {
const rect2 = other.getBoundingClientRect();
if (this.isOverlapping(rect1, rect2)) {
overlapping.add(layer);
overlapping.add(other);
}
});
});
// 优化非重叠层
layers.forEach(layer => {
if (!overlapping.has(layer)) {
layer.style.willChange = 'auto';
}
});
}
static isOverlapping(rect1, rect2) {
return !(rect1.right < rect2.left ||
rect1.left > rect2.right ||
rect1.bottom < rect2.top ||
rect1.top > rect2.bottom);
}
}
4. JavaScript执行优化
4.1 执行上下文优化
- 变量作用域优化
- 避免全局变量
- 使用块级作用域
- 及时清理引用
- 闭包优化
- 避免过度使用闭包
- 及时释放闭包
- 控制闭包大小
// 执行上下文优化示例
class ExecutionContextOptimizer {
static optimizeScope() {
// 避免意外的全局变量
'use strict';
// 使用块级作用域
{
const temp = heavyComputation();
processData(temp);
} // temp在这里被回收
// 避免闭包陷阱
function createCounter() {
let count = 0;
return () => ++count;
}
const counter = createCounter();
// 使用完后解除引用
counter = null;
}
static optimizeMemory() {
// 使用WeakMap/WeakSet
const cache = new WeakMap();
function memoize(fn) {
return function(obj) {
if (!cache.has(obj)) {
cache.set(obj, fn.call(this, obj));
}
return cache.get(obj);
};
}
// 及时清理DOM引用
function addListener(element, event, handler) {
element.addEventListener(event, handler);
return {
destroy() {
element.removeEventListener(event, handler);
element = null;
handler = null;
}
};
}
}
}
4.2 事件循环优化
- 任务调度
- 使用requestAnimationFrame
- 使用requestIdleCallback
- 任务分片
- 微任务管理
- 合理使用Promise
- 控制微任务数量
- 避免长任务
// 事件循环优化示例
class EventLoopOptimizer {
constructor() {
this.tasks = [];
this.isProcessing = false;
}
addTask(task, priority = 0) {
this.tasks.push({ task, priority });
this.tasks.sort((a, b) => b.priority - a.priority);
if (!this.isProcessing) {
this.processTasks();
}
}
async processTasks() {
this.isProcessing = true;
while (this.tasks.length > 0) {
const { task } = this.tasks.shift();
// 检查是否需要让出控制权
if (navigator.scheduling?.isInputPending()) {
await new Promise(resolve =>
requestIdleCallback(resolve)
);
}
try {
await task();
} catch (error) {
console.error('Task error:', error);
}
}
this.isProcessing = false;
}
scheduleRender(renderFn) {
let frameId = null;
let lastTimestamp = 0;
const animate = (timestamp) => {
if (timestamp - lastTimestamp >= 16) { // 约60fps
renderFn();
lastTimestamp = timestamp;
}
frameId = requestAnimationFrame(animate);
};
frameId = requestAnimationFrame(animate);
return () => {
cancelAnimationFrame(frameId);
};
}
}
[未完待续...]