Web Workers

在浏览器和服务器上,JavaScript程序在单个处理线程上运行。这意味着程序一次只能做一件事。简单来说,你的新电脑可能有32核的CPU,但当JavaScript应用程序运行时,其中31个核心都处于空闲状态。 JavaScript的单线程避免了复杂的并发情况。如果两个线程同时尝试进行不兼容的更改会发生什么?例如,一个浏览器可能在另一个线程重定向到新的URL并从内存中清除该文档时正在更新DOM。Node.js、Deno和Bun都继承了浏览器的单线程引擎。 这不是JavaScript特定的限制。大多数语言都是单线程的,但像PHP和Python这样的Web选项通常在Web服务器上运行,为每个用户请求启动解释器的新线程的不同实例。这会消耗资源,因此Node.js应用程序通常定义自己的Web服务器,该服务器在单个线程上运行,并异步处理每个传入的请求。 Node.js的方法在处理高流量负载时可能更高效,但长时间运行的JavaScript函数会消耗效率收益。 在我们演示如何使用Web Workers解决执行速度问题之前,我们首先要了解JavaScript的运行方式以及为什么长时间运行的函数会引发问题。 JavaScript非阻塞I/O事件循环 你可能会认为一次只做一件事会导致性能瓶颈,但JavaScript是异步的,这就避免了大部分单线程处理问题,因为: 不需要等待用户在网页上点击按钮。 浏览器触发一个事件,当点击发生时调用JavaScript函数。 不需要等待Ajax请求的响应。 浏览器触发一个事件,在服务器返回数据时调用JavaScript函数。 Node.js应用程序不需要等待数据库查询的结果。 运行时在数据可用时调用JavaScript函数。 JavaScript引擎运行一个事件循环。一旦代码的最后一条语句执行完毕,运行时会循环回来,检查是否有未完成的定时器、待处理的回调函数和数据连接,然后根据需要执行回调函数。 其他操作系统处理线程负责调用诸如HTTP请求、文件处理和数据库连接等输入/输出系统。它们不会阻塞事件循环。事件循环可以继续执行下一个在队列中等待的JavaScript函数。 实质上,JavaScript引擎的主要责任是运行JavaScript代码。操作系统处理所有其他可能导致引擎调用JavaScript函数的I/O操作。 长时间运行的JavaScript函数 JavaScript函数通常由事件触发。它们会进行一些处理,输出一些数据,并且大部分情况下会在毫秒级别内完成,以便事件循环可以继续运行。 不幸的是,一些长时间运行的函数可能会阻塞事件循环。想象一下,你正在开发自己的图像处理函数(如锐化、模糊、灰度化等)。异步代码可以从文件中读取(或写入)数百万字节的像素数据,这对JavaScript引擎影响很小。然而,处理图像的JavaScript代码可能需要几秒钟来计算每个像素。该函数会阻塞事件循环,直到它完成,其他JavaScript代码无法运行。 在浏览器中,用户将无法与页面进行交互。他们无法点击、滚动或输入,并且可能会看到一个“脚本无响应”的错误,其中有停止处理的选项。 对于Node.js服务器应用程序而言,情况更糟糕。在函数执行时,它无法响应其他请求。如果函数需要花费10秒钟来完成,那么在那个时间点访问的每个用户都必须等待最多10秒钟,即使他们没有处理图像。 你可以通过将计算拆分为较小的子任务来解决这个问题。下面的代码使用传递的imageFn函数,最多处理1,000个像素(从一个数组中)。然后,它使用1毫秒的setTimeout延迟调用自身。事件循环的阻塞时间较短,这样JavaScript引擎可以在迭代之间处理其他传入的事件: // pass callback function, image processing function, and input image // return an output image to a callback function function processImage( callback, imageFn = i => {}, imageIn = [] ) { const chunkSize = 1000; // pixels to process per iteration // process first chunk let imageOut = [], pointer = 0; processChunk(); // process a chunk of data function processChunk() { const pointerEnd = pointer + chunkSize; // pass chunk to image processing function imageOut = imageOut.concat( imageFn( imageIn.slice( pointer, pointerEnd ) ) ); if (pointerEnd < imageIn.length) { // process next chunk after a short delay pointer = pointerEnd; setTimeout(processChunk, 1); } else if (callback) { // complete - return output image to callback function callback( null, imageOut ); } } } 这种方法可以防止脚本无响应,但不总是切实可行的。单个执行线程仍然会执行所有的工作,即使CPU可能有更多的处理能力。为了解决这个问题,我们可以使用Web Workers。 ...

October 16, 2024

离线存储

HTML5 离线存储是指通过 Web Storage API 提供的一种在客户端本地存储数据的机制,主要有两种方式:localStorage 和 sessionStorage。与传统的 Cookie 相比,Web Storage 提供了更大容量、灵活性,并且数据不会随着 HTTP 请求自动发送到服务器,这可以优化性能和安全性。此外,HTML5 还支持 离线应用缓存(Application Cache) 和 IndexedDB,用于更加复杂的数据存储需求和离线应用。 工作原理 HTML5 的离线存储主要通过以下几个机制来实现数据存储和访问: localStorage:一种持久性存储机制,数据存储在用户的浏览器中,即使关闭浏览器或重新加载页面,数据也不会丢失,除非明确被删除。其特点是跨页面、跨会话存储数据。 sessionStorage:会话级别的存储机制,只在当前页面的会话期间有效。当会话结束或浏览器窗口关闭时,数据会自动删除。其特点是只能在当前窗口或标签页中存储数据,不能跨窗口或标签页共享。 Application Cache(已被 Service Worker 替代):允许开发者将网站的某些资源缓存到本地,使得用户即使在离线的情况下也能访问特定页面。虽然该机制已被弃用,但它曾经是实现离线访问的核心。 IndexedDB:一种更为复杂的浏览器内置数据库,支持存储结构化数据,如对象和文件。它适用于更大规模的数据存储需求,并且允许通过索引进行高效的查询和数据操作。 使用方式 1. localStorage 特点:持久性存储,浏览器关闭后数据依然存在,除非手动清除。 使用方式: // 存储数据 localStorage.setItem('key', 'value'); // 获取数据 let value = localStorage.getItem('key'); // 删除数据 localStorage.removeItem('key'); // 清空所有 localStorage localStorage.clear(); 2. sessionStorage 特点:仅在当前会话期间有效,浏览器关闭或页面重新加载后数据会被清除。 使用方式: // 存储数据 sessionStorage.setItem('sessionKey', 'sessionValue'); // 获取数据 let sessionValue = sessionStorage.getItem('sessionKey'); // 删除数据 sessionStorage.removeItem('sessionKey'); // 清空所有 sessionStorage sessionStorage.clear(); 3. IndexedDB 特点:适合存储大量的结构化数据,支持异步 API 和事务。 使用方式: IndexedDB 的使用较为复杂,通常涉及打开数据库、创建事务、存储和检索数据等操作。一个简单的示例如下: let request = indexedDB.open('myDatabase', 1); request.onupgradeneeded = function(event) { let db = event.target.result; let objectStore = db.createObjectStore('myObjectStore', { keyPath: 'id' }); }; request.onsuccess = function(event) { let db = event.target.result; let transaction = db.transaction(['myObjectStore'], 'readwrite'); let objectStore = transaction.objectStore('myObjectStore'); objectStore.add({ id: 1, name: 'John Doe' }); }; 4. Application Cache(弃用) 特点:通过 manifest 文件指定需要缓存的资源,允许在离线时访问应用。 使用方式: 在 HTML 文档中指定 manifest 文件: <html manifest="app.manifest"> 然后在 app.manifest 文件中列出需要缓存的资源: ...

October 16, 2024

实体字符(转义字符)

HTML 的实体字符(HTML Entities)是用于显示某些特殊字符或符号的编码形式。 HTML 中某些符号具有特殊含义,例如 <, >, & 等字符不能直接在网页内容中使用,所以需要使用实体字符来表示它们。 常见的 HTML 实体字符 空格和控制字符 &nbsp;:空格字符 (non-breaking space) &nbsp; <!-- 表示一个空格 --> &lt;:小于号 < &lt;div&gt; <!-- 表示 <div> --> &gt;:大于号 > &gt;/div&gt; <!-- 表示 </div> --> &amp;:与符号 & &amp; <!-- 表示 & --> &quot;:双引号 " &quot;Hello&quot; <!-- 表示 "Hello" --> &apos;:单引号 ' &apos;Hello&apos; <!-- 表示 'Hello' --> 数学符号 &plus;:加号 + &minus;:减号 − &times;:乘号 × &divide;:除号 ÷ 货币符号 &dollar;:美元符号 $ &euro;:欧元符号 € &pound;:英镑符号 £ &yen;:日元符号 ¥ 其他常见符号 &copy;:版权符号 © &reg;:注册商标符号 ® &trade;:商标符号 ™ &sect;:章节符号 § &deg;:度数符号 ° 希腊字母 &alpha;:α &beta;:β &gamma;:γ &delta;:δ 为什么需要实体字符? 避免 HTML 解析冲突:例如 <, >, & 等符号在 HTML 中有特定的用途,用实体字符可以避免冲突。 显示特殊符号:例如版权符号、商标符号或货币符号等,不能直接在键盘上输入,使用实体字符可以确保正确显示。 常见考点 1. HTML 实体字符的定义与作用 什么是 HTML 实体字符?为什么需要使用它们? 实体字符用于在 HTML 中显示特殊符号(如 <, >, &, " 等)或不可见字符(如空格 、换行符等),避免与 HTML 语法冲突。 2. 常见的 HTML 实体字符 &lt;:小于号 < &gt;:大于号 > &amp;:与符号 & &quot;:双引号 " &apos;:单引号 ' &nbsp;:不间断空格 3. Unicode 和 ASCII 编码的实体字符 HTML 支持通过 &#xNNNN;(十六进制)或 &#NNNN;(十进制)表示 Unicode 字符。例如: &#x2665; 或 &#9829; 表示 ♥ 考察点可能涉及通过实体字符显示 Unicode 表情符号、语言字符等。 4. 转义字符 在 HTML 中如何使用实体字符进行转义? 为什么需要转义符号(如 <, >, &)?特别是在 HTML 元素内嵌 JavaScript 或样式时需要避免歧义。 安全性方面的考量,尤其是防止 XSS(跨站脚本攻击)。 5. 空白字符 &nbsp;(不间断空格):防止自动换行或保留多个空格。 &ensp; 和 **&emsp;**:半角和全角空格符号。 实体空白符在排版和布局中的应用(如对齐、占位)。 6. 其他常见实体字符 货币符号:如 &yen;(¥)、&euro;(€)。 数学符号:如 &plusmn;(±)、&times;(×)、&divide;(÷)。 特殊符号:如 &copy;(©)、&reg;(®)、&trade;(™)。 7. HTML5 实体字符扩展 HTML5 中新增的一些实体字符,如: **&le;**(≤)和 &ge;(≥) &bull;(•)用于列表符号 &hellip;(…)省略号 8. 实体字符与浏览器兼容性 浏览器如何处理不支持的实体字符? 考察点可能涉及浏览器对不同编码的支持,以及如何确保兼容性。 9. HTML 实体字符与国际化 如何通过实体字符支持多语言文本? 考察 Unicode 与实体字符在国际化中的应用,如如何使用实体字符表示非 ASCII 字符。 10. HTML 实体字符在输入表单中的应用 在表单中,如何防止特殊字符输入导致 HTML 注入攻击? 使用实体字符保护表单数据的完整性和安全性。

October 15, 2024

HTML 常见属性

HTML 元素的属性是用于控制元素行为或样式的附加信息。 以下是一些常见的 HTML 属性: 1. 通用属性(Global Attributes) 这些属性几乎可以应用于所有 HTML 元素: id:元素的唯一标识符,用于 JavaScript 或 CSS 选择。 <div id="main"></div> class:为元素指定一个或多个类名,用于 CSS 样式或 JavaScript 操作。 <div class="container header"></div> style:为元素设置内联样式。 <div style="color: red;"></div> title:为元素提供额外信息,当鼠标悬停在元素上时显示。 <button title="Click me!">Click</button> data-*:自定义属性,用于存储私有数据,以 data- 为前缀,可以通过 JavaScript 访问。 <div data-user-id="123"></div> 2. 事件属性(Event Attributes) 这些属性用于处理元素上的事件: onclick:定义单击事件处理函数。 <button onclick="alert('Clicked!')">Click me</button> onmouseover:鼠标悬停时触发的事件。 <div onmouseover="console.log('Mouse over')"></div> onkeydown:按下键盘时触发的事件。 <input type="text" onkeydown="handleKeyDown(event)"> 3. 表单相关属性 type:定义输入框类型,例如 text、password、email 等。 <input type="text"> name:定义表单元素的名称,用于表单提交。 <input type="text" name="username"> value:定义表单元素的默认值。 <input type="text" value="Default value"> placeholder:为输入框提供占位符文本,提示用户输入。 <input type="text" placeholder="Enter your name"> disabled:禁用输入元素,使其不可交互。 <input type="text" disabled> required:标记表单元素为必填项。 <input type="text" required> 4. 链接和资源加载属性 href:用于指定超链接的目标 URL。 <a href="https://example.com">Go to example.com</a> src:指定图像、脚本或其他资源的路径。 <img src="image.jpg" alt="Image description"> alt:为图像提供替代文本,图像无法加载时显示。 <img src="image.jpg" alt="Alternative text"> rel:指定链接与目标文档的关系,常用于 link 标签和 a 标签。 <link rel="stylesheet" href="style.css"> target:定义链接的打开方式,常用值包括 _self(当前窗口)和 _blank(新窗口)。 <a href="https://example.com" target="_blank">Open in new tab</a> 5. 多媒体属性 controls:为 <audio> 或 <video> 元素添加播放控件。 <video src="video.mp4" controls></video> autoplay:自动播放音频或视频。 <audio src="audio.mp3" autoplay></audio> loop:音频或视频播放完后重新开始播放。 <video src="video.mp4" loop></video> muted:自动静音视频或音频。 <audio src="audio.mp3" muted></audio> 6. 图像和媒体相关属性 width 和 height:定义图像或视频的宽度和高度。 <img src="image.jpg" width="200" height="100"> 7. iframe 属性 src:定义嵌入的网页 URL。 <iframe src="https://example.com"></iframe> sandbox:为嵌入内容提供更严格的限制,控制 iframe 的行为。 <iframe src="https://example.com" sandbox></iframe> 8. 语义化属性 role:为屏幕阅读器和辅助技术提供语义化描述。 <div role="navigation"></div> 9. 布局和可访问性属性 hidden:隐藏元素,元素不会显示在页面中。 <div hidden>This content is hidden</div> tabindex:定义元素的 tab 键导航顺序。 <button tabindex="1">First</button> <button tabindex="2">Second</button> 常见考点 1. 全局属性(Global Attributes) class:用于给元素指定样式类,如何使用多个类?有哪些常见用法? id:唯一标识元素,id 的作用和注意事项。 style:内联样式的作用及其优先级。 title:为元素提供额外信息(鼠标悬停时显示)。 data- 属性*:自定义数据属性,如何在 JavaScript 中访问 data-* 属性? 2. 事件属性(Event Attributes) onclick、onmouseover 等事件处理属性。 如何使用事件属性进行事件绑定? 事件属性和 JavaScript 事件监听器(addEventListener)的区别和应用场景。 3. 表单属性 name:用于提交表单数据时作为键名。 action:表单提交的目标 URL。 method:表单的提交方式(GET 或 POST)。 autocomplete:是否启用表单自动填充。 required、pattern、min/max 等 HTML5 校验属性。 4. 输入控件相关属性 value:为输入控件设置默认值。 placeholder:为输入框提供提示文本。 disabled:禁用输入控件。 readonly:只读属性,控件不能被修改。 maxlength/minlength:输入字符长度的限制。 accept:文件上传时,限制允许的文件类型(适用于 <input type="file">)。 5. 媒体元素属性 src:媒体文件的路径(适用于 <img>、<audio>、<video> 等)。 alt:图片无法加载时显示的替代文本。 controls:是否为媒体文件显示默认控制条。 autoplay:媒体文件是否自动播放。 loop:媒体文件是否循环播放。 muted:媒体文件是否静音。 6. 超链接相关属性 href:链接目标的 URL。 target:定义链接打开的方式(如 _blank 打开新窗口)。 download:允许下载链接资源,而不是导航到资源。 rel:定义链接与当前文档的关系(如 nofollow、noopener)。 7. iframe 属性 src:嵌入的文档或网页的 URL。 sandbox:启用或禁用特定行为(如禁止脚本、表单提交等)。 allow:控制 iframe 内的权限(如允许摄像头、麦克风访问)。 8. 图像和图形属性 srcset:为不同设备提供不同分辨率的图像资源。 sizes:配合 srcset,定义不同视口下图像显示的尺寸。 usemap:指定图片使用的图像映射。 height/width:定义图片或其他元素的高度和宽度。 9. meta 标签属性 charset:指定文档的字符编码(如 UTF-8)。 name 和 content:为文档提供元信息(如 description, viewport)。 http-equiv:配合 content,定义 HTTP 头信息(如 refresh, Content-Type)。 10. 无障碍相关属性 aria- 属性*:为无障碍工具(如屏幕阅读器)提供更多语义信息。 role:指定元素的功能角色,增强无障碍性。 11. iframe 和图片的懒加载 loading=“lazy”:为图片和 iframe 启用懒加载。 12. 语言和方向属性 lang:指定元素或页面的语言(如 lang="en")。 dir:指定文本的书写方向(如 ltr 左到右,rtl 右到左)。

October 15, 2024

HTML 元素分类

根据标签的功能和表现,HTML 标签主要分为以下几类: 1. 块级元素(Block-level Elements) 块级元素通常独占一行,并且可以包含其他块级元素或内联元素,能够扩展至父容器的全部宽度。 特点: 独占一行,默认情况下会在前后产生换行。 宽度默认是父元素的 100%。 可以设置 width、height、margin、padding 等布局属性。 常见的块级元素: <div>:无特定语义的容器,常用于布局。 <p>:段落标签。 <h1> 到 <h6>:标题标签,按重要性递减。 <ul>、<ol>、<li>:无序、有序列表及其项。 <header>、<footer>、<section>、<article>:HTML5 语义化标签,分别表示页头、页尾、内容区块等。 示例: <div> <h1>标题</h1> <p>这是一个段落。</p> </div> 2. 内联元素(Inline Elements) 内联元素与其他元素排列在同一行,且只占用其内容所需的宽度。它们不会独占一行,不能包含块级元素。 特点: 与相邻的内联元素排列在同一行。 仅占据自身内容的宽度,不会在前后产生换行。 不能设置 width、height(无效),但可以设置 padding 和 margin,只影响水平方向。 常见的内联元素: <span>:无特定语义的容器,常用于局部样式。 <a>:超链接。 <strong>:表示强调的文本,通常加粗显示。 <em>:表示强调的文本,通常斜体显示。 <img>:图像标签。 <input>:表单输入元素。 示例: <p>这是一个段落,<a href="#">点击这里</a> 了解更多。</p> 3. 内联块元素(Inline-block Elements) 内联块元素是同时具备内联元素和块级元素特性的元素。它们在布局上是内联的,但表现为块状元素,因此可以设置宽高。 特点: 与其他元素排列在同一行,但可设置宽度和高度。 不会自动换行。 常见的内联块元素: <img>:虽然图像显示在行内,但可以设置 width 和 height。 <button>:按钮。 <input type="text">:文本输入框。 CSS 设置为 display: inline-block 的元素。 示例: <span style="display: inline-block; width: 100px; height: 100px; background: red;"></span> 4. 自闭合元素(Self-closing Elements) 自闭合元素是没有闭合标签的元素,因为它们没有任何内容。这类标签通常用于插入内容或进行控制,而不包含其他内容。 ...

October 15, 2024

表单验证

HTML 表单是用于收集用户输入数据的重要元素,通常用于提交到服务器进行处理。它包括一系列控件,如文本输入框、按钮、复选框、单选按钮、文件上传等。表单通过 form 标签定义,用户可以通过这些控件与网页进行交互。 1. 基础结构 表单通过 <form> 标签创建,表单的 action 属性定义数据提交的目标 URL,method 属性定义提交方式(如 GET 或 POST)。 <form action="/submit" method="POST"> <input type="text" name="username"> <input type="submit" value="Submit"> </form> 2. 表单控件 HTML 表单提供了多种用户输入控件: 文本输入 (<input type="text">, <textarea>) 密码输入 (<input type="password">) 单选框和复选框 (<input type="radio">, <input type="checkbox">) 下拉列表 (<select>) 按钮 (<button>, <input type="submit">) 文件上传 (<input type="file">) 隐藏输入 (<input type="hidden">) 3. 表单的属性 action:指定表单数据提交到的 URL。 method:定义提交方式,常用 GET(将数据附在 URL 后)和 POST(将数据放入 HTTP 请求体中)。 enctype:指定数据的编码类型,常用于文件上传(如 multipart/form-data)。 autocomplete:启用或禁用浏览器的自动完成功能。 4. 表单校验 HTML5 引入了许多新的表单校验功能,能够减少 JavaScript 校验代码的复杂度。校验功能包括: ...

October 14, 2024

跨域通信

什么是跨域 在了解跨域之前,我们必须要了解一下同源策略。 跨域问题其实就是浏览器的同源策略造成的。 同源策略 同源策略限制了从同一个源加载的文档或脚本如何与另一个源的资源进行交互。这是浏览器的一个用于隔离潜在恶意文件的重要的安全机制。同源指的是:协议、端口号、域名必须一致。 跨源资源共享([CORS],或通俗地译为跨域资源共享)是一种基于 [HTTP] 头的机制,该机制通过允许服务器标示除了它自己以外的其他[源] (域、协议或端口),使得浏览器允许这些源访问加载自己的资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的“预检”请求。在预检中,浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。 为什么会产生跨域 下表给出了与 URL wmq.com/dir/page.ht… 的源进行对比的示例: URL 是否跨域 原因 wmq.com/dir/page.ht… 同源 完全相同 wmq.com/dir/inner/a… 同源 只有路径不同 wmq.com/secure.html 跨域 协议不同 wmq.com:81/dir/etc.htm… 跨域 端口不同 ( http:// 默认端口是80) news.company.com/dir/other.h… 跨域 主机不同 同源策略:protocol(协议)、domain(域名)、port(端口)三者必须一致。 同源政策主要限制了三个方面: 当前域下的 js 脚本不能够访问其他域下的 cookie、localStorage 和 indexDB。 当前域下的 js 脚本不能够操作访问操作其他域下的 DOM。 当前域下 ajax 无法发送跨域请求。 源政策的目的主要是为了保证用户的信息安全,它只是对 js 脚本的一种限制,并不是对浏览器的限制,对于一般的 img、或者script 脚本请求都不会有跨域的限制,这是因为这些操作都不会通过响应结果来进行可能出现安全问题的操作。 有哪些解决跨域的办法 CORS CORS: 通过设置服务器端的响应头来允许跨域请求。这需要在服务器端进行配置,以允许特定的来源访问资源。例如,在Node.js的Express框架中,可以使用以下代码来设置CORS: const express = require('express'); const app = express(); app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); // 允许所有来源访问 res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); // 允许的请求头 res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); // 允许的请求方法 next(); }); // 其他路由和中间件 app.listen(3000, () => { console.log('Server is running on port 3000'); }); 减少OPTIONS请求次数 在跨域请求中,浏览器会自动处理一些非简单请求的预检流程,这包括"预检请求"(也被称为CORS预检请求)和"OPTIONS请求"。预检请求是发起一个HTTP OPTIONS请求到服务器,以确认目标资源是否支持跨域。这种机制是为了兼容同源策略而产生的,但有时这种额外的请求会导致性能问题。 ...

October 14, 2024

优化性能

前言 随着互联网发展至今,对于网站来说,性能显的越来越重要了,CSS作为页面渲染和内容展现的重要环节,影响着用户对整个网站的第一体验。所以,我们需要重视与CSS相关的性能优化。 项目开发初期我们可能因为各种原因(很大一部分原因是因为项目工期,产品往往把项目上线时间卡的死死的,根本不听你说的什么性能优化),怎么写的舒服就怎么来,对于性能优化我们常常在项目完成时才去考虑,经常被推迟到项目的末期,甚至到暴露出严重的性能问题时才进行性能优化。 为了更多地避免这一情况,首先要重视起性能优化相关的工作,将其贯穿到整个产品设计与开发中。其次,就是了解性能相关的内容,在项目开发过程中,自然而然地进行性能优化。 css渲染规则 想要优化CSS的性能,我们首先需要了解CSS的渲染规则,CSS选择器是从右向左进行匹配的 来看个例子🌰: .nav h3 a{font-size: 14px;} 渲染过程大概是:首先找到所有的a,沿着a的父元素查找h3,然后再沿着h3,查找.nav。中途找到了符合匹配规则的节点就加入结果集。如果找到根元素html都没有匹配,则不再遍历这条路径,从下一个a开始重复这个查找匹配(只要页面上有多个最右节点为a)。 Tips:为什么CSS选择器是从右向左匹配的? CSS中更多的选择器是不会匹配的,所以在考虑性能问题时,需要考虑的是如何在选择器不匹配时提升效率。从右向左匹配就是为了达成这一目的的,通过这一策略能够使得CSS选择器在不匹配的时候效率更高。这样想来,在匹配时多耗费一些性能也能够想的通了。 内联首屏关键CSS(Critical CSS) 性能优化中有一个重要的指标——首次有效绘制(First Meaningful Paint,简称FMP)即指页面的首要内容(primary content)出现在屏幕上的时间。这一指标影响用户看到页面前所需等待的时间,而 内联首屏关键CSS(即Critical CSS,可以称之为首屏关键CSS) 能减少这一时间。 很多人都喜欢通过link标签引用外部CSS文件。但需要知道的是,将CSS直接内联到HTML文档中能使CSS更快速地下载。而使用外部CSS文件时,需要在HTML文档下载完成后才知道所要引用的CSS文件,然后才下载它们。所以说,内联CSS能够使浏览器开始页面渲染的时间提前,因为在HTML下载完成之后就能渲染了。 但是我们不应该将所有的CSS都内联在HTML文档中,因为[初始拥塞窗口]存在限制(TCP相关概念,通常是 14.6kB,压缩后大小),如果内联CSS后的文件超出了这一限制,系统就需要在服务器和浏览器之间进行更多次的往返,这样并不能提前页面渲染时间。因此,我们应当只将渲染首屏内容所需的关键CSS内联到HTML中。 ⚠️还有一点需要注意的是内联CSS没有缓存,每次都会随HTML的加载而重新下载,但我们将内联首屏关键CSS控制在 14.6kB以内,它对性能优化还是起到正向作用的。(凡事有利也有弊) 异步加载非首屏CSS 我们需要知道两点内容:(具体可以看我之前的文章:这些浏览器面试题,看看你能回答几个?) CSS不会阻塞DOM的解析,但会阻塞DOM的渲染 CSS会阻塞JS执行,但不会阻塞JS文件的下载 由于CSS会阻塞DOM的渲染,所以我们将首屏关键CSS内联后,剩余的非首屏CSS内容可以使用外部CSS,并且异步加载,防止非首屏CSS内容阻塞页面的渲染。 CSS异步加载方式 第一种方法是动态创建 // 创建link标签 const myCSS = document.createElement( "link" ); myCSS.rel = "stylesheet"; myCSS.href = "mystyles.css"; // 插入到header的最后位置 document.head.insertBefore( myCSS, document.head.childNodes[ document.head.childNodes.length - 1 ].nextSibling ); 第二种方法是将link元素的media属性设置为用户浏览器不匹配的媒体类型(或媒体查询) 对浏览器来说,如果样式表不适用于当前媒体类型,其优先级会被放低,会在不阻塞页面渲染的情况下再进行下载。在首屏文件加载完成之后,将media的值设为screen或all,从而让浏览器开始解析CSS。 <link rel="stylesheet" href="mystyles.css" media="noexist" onload="this.media='all'"> 第三种方法是通过rel属性将link元素标记为alternate可选样式表 <link rel="alternate stylesheet" href="mystyles.css" onload="this.rel='stylesheet'"> 第四种方法是使用rel=preload来异步加载CSS <link rel="preload" href="mystyles.css" as="style" onload="this.rel='stylesheet'"> 注意,as是必须的。忽略as属性,或者错误的as属性会使preload等同于XHR请求,浏览器不知道加载的是什么内容,因此此类资源加载优先级会非常低。as的可选值可以参考上述标准文档。 看起来,rel="preload"的用法和上面两种没什么区别,都是通过更改某些属性,使得浏览器异步加载CSS文件但不解析,直到加载完成并将修改还原,然后开始解析。 但是它们之间其实有一个很重要的不同点,那就是使用preload,比使用不匹配的media方法能够更早地开始加载CSS。所以尽管这一标准的支持度还不完善,仍建议优先使用该方法。 CSS文件压缩 这应该是最容易想到的一个方法了,通过压缩CSS文件大小来提高页面加载速度。现在的构建工具,如webpack、gulp/grunt、rollup等也都支持CSS压缩功能。压缩后的文件能够明显减小,可以大大降低了浏览器的加载时间。 CSS层级嵌套最好不要超过3层 一般情况下,元素的嵌套层级不能超过3级,过度的嵌套会导致代码变得臃肿,沉余,复杂。导致css文件体积变大,造成性能浪费,影响渲染的速度!而且过于依赖HTML文档结构。这样的css样式,维护起来,极度麻烦,如果以后要修改样式,可能要使用!important覆盖。尽量保持简单,不要使用嵌套过多过于复杂的选择器。 删除无用CSS代码 一般情况下,会存在这两种无用的CSS代码:一种是不同元素或者其他情况下的重复代码,一种是整个页面内没有生效的CSS代码。 对于前者,在编写的代码时候,我们应该尽可能地提取公共类,减少重复。对于后者,在不同开发者进行代码维护的过程中,总会产生不再使用的CSS的代码,当然一个人编写时也有可能出现这一问题。而这些无用的CSS代码不仅会增加浏览器的下载量,还会增加浏览器的解析时间,这对性能来说是很大的消耗。所以我们需要找到并去除这些无用代码。 那么我们如何知道哪些CSS代码是无用代码呢? 谷歌的Chrome浏览器就有这种开箱即用的功能。只需转到查看>开发人员>开发人员工具,并在最近的版本中打开Sources选项卡,然后打开命令菜单。然后,点击Coverage,在Coverage analysis窗口中高亮显示当前页面上未使用的代码。 慎用*通配符 我们有时候可能会写下面这种代码来消除一些标签的默认样式或统一浏览器对标签渲染的差异化: *{ margin:0; padding:0; } 这样虽然代码量少,但它的性能可不是最佳的,我们最好还是写对应的标签选择器: ...

October 10, 2024

视觉格式化模型

大部分 Web 开发者都知道 CSS 中有一个盒模型,但很少有人知道在 CSS 的世界中还有另一个模型,他就是 CSS 的视觉格式化模型。 CSS 视觉格式化模型(英文称之Visual Formatting Model)。该模型主要是用来处理在视觉媒体上显示文档时使用的计算规则。仅从这一句话来描述,估计很多同学都会感到困惑,视觉格式化模型到底是什么鬼?大家先不用急着知道视觉格式化模型是什么,只需要知道它是 CSS 中很重要的一部分,如果理解了该部分所涉及到的知识点,将有助于你更好的理解和学习 CSS,特别是 Web 布局 什么是视觉格式化模型 在盒模型中我们了解到,在 CSS 的世界中,任何一个元素都是一个盒子,它具有一个专业术语,即盒模型。而视觉格式化模型会根据 CSS 盒模型将文档中的元素转换为一个个盒子。看到这里,很多同学可能更会感到困惑,“任何元素都是一个盒子,视觉格式化模型将元素转换为盒子,那这盒子是不是都是一样的盒子呢”? 首先要声明一点:视觉格式化模型和CSS盒模型不是同一个东西! 简单点说呢。Web 页面(文档树)是由很一个个盒子组成(因为任何元素都可以被视为是一个盒子),而视觉格式化模型却是一套规则,用来计算元素转换为盒子的规则。而页面的布局都由这些盒子的所处的各处位置组合而成。那么理解了元素怎么转成盒子的规则,就理解了 Web 页面是怎么布局。而每个盒子的布局主要由以下几个因素决定: 盒子的尺寸:精确指定、由约束条件指定或没有指定 盒子的类型:行内盒子(inline)、行内级盒子(inline-level)、原子行内级盒子(atomic inline-level)和块盒子(block) 定位方案:普通流定位、浮动定位或绝对定位 文档树中的其它元素:即当前盒子的子元素或兄弟元素 视窗尺寸与位置 所包含的图片的尺寸 其他的某些外部因素 先来看一张图,这个图是由下面的 HTML 结构构造出来的: <body> <header> <nav></nav> </header> <article> <p></p> <figure></figure> <p></p> </article> <aside></aside> <footer></footer> </body> 每个元素都是一个盒子,盒子中可以包含另一个或多个盒子。也正如上图所示,CSS 的视觉格式化模型会根据盒子的包含块(Containing Block,即包含其他盒子的块)的边界来渲染盒子。通常,盒子会创建一个包含其后代元素的包含块,但是盒子并不会被包含块限制住,盒子的布局会撑破包含块(即跑出包含块的边界),对于这种现象,在 CSS 中常被称为溢出。 溢出是一个很有意思的东东,在 Web 布局中时常会碰到,但在这一章中不做方面的阐述。 ...

October 10, 2024

架构

前言 在项目中经常存在如下问题: 1.模块拆分不合理 2.变量和函数命名不知所云 3.缺少注释或者是写了一堆描述不清的内容 4.重复的代码遍布各个角落等 因为这些不良的编程习惯,导致了项目越来越难以维护,程序性能越来越低,大大降低了日常的工作效率以及提高了公司的开发成本。 下面就以CSS在Vue3项目中的架构为切入点,通过减少CSS代码的冗余度和增强CSS代码的维护性、扩展性来提高我们的编程能力和项目架构能力。 CSS的设计模式 在学习CSS架构之前,我们先简单看一下常见的5种CSS设计模式,这些设计模式都为我们的CSS架构提供了一定的开发思路。 1.OOCSS模式 OOCSS(Object-Oriented CSS)字面意思是面向对象的CSS,在开发中它有如下的规范约定 减少对 HTML 结构的依赖 # bad # 1.匹配效率低,影响css性能 # 2.和html耦合度高,维护性和扩展性低 .container-list ul li a {} <div class="container-list"> <ul> <li> <a>...</a> </li> </ul> </div> # good .container-list .list-item {} <div class="container-list"> <ul> <li> <a class="list-item">...</a> </li> </ul> </div> 增加样式的复用性 .label { # 公共代码 } .label-danger { # 特定代码 } .label-info { # 特定代码 } <div> <p class="label label-danger"></p> <p class="label label-info"></p> </div> 2.BEM模式 BEM 是进阶版的OOCSS,是一个分层系统,它把我们的网站分为三层,这三层正好对应着 BEM 三个英文单词的简写 block, element, modifier,分为为 块层、元素层、修饰符层。 ...

October 10, 2024