# 浏览器底层

浏览器是线程的,JS是单线程的。

当遇到link导入外部样式资源浏览器会开辟一个线程,去服务器获取对应的资源文件,不会阻塞主线程的渲染

# 有关样式的处理

浏览器渲染原理

  1. 解析HTML,生成DOM树,解析CSS,生成CSSOM树
  2. 将DOM树和CSSOM树结合,生成渲染树(Render Tree)
  3. Layout(回流): 根据生成的渲染树,计算它们在设备视口(viewport)内的确切位置和大小,这个计算的阶段就是回流
  4. Painting(重绘): 根据渲染树以及回流得到的几何信息,得到节点的绝对像素
  5. Display:将像素发送给GPU,展示在页面上 图片

link和@import区别

1.从属关系区别 @import是 CSS 提供的语法规则,只有导入样式表的作用;link是HTML提供的标签,不仅可以加载 CSS 文件,还可以定义 RSS、rel 连接属性等。

2.加载顺序区别 加载页面时,link标签引入的 CSS 被同时加载;@import引入的 CSS 将在页面加载完毕后被加载。

3.兼容性区别 @import是 CSS2.1 才有的语法,故只可在 IE5+ 才能识别;link标签作为 HTML 元素,不存在兼容性问题。

4.DOM可控性区别 可以通过 JS 操作 DOM ,插入link标签来改变样式;由于 DOM 方法是基于文档的,无法使用@import的方式插入样式。

浏览器是多线程,页面渲染是单线程

进程:一个程序(一个进程中可以包含多个线程) 线程:一个程序中要做的事情(一个线程同时只能做一个事情)

HTTP网络请求并发数: 大部分浏览器都维持在6个,为了避免并发的上限,导致某些资源延迟加载,页面渲染速度变慢,我们应该尽可能减少HTTP的请求数量

优化:在真实项目中,如果css样式代码不是很多我们应该使用内嵌式,依次来减少HTTP资源请求,提高页面的渲染速度。

# 有关JS的处理

页面加载过程中遇到JS(JS会操作DOM):

  • 默认script="xxx" 主线程会从服务器获取JS资源,并把JS资源解析加载,加载完成后再继续渲染DOM结构。现代刘篮球都有完善的代码扫描机制:如果遇到script需要同步加载和渲染代码,浏览器在渲染JS的时候,同时会向下继续扫描代码,如果发现有一些异步的资源代码,会加载请求。

-如果设置了defer或async 都会让其变成异步获取资源,不会阻碍DOM的渲染。 defer可以遵循原有的加载顺序,获取后按照顺序去依次渲染JS async无序的谁先获取到先执行谁

因为JS还有可能操作css元素样式,所以哪怕都是异步请求资源的情况下,JS先加载回来,也要等到CSS加载并渲染完成后才会执行JS代码。

为啥要把link写在上面,把script写在结构的下面?

link放到顶部是为了更快加载CSS;script放在底部是为了获取DOM元素或者不阻碍DOM渲染。

现代浏览器都有相对完善的代码扫描机制,如果遇到script需要同步加载和渲染代码,浏览器在渲染JS的时候同时会向下继续扫描代码,如果发现有一些异步的资源代码,此时也会加载请求。

load事件:当所有资源加载完成后才会触发,包含所需图片加载完。DOMContentLoaded事件:当DOM结构加载完成后会触发,DOM树有了并且JS也执行加载了,此时触发这个事件。

JQ类库中,$(function(){})或者$(document).ready(function(){}),当DOM结构加载完才会执行函数中的代码,原理就是应用DOMContentLoaded事件事件完成。DOMContentLoaded在低版本浏览器中不兼容,使用onreadystatechange事件代替,在这个事件中监听document.readyState值,值为complete表示DOM结构加载完成。

# DOM回流和重绘

重绘:元素样式的改变:outline、visibility、color、background-color等

回流:元素的大小或者位置发生了变化(当页面布局和几何信息发生变化的时候),触发了重新布局,导致渲染树重新计算布局和渲染

回流一定会触发重绘,重绘不一定会回流

前端性能优化之:避免DOM的回流

  • 放弃传统操作dom的时代,基于vue/react开始数据影响视图模式

    mvvm / mvc / virtual dom / dom diff ......

  • 分离读写操作 (现代的浏览器都有渲染队列的机制)

offsetTop、offsetLeft、offsetWidth、offsetHeight、clientTop、clientLeft、clientWidth、clientHeight scrollTop、scrollLeft、scrollWidth、scrollHeight、getComputedStyle、currentStyle....会刷新渲染队列

var div = document.getElementbyId('div');

//在较新的浏览器中由于渲染队列机制,以下代码只会引起一次回流和重绘
div.style.height = '100px';
div.style.width = '100px';

// 由于console.log的存在中断了渲染队列机制,所以会引起两次回流和重绘
div.style.height = '100px';
console.log(div.style.height);
div.style.width = '100px';
  • 样式集中改变

div.style.cssText = 'width:20px;height:20px;' div.className = 'box';

  • 缓存布局信息
div.style.left = div.offsetLeft + 1 + 'px';     div.style.top = div.offsetTop + 1 + 'px';
=>改为
var curLeft = div.offsetLeft;   var curTop = div.offsetTop;
div.style.left = curLeft + 1 + 'px';    div.style.top = curTop + 1 + 'px';
  • 元素批量修改 文档碎片:createDocumentFragment 模板字符串拼接
const box = document.getElementById('box'),
frag = document.createDocumentFragment();

for (let i = 0; i < 10; i++) {
    const li = document.createElement('li');
    li.innerHTML = i;
    frag.appendChild(li);
}
box.appendChild(frag);


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

for (let i = 0; i < 10; i++) {
    str += '<li>' + i +'</li>';
}
box.innerHTML = str;
  • 动画效果应用到position属性为absolute或fixed的元素上(脱离文档流)

  • CSS3硬件加速(GPU加速)

比起考虑如何减少回流重绘,我们更期望的是,根本不要回流重绘;transform \ opacity \ filters ... 这些属性会触发硬件加速,不会引发回流和重绘...... 可能会引发的坑:过多使用会占用大量内存,性能消耗严重、有时候会导致字体模糊等

  • 牺牲平滑度换取速度

每次1像素移动一个动画,但是如果此动画使用了100%的CPU,动画就会看上去是跳动的,因为浏览器正在与更新回流做斗争。每次移动3像素可能看起来平滑度低了,但它不会导致CPU在较慢的机器中抖动

  • 避免table布局和使用css的javascript表达式