Vue3 的侦听器

计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些“副作用”:例如更改 DOM,或是根据异步操作的结果去修改另一处的状态。

Vue.js 设计与实现 - 06 原始值的响应式方案

原始值指的是 Boolean、Number、String、Symbol、BigInt、Null 和 Undefined 等类型的值。在 JS 中,原始值是按值传递的,这意味着,如果一个函数接受原始值作为参数,那么形参和实参之间没有引用关系,它们是两个完全独立的值,对形参的修改不会影响实参。另外,JS 中的 Proxy 无法提供对原始值的代理,必须对其进行包装,才能实现对原始值的代理。

原始值 按值传递 形参 实参 包装

第二篇 响应系统

Read More

Vue.js 设计与实现 - 05 非原始值的响应式方案

上一章中,着重讨论了响应系统的概念和实现,并简单介绍了响应式数据的基本原理。本章中我们把目光聚焦在响应式数据本身,深入探讨实现响应式数据需要考虑哪些内容,其中的难点是什么。除了拦截 get、set 操作外,还需要考虑其他问题,比如 追踪拦截for…in 循环?如何代理数组、对象、Set、Map 等类型的数据。想要实现完善的响应式数据,需要深入语言规范(ECMA-262)。

第二篇 响应系统 Proxy Reflect

Read More

Vue.js 设计与实现 - 04 响应系统的作用和实现

响应系统也是 Vue.js 的重要组成部分。在本章中,首先讨论什么是响应式数据副作用函数,然后尝试实现一个相对完善的响应系统。在这个过程中,我们会遇到各种各样的问题,例如如何避免无限递归?为什么需要嵌套的副作用函数?两个副作用函数之间会产生哪些影响?以及其他需要考虑的细节。

第二篇 响应系统 响应式数据 副作用函数 Proxy WekaMap Set Map

Read More

Vue.js 设计与实现 - 03 Vue.js 3 的设计思路

在第 1 章中,我们阐述了框架设计是权衡的艺术,这里面存在取舍,例如性能和可维护性之间的取舍、运行时和编译时之间的取舍。在第 2 章中,我们详细讨论了框架设计的几个核心要素,有些要素是框架设计者必须要考虑的,另一些要素则是从专业和提升开发体验的角度考虑的。框架的设计将就全局视角的把控,一个项目就算在大,也是存在一条核心思路的,并围绕核心展开。本章就从全局视角了解 Vue.js 3 的设计思路、工作机制及其重要的组成部分。可以把这些组成部分当作独立的功能模块,看看它们之间是如何相互配合的。后续的章节中,我们会深入各个功能模块了解它们的运作机制。

第一篇 框架设计概览 虚拟DOM 渲染器 组件 模板 编译器

Read More

Vue.js 设计与实现 - 02 框架设计的核心要素

框架设计要比想象得复杂,并不是说只把功能开发完成,能用就算大功告成了,这里还有很多问题:

  • 框架应该给用户提供哪些构建的产物?
  • 产物的模块格式如何?
  • 当用户没有以预期的方式使用框架时,是否应该答应合适的警告信息从而提供更好的开发体验,让用户快速定位问题?
  • 开发版本的构建和生产版本的构建有何区别?
  • 热更新(Hot Module Replacement,HMR)需要框架层面的支持,我们是否考虑?
  • 框架提供了多个功能,而用户只需要其中几个功能时,用户能都选择关闭其他功能从而减少最终资源的打包体积

第一篇 框架设计概览 webpack rollup.js Tree-Shaking 函数副作用 压缩工具 terser

Read More

Vue.js 设计与实现 - 01 权衡的艺术

“框架设计里到处都体现了权衡的艺术”

深入探讨 Vue.js 3 各个模块的实现思路和细节之前,我认为有必要先来讨论视图层框架设计方面的内容。为什么呢?因为当我们设计一个框架的时候,框架本身各个模块之间并不是相互独立的,而是相互关联、相互制约的。作为框架设计者,一定要对框架的定位和方向拥有全局的把控,这样才能做好后续的模块设计和拆分。同样,作为学习中,我们在学习框架时,也应该从全局的角度对框架的设计拥有清晰的认知,否则很容易被细节困住,看不清全貌。

第一篇 框架设计概览 命令式(性能) 声明式(维护) 虚拟DOM 运行时 编译时#行编译时

Read More

富文本里面, 是如何做到划词的

在富文本环境中实现划词(鼠标滑动选择一组字符并对其进行操作)通常涉及以下几个关键步骤和技术:

  • 事件监听
    • 监听鼠标按下、鼠标移动和鼠标松开这三个主要的鼠标事件。当鼠标按下时,标记选择的开始;在鼠标移动过程中,根据鼠标的位置更新选择范围;鼠标松开时,确定最终的选择。
  • 选择范围计算
    • 使用浏览器提供的 Selection 对象来获取和管理选择的范围。在鼠标移动过程中,不断更新 Selection 对象的范围。
  • 操作处理
    • 一旦选择完成,可以根据具体的需求对选中的字符进行操作。例如,修改样式(如加粗、变色)、获取选中的文本内容、执行复制粘贴等操作。

示例(展示了如何获取选中的文本):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>

<body>
<p>这是一段示例文本,您可以尝试选中一部分。</p>

<script>
document.addEventListener("mouseup", function () {
const selection = window.getSelection();
if (selection) {
const selectedText = selection.toString();
console.log("选中的文本: ", selectedText);
}
});
</script>
</body>
</html>

浏览器缓存策略

浏览器缓存策略主要有以下几种:

一、强缓存

  1. 原理:

    • 当浏览器访问资源时,会先检查强缓存。如果资源在强缓存中有效,浏览器直接从缓存中读取资源,不会向服务器发送请求。
    • 强缓存通过 HTTP 响应头中的 Cache-ControlExpires 字段来控制。
  2. Cache-Control

    • 可以设置多种指令,如 max-age 指定资源的缓存时间(单位为秒)。
    • 例如,Cache-Control: max-age=3600 表示资源在 3600 秒内有效,可以直接从缓存中读取。
    • 其他指令还包括 no-cache(强制每次请求都向服务器验证资源是否更新)、no-store(不允许缓存资源)等。
  3. Expires

    • 指定资源的过期时间,是一个绝对时间。
    • 例如,Expires: Thu, 31 Dec 2024 23:59:59 GMT 表示资源在这个时间点之前有效。
    • 但由于 Expires 是绝对时间,可能会受到客户端时间不准确的影响,所以现在更推荐使用 Cache-Control

二、协商缓存

  1. 原理:

    • 当强缓存失效时,浏览器会向服务器发送请求,同时携带缓存的标识。服务器根据这些标识判断资源是否有更新。如果资源没有更新,服务器返回 304 Not Modified 状态码,浏览器继续从缓存中读取资源;如果资源有更新,服务器返回新的资源和新的缓存标识。
    • 协商缓存通过 HTTP 响应头中的 Last-ModifiedIf-Modified-Since 或者 ETagIf-None-Match 字段来实现。
  2. Last-ModifiedIf-Modified-Since

    • Last-Modified 是服务器在响应资源时返回的资源最后修改时间。
    • 浏览器在下次请求时,会在 If-Modified-Since 请求头中携带上一次资源的 Last-Modified 值。
    • 服务器比较资源的最后修改时间和 If-Modified-Since 的值,如果相同则返回 304,否则返回新的资源和新的 Last-Modified 值。
  3. ETagIf-None-Match

    • ETag 是服务器为资源生成的唯一标识,通常是基于资源内容生成的哈希值。
    • 浏览器在下次请求时,会在 If-None-Match 请求头中携带上一次资源的 ETag 值。
    • 服务器比较资源的 ETag 值和 If-None-Match 的值,如果相同则返回 304,否则返回新的资源和新的 ETag 值。

三、缓存的优先级

强缓存优先级高于协商缓存。如果强缓存有效,浏览器不会发送请求进行协商缓存。

四、实际应用中的注意事项

  1. 合理设置缓存时间:

    • 根据资源的更新频率和重要性,合理设置强缓存的 Cache-ControlExpires 值。对于经常更新的资源,可以设置较短的缓存时间;对于不常更新的资源,可以设置较长的缓存时间。
    • 例如,对于静态的 CSS、JavaScript 文件,可以设置较长的缓存时间,以提高页面加载速度;对于动态生成的内容,如 API 响应,可以设置较短的缓存时间或不使用强缓存。
  2. 动态更新缓存:

    • 如果资源的内容发生了变化,需要确保浏览器能够获取到新的资源而不是使用旧的缓存。可以通过在资源的 URL 中添加版本号、时间戳或随机数等方式来强制浏览器重新获取资源。
    • 例如,<link rel="stylesheet" href="style.css?v=1.0.1">,当更新 CSS 文件时,修改版本号可以让浏览器重新下载新的资源。
  3. 缓存控制的粒度:

    • 可以根据不同的资源类型和需求,设置不同的缓存策略。例如,可以对图片、字体等静态资源设置较长的缓存时间,对 HTML 页面设置较短的缓存时间,以便及时获取最新的页面内容。
  4. 考虑用户体验:

    • 在设置缓存策略时,要考虑用户体验。如果缓存时间过长,可能会导致用户在资源更新后无法及时看到新的内容。可以提供手动刷新或清除缓存的方式,让用户能够获取到最新的资源。