关于解析 HTML 过程中遇到 script 标签

当在解析 HTML 的过程中遇到 <script> 标签时,浏览器的处理方式如下:

1. 阻塞解析

  • 如果 <script> 标签没有设置 asyncdefer 属性,浏览器会暂停 HTML 的解析,直到该脚本下载、解析和执行完成。这是因为 JavaScript 代码可能会修改正在构建的 DOM,所以浏览器需要确保在执行脚本之前 DOM 的状态是稳定的。

2. 异步加载和执行(使用 async 属性)

  • 如果 <script> 标签设置了 async 属性,浏览器会在后台异步下载该脚本。一旦脚本下载完成,浏览器会暂停 HTML 的解析,执行该脚本,然后继续解析 HTML。

3. 延迟执行(使用 defer 属性)

  • 如果 <script> 标签设置了 defer 属性,浏览器会在后台异步下载该脚本,但不会暂停 HTML 的解析。脚本会在 HTML 文档解析完成后,按照它们在文档中出现的顺序执行。

总的来说,<script> 标签的出现会影响 HTML 的解析过程,开发人员可以根据具体需求使用 asyncdefer 属性来控制脚本的加载和执行时机,以优化页面的性能和加载速度。

关于构建 RenderTree

在浏览器中,解析 HTML 文档、解析 CSS 样式表和构建渲染树是一系列紧密相关的过程,它们共同作用以将网页内容呈现给用户。以下是它们之间的关系:

一、解析 HTML 文档

  1. 当浏览器接收到一个 HTML 文档时,它会开始解析这个文档。解析过程是将 HTML 文本转换为 DOM(Document Object Model)树的过程。
  2. DOM 树是一个由节点组成的树状结构,每个节点代表 HTML 文档中的一个元素。例如,<html><body><div>等都是 DOM 树中的节点。
  3. 解析 HTML 文档是一个逐步的过程,浏览器会按照 HTML 文本的顺序依次处理每个标签,构建 DOM 树的节点,并确定它们之间的父子关系。

二、解析 CSS 样式表

  1. 同时,浏览器也会解析网页中引用的 CSS 样式表。CSS 样式表定义了网页中元素的外观样式,如颜色、字体、布局等。
  2. 解析 CSS 样式表的过程是将 CSS 文本转换为一组规则的过程。这些规则描述了如何将样式应用于特定的 HTML 元素。
  3. 浏览器会根据 CSS 选择器和元素的属性来确定哪些规则适用于哪些元素。例如,选择器p将适用于所有<p>标签的元素。

三、构建渲染树

  1. 在解析 HTML 文档和 CSS 样式表之后,浏览器开始构建渲染树。渲染树是由可视元素及其对应的样式信息组成的树状结构。
  2. 构建渲染树的过程是将 DOM 树中的节点与 CSS 样式表中的规则进行匹配,确定每个节点的最终样式。只有可见的元素才会被包含在渲染树中,例如<head>标签中的内容通常不会在渲染树中。
  3. 渲染树中的每个节点都包含了元素的样式信息,如颜色、字体大小、位置等。这些信息将用于后续的布局和绘制过程。

四、关系总结

  1. 解析 HTML 文档是构建渲染树的基础,它提供了网页的结构信息。
  2. 解析 CSS 样式表为构建渲染树提供了样式信息,确定了每个元素的外观。
  3. 构建渲染树是将 HTML 结构和 CSS 样式结合起来的过程,它确定了网页中每个可见元素的最终外观和位置,为后续的布局和绘制过程提供了输入。

总之,解析 HTML 文档、解析 CSS 样式表和构建渲染树是浏览器渲染网页的关键步骤,它们相互协作以将网页内容以可视化的形式呈现给用户。

Read More

前端代码优化

以下是一些前端代码优化的具体建议:

一、性能优化

  1. 减少 HTTP 请求:

    • 合并 CSS 和 JavaScript 文件。
    • 使用 CSS sprites(雪碧图)来减少图片请求数量。
    • 对于小图标,可以考虑使用字体图标,如 Font Awesome 或 Iconfont。
  2. 优化图片:

    • 使用合适的图片格式,例如 JPEG 用于照片,PNG 用于透明图像,WebP 用于现代浏览器支持的高效图像格式。
    • 对图片进行压缩,可使用在线工具或命令行工具如 ImageMagick。
    • 对于大尺寸图片,可以考虑使用懒加载技术,当图片进入视口时再加载。
  3. 利用浏览器缓存:

    • 设置适当的缓存头,让浏览器缓存静态资源,如 CSS、JavaScript 和图片。可以在服务器端配置缓存策略。
    • 使用版本号或哈希值来更新缓存,例如在文件名中添加版本号,当文件内容改变时,文件名也会改变,从而强制浏览器重新下载新文件。
  4. 代码压缩和精简:

    • 对 JavaScript 和 CSS 进行压缩,去除不必要的空格、注释和换行符,减小文件大小。可以使用工具如 UglifyJS 和 Clean-CSS。
    • 去除未使用的代码,例如在使用构建工具时,可以通过 tree shaking 技术去除未引用的模块。
  5. 延迟加载和异步加载:

    • 对于非关键的 JavaScript 和 CSS,可以使用异步加载或延迟加载技术,让页面先加载关键内容,提高初始加载速度。
    • 对于大型库或框架,可以考虑按需加载,只加载实际使用的部分。
  6. 优化 DOM 操作:

    • 减少对 DOM 的频繁操作,因为 DOM 操作相对较慢。可以将多个操作合并为一次操作,或者使用文档片段(document fragment)进行批量操作后再插入到 DOM 中。
    • 避免使用 innerHTML 大量更新 DOM,因为这可能导致性能问题。可以使用 DOM 方法如 createElement 和 appendChild 来手动构建 DOM 结构。

二、可维护性优化

  1. 代码结构和组织:

    • 遵循良好的代码结构规范,例如将代码分为不同的模块、组件或功能区域,提高代码的可读性和可维护性。
    • 使用有意义的变量名和函数名,避免使用缩写或无意义的名称。
    • 对代码进行注释,解释复杂的逻辑或算法,方便其他开发者理解。
  2. 模块化开发:

    • 使用模块化的方式组织代码,例如使用 ES6 的模块系统或 CommonJS 模块规范。这可以提高代码的可复用性和可维护性。
    • 对于大型项目,可以考虑使用前端框架提供的模块管理功能,如 Vue 的单文件组件(SFC)或 React 的组件化开发。
  3. 状态管理:

    • 对于复杂的应用,使用状态管理库如 Vuex(对于 Vue)或 Redux(对于 React)来集中管理应用的状态,避免状态的混乱和难以维护。
    • 确保状态的更新是可预测的和可追踪的,避免直接修改状态,而是通过派发动作来更新状态。
  4. 错误处理:

    • 对代码进行适当的错误处理,捕获可能出现的错误并提供友好的错误提示。可以使用 try-catch 语句或 Promise 的 catch 方法来处理同步和异步错误。
    • 记录错误信息,以便在出现问题时进行调试和排查。可以使用浏览器的控制台或日志工具来记录错误。
  5. 测试和调试:

    • 编写单元测试和集成测试,确保代码的正确性和稳定性。可以使用测试框架如 Jest、Mocha 或 Karma。
    • 利用浏览器的开发者工具进行调试,包括控制台输出、断点调试、网络请求查看等功能。

三、用户体验优化

  1. 响应式设计:

    • 确保页面在不同设备和屏幕尺寸上都能良好显示,采用响应式设计技术,如媒体查询、弹性布局等。
    • 优化移动端体验,考虑触摸事件、加载速度和性能优化等方面。
  2. 加载状态和反馈:

    • 在页面加载过程中提供加载状态指示,如进度条或加载动画,让用户知道页面正在加载。
    • 对于长时间的操作,如数据加载或提交表单,提供反馈信息,让用户知道操作的进度和状态。
  3. 交互设计:

    • 设计简洁明了的用户界面,避免复杂的交互和操作流程。
    • 提供清晰的用户反馈,如按钮点击效果、表单验证提示等,增强用户与页面的交互感。
  4. 性能感知优化:

    • 优化页面的首屏加载时间,让用户尽快看到关键内容。
    • 减少页面的卡顿和闪烁,提高页面的流畅度和响应速度。

总之,前端代码优化是一个持续的过程,需要综合考虑性能、可维护性和用户体验等方面。通过采用合适的技术和方法,可以提高前端代码的质量和效率,为用户提供更好的体验。

Tailwind CSS

Tailwind CSS 是一个高度可定制的 CSS 框架。

主要特点:

  • 实用优先的类名体系:它提供了大量的预定义类名,用于快速构建页面布局、设置文本样式、控制颜色、添加响应式设计等。例如,可以使用 bg-blue-500 来设置背景颜色为蓝色,text-lg 来设置较大的文本大小,flex 和 justify-between 等类名来快速实现 flex 布局的各种样式。
  • 响应式设计:内置了响应式设计的类名,可以轻松实现不同屏幕尺寸下的适配。比如 md:flex 表示在中等屏幕尺寸及以上显示为 flex 布局,sm:hidden 表示在小屏幕尺寸下隐藏元素。
  • 高度可定制:可以根据项目的具体需求进行高度定制。可以选择要包含的功能、颜色、字体大小等,从而减小生成的 CSS 文件大小,提高性能。

Read More

requestAnimationFrame

requestAnimationFrame是浏览器提供的一个用于在浏览器下一次重绘之前执行特定代码的方法。

用途和优势:

  • 高效的动画实现:与传统的使用setInterval或setTimeout来实现动画不同,requestAnimationFrame会在浏览器下一次重绘之前调用指定的函数,这样可以确保动画与浏览器的刷新频率同步,从而更加流畅,减少不必要的计算资源浪费。

  • 性能优化:浏览器可以对多个使用requestAnimationFrame的动画进行优化,例如在页面不可见时暂停动画,当页面重新可见时恢复动画,从而提高性能和节省电量。

为什么不会卡顿?

JavaScript reduce 方法

关于最后一个参数 – 初始值:

  1. 计算特定初始值的累计结果
    例如,计算数组中所有元素的乘积,并且希望从特定的初始值(比如 1)开始计算,而不是从数组的第一个元素开始。如果不设置初始值,第一个元素将作为初始累计值,可能不符合预期的计算逻辑。

    1
    2
    3
    const arr = [2, 3, 4];
    const productWithInitialValue = arr.reduce((acc, curr) => acc * curr, 1);
    console.log(productWithInitialValue); // 24
  2. 处理空数组
    如果不确定数组是否为空,设置初始值可以确保在空数组的情况下也能返回一个合理的值,而不是 undefined。
    例如,计算数组中所有数字的和,如果数组为空,希望返回 0。

    1
    2
    3
    const arr = [];
    const sumWithInitialValue = arr.reduce((acc, curr) => acc + curr, 0);
    console.log(sumWithInitialValue); // 0
  3. 处理非数值类型的初始状态
    如果要进行的累计操作不是简单的数值计算,而是涉及到更复杂的数据结构或状态,通常需要设置一个合适的初始值。
    例如,将数组中的对象合并为一个新的对象,可以设置一个空对象作为初始值。**

    1
    2
    3
    const arr = [{ name: 'Alice' }, { name: 'Bob' }];
    const mergedObject = arr.reduce((acc, curr) => ({...acc,...curr }), {});
    console.log(mergedObject); // { name: 'Bob' }
  4. 处理可能存在单个元素的数组
    当数组可能只有一个元素时,设置初始值可以确保处理逻辑的一致性。
    例如,对数组中的元素进行特定的转换操作,如果只有一个元素,也希望按照统一的逻辑进行处理。

    1
    2
    3
    const arr = [5];
    const transformedValue = arr.reduce((acc, curr) => acc + curr, 0);
    console.log(transformedValue); // 5

人月神话阅读笔记

用人月+人天这种作为衡量一项工作的规模是非常危险和具有欺骗性的,它暗示着人员的数量和时间是可以相互替换的,任务是没有次序上的限制的,且不需要开发人员沟通交流的。随着人手的增多,沟通成本也会越来越多,甚至会比之前更加缓慢。

经验显示,一个需求的开发的时间安排是这样的,1/3计划,1/6做编码,1/4单元+组件+系统测试,1/4端到端系统测试测试。