type
status
date
slug
summary
tags
category
icon
password

HTML语义化

HTML 语义化 是指通过使用有意义的 HTML 标签来描述页面的结构和内容,从而让代码更具可读性、可维护性,并提升网页的可访问性和搜索引擎优化(SEO)。
比如:
  • 使用 <header> 而不是 <div> 明确表示这是网页的头部区域。
  • 使用 <article> 标签表示一篇独立的内容块。
好处:
  • 对机器友好,带有语义的文字表现力丰富,更适合搜索引擎的爬虫爬取有效信息,有利于SEO。除此之外,语义类还支持读屏软件,根据文章可以自动生成目录;
  • 对开发者友好,使用语义类标签增强了可读性,结构更加清晰,开发者能清晰的看出网页的结构,便于团队的开发与维护。

innerText、textContent、innerHTML

  • innerText
    • 通过 innerText 获取的文本会自动去掉 HTML 标签,并且会根据 CSS 样式调整,例如,隐藏的元素内容(如 display: none)不会被返回。
    • 当使用 innerText 设置内容时,会自动编码内容,任何 HTML 标签会被当作普通文本处理,不能插入 <script> 或其他标签,避免了 XSS 攻击(跨站脚本攻击)。
  • textContent
    • textContent 获取的是元素内所有文本内容,包括隐藏元素的文本内容。如果一个元素是通过 CSS 隐藏的(例如 display: none),它的文本内容仍然会被返回。
    • textContent 通常比 innerText 快,因为它没有考虑 CSS 样式,它只会返回或设置纯文本。
  • innerHTML
    • 通过 innerHTML 获取的内容是元素内部的HTML 代码,包括标签、文本和子元素。
    • 由于 innerHTML 会直接插入 HTML 标签,它存在一定的安全风险。例如,如果你直接插入用户输入的内容,可能会导致 XSS(跨站脚本攻击)漏洞。

CSS动画

transform

矩阵变换来实现的
transform: translate:
  • translate(10px):水平方向右移,百分比也可以
  • translate(10px, 20px):水平右和垂直下移动,百分比也可以
tranform: rotate:
  • rotate(30deg):整数顺时针,负数逆时针
transform-origin:
  • top left:左上角为原点进行旋转
transform: scale:
  • 缩放

transition

为动画添加过渡效果:
notion image
背景色、宽高、阴影都可以用transform
缓动函数:
notion image
notion image
阶跃缓动函数:
notion image

用grid实现响应式布局

在grid布局中,fr用于分配可用空间,1fr表示一等份的可用空间,可以用来创建自适应网格布局
  • 这样表示将网格平均分成4列
  • 也可以直接写grid-template-columns: repeat(4, 1fr)
响应式:
  • 表示每个网格列最小260px,最大1fr
  • auto-fill:表示自动填充尽可能多的列,每列至少260px,每列都占1fr
子元素的布局,比如说实现第一个子元素占两行两列,其他的平均分配:
  • grid-row:start/end:表示从第start行开始到第end行结束,不包括end
  • grid-column:操作列,也一样

浏览器多进程/JS单线程

浏览器多进程和渲染进程多线程

  • 浏览器是多进程的
  • 浏览器之所以能够运行,是因为系统给它的进程分配了资源(cpu、内存)
  • 简单点理解,每打开一个Tab页,就相当于创建了一个独立的浏览器进程。
浏览器的进程:
在浏览器中打开一个网页相当于新起了一个进程,进程内有自己的多线程。但有时候浏览器会将多个进程合并,例如打开多个空白标签页,就会被合并成同一个进程。
  • Browser进程(控制进程):浏览器的主进程,只有一个
    • 负责浏览器界面的显示,与用户交互。如前进、后退等
    • 负责各个页面的管理,创建和销毁其他进程
    • 将Renderer进程得到的内存中的Bitmap绘制到用户界面上
    • 网络资源的管理和下载等
  • 浏览器渲染进程(Renderer进程/浏览器内核):内部是多线程的,默认每个Tab页面一个进程,互不影响
    • 页面渲染、脚本执行、事件处理
  • 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建
浏览器多进程的优势:
如果浏览器是单进程,那么某个Tab页崩溃了,就影响了整个浏览器,体验有多差;同理如果是单进程,插件崩溃了也会影响整个浏览器。
渲染进程:
页面渲染、JS的执行、事件循环等,都在这个进程内进行。
浏览器的渲染进程是多线程的:
  • GUI渲染线程
    • 负责渲染浏览器界面,解析HTML、CSS、构建渲染树、布局和绘制等
    • 当界面需要重绘或由于某种操作引发回流时,该线程就会执行
    • GUI渲染与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行
      • 这是因为js是可以操作dom的,如果在修改这些元素属性的同时渲染界面,那么渲染线程前后获得的元素数据就可能不一致了
  • JS引擎线程
    • 也称为JS内核,负责处理JS脚本程序,如chrome的V8引擎
    • 负责解析JS脚本、运行代码,JS引擎一直等待任务队列中任务的到来,然后加以处理
    • 一个Tab页(Renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
    • 由于GUI渲染线程和JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会导致页面的渲染不连贯,导致页面渲染加载阻塞
  • 事件触发线程
    • 用来控制事件循环
    • 当JS引擎执行代码块如setTimeOut时(也可以来自浏览器内核的其他进程,如鼠标点击/Ajax异步请求等),会将对应任务添加到事件线程中
    • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
    • 由于JS引擎的单线程,这些事件都必须排队等待JS引擎的处理
  • 定时触发器线程
    • setInterval与setTimeout所在的线程
    • 浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
  • 异步HTTP请求线程
notion image

Browser进程和浏览器内核(Renderer进程)的通信过程

如果自己打开任务管理器,然后打开一个浏览器,就可以看到:任务管理器中出现了两个进程(一个是主控进程,一个则是打开Tab页的渲染进程)
  1. Browser进程收到了用户请求,首先需要获取页面内容。随后将该任务通过RendererHost接口传递给Rederer进程
  1. Renderer进程的Renderer接口收到消息,简单解释后交给渲染线程,然后开始渲染,期间可能需要Browser进程获取资源和需要GPU进程来帮助渲染。最后将结果传递给Browser进程
  1. Browser进程接收到结果并将结果绘制出来

WebWorker,JS的多线程?

解决js执行时间过长导致的页面阻塞,增强js对cpu密集型计算的能力
  • 创建Worker时,JS引擎向浏览器申请开一个子线程(这个子线程是浏览器开的,完全受主线程控制,而且不能操作dom)
  • JS引擎线程与worker线程间通过特定的方式通信
  • 有非常耗时的计算工作时,可以单独开一个Worker线程,这样里面不管如何翻天覆地都不会影响JS引擎主线程,只等到计算出结果后,将结果通信给主线程
而且注意下,JS引擎是单线程的,这一点的本质仍然未改变,Worker可以理解是浏览器给JS引擎开的外挂,专门用来解决那些大量计算问题。

CSS加载阻塞?

这里是指的在头部引入的情况(link标签)
CSS是由单独的下载线程异步下载的:
  • CSS加载不会阻塞dom树解析,异步加载时dom照常构建
  • 但会阻塞render树的渲染,因为render树需要cssom
原因:
因为你加载css的时候,可能会修改下面DOM节点的样式,如果css加载不阻塞render树渲染的话,那么当css加载完之后,render树可能又得重新重绘或者回流了,这就造成了一些没有必要的损耗。所以干脆就先把DOM树的结构先解析完,把可以做的工作做完,然后等你css加载完之后,在根据最终的样式来渲染render树,这种做法性能方面确实会比较好一点。

浏览器渲染过程

例子:

dom和cssom的构建

简单来说就是把HTML文档和样式表都转换为树状结构,因为HTML和Css都是嵌套的,浏览器在计算任何节点的样式时,它会从适用于该节点的最通用(顶层)的规则开始计算。比如说要计算的节点是body元素的子元素,它就会先应用body的样式,之后会一层一层进行递归该过程,最终得到该节点的样式。
HTML Parser:主线程上的同步解析过程,遇到img等资源标签会触发异步加载,但不会阻塞解析
Css Parse和Dom Parse的并行关系?
如果Css是写在style标签中的,那么在chrome中style标签会被HTML Parse来解析,此时Css Parse和Dom Parse就不是并行的。
但是大多数情况下,Css文件都会使用外部链接的方式进行引入,当使用link标签引入Css文件时,由于Cssom的生成时机并不会影响DomTree的改变,因此当HTML Parse遇到link标签的stylesheet时并不会等待stylesheet下载并解析完毕后才解析后续dom,而是在网络进程加载style脚本的同时可以继续去解析后续dom,这时dom和cssom就是一个并行关系/非阻塞关系
所以,通常我们所说 Dom 和 Cssom 生成的非阻塞更多的是指加载样式脚本并不会阻塞后续 Dom 解析(这里的非阻塞更多相对于 JS 文件来说,因为同步 JS 文件的加载是会阻塞后续 Dom 构建的),而主线程解析生成 cssom 的过程一定是会和 parse Html 抢占主线程资源的(主线程并不会同时 parse Html 以及同时 parse stylesheet)。

生成渲染树

上述过程中基于HTML和CSS构建了dom树和cssom树,此时两棵树都还是互相独立的两个树状对象。
上述过程结束后,浏览器会将两个Tree进行合并,最终组成一个具有所有可见节点样式和内容的render tree。
  • 一些脚本标签、元标签等节点是不可见的,由于它们未反映在页面的呈现中所以会被被省略。
  • 同时对于一些通过 CSS 隐藏的节点,也会从渲染树中省略。比如,上述 HTML 中的 span 节点在上面的例子中会在渲染树中丢失,因为它明确的设置了 “display: none” 属性。
  • visibility: hidden 不同于 display: none. 前者使元素不可见,但元素在布局中仍然占据空间(渲染为空框),而后者display: none表示将元素从渲染树中完全移除,使元素不可见从而不是布局的一部分。
最终,经过上述步骤浏览器会组装 DomTree 和 CssomTree 成为 RenderTree,RenderTree 中包含屏幕上所有可见内容的内容和样式信息

布局

render tree构建完成后,就会进入布局Layout阶段了。此时浏览器已经明确地清楚哪些节点应该被渲染到页面上,同时也获得了可见节点的样式,但是浏览器还没有计算出每个节点在对应设备上确切的位置和大小,这一步就是Layout阶段应该做的事。
布局确定了树中所有节点的宽度、高度和位置信息。

绘制

渲染树和布局完成后,像素就可以被绘制在屏幕上。在绘制阶段,浏览器将布局阶段计算的每个 frame 转为屏幕上实际的像素点。包括将元素的可见部分进行绘制,比如文本、颜色、边框、阴影、替换元素等。

js阻塞?

内联 js:
  • 对于内联脚本,JS不存在加载,但是当浏览器对HTML进行解析时解析到script脚本之后,会将主线程交给JS引擎线程去执行js,这样GUI渲染线程就被JS抢走了,自然无法进行渲染了
  • 因此JS的解析和执行一定会阻塞页面的渲染
外链 js:
  • JS脚本在顶部
    • 将外部标签卸载head标签之后是会阻塞后续DOM渲染的
      • 解析到script标签,开始下载资源,渲染阻塞
      • 停止dom解析
      • 执行js代码
      • js执行完成恢复解析,此时才开始构建渲染树
  • JS脚本在底部
    • 在执行js之前浏览器已经进行了首次绘制,此时外部脚本的加载和执行只是会影响后续dom的解析的渲染,对于脚本之前的dom并不会阻塞它的解析以及渲染
async 和 defer:
  • async
    • 浏览器解析到 <script async> 时,会并行下载脚本(不阻塞 DOM 解析)
    • 脚本下载完成后立即暂停 DOM 解析,执行脚本,执行完毕后再恢复解析
      • 虽然下载的时候不阻塞 DOM 解析,但是执行的时候会阻塞
    • 多个 async 脚本的执行顺序与它们在文档中的顺序无关,取决于下载完成的先后顺序
    • 应用场景:独立脚本,如广告脚本等,不依赖 DOM 或其他的脚本
  • defer
    • 浏览器解析到 <script defer> 时,会并行下载脚本(不阻塞 DOM 解析)
    • 脚本的执行会被推迟到 DOM 解析完成之后(DOMContentLoaded 事件触发前)
      • 脚本的下载和执行都不会阻塞 DOM 解析
    • 多个 defer 脚本按文档中的顺序执行,即使后面的脚本先下载完成
    • 应用场景:依赖 DOM 或其他脚本的代码(如需要操作 DOM 的库)
  • 浏览器内核
    • 浏览器网络线程池负责下载脚本,与主线程(负责解析/渲染)并行

TS单例

有一个实例之后,用new就不能创建一个新实例了
微信小程序:vue+uniappVue3
  • Giscus