整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

D3.js实战教程:1 D3.js简介


章涵盖

  • 了解 D3.js 的作用及其背后的理念
  • 认识与 D3 结合使用来创建数据可视化的工具
  • 使用代码创建可缩放矢量图形 (SVG) 并为其设计样式
  • 了解数据可视化最佳实践如何支持您作为 D3 开发人员的旅程

D3.js 是网络上几乎所有最具创新性和令人兴奋的信息可视化的幕后黑手。D3 代表数据驱动文档,它是一个品牌名称,也是多年来以某种形式提供的一类应用程序。我们可以使用这个库来构建各种数据驱动的项目,从简单的条形图到动态地图,再到复杂的空间和时间探索。当您希望在数据可视化方面获得完全的创意和技术自由时,无论您是构建用于研究的交互式原型、顶级科技公司广泛且完全响应的数据仪表板,还是揭示数据故事的长篇文章,D3 都是您的首选工具当用户滚动时。

1.1 什么是D3.js

D3 是一个开源 JavaScript 库,由 Mike Bostock 于 2011 年创建,用于为 Web 生成动态和交互式数据可视化。尽管过去几年推出了许多新的数据可视化库,但它们通常在底层使用 D3。这是因为 D3 与 JavaScript 一样,非常灵活且强大。




图 1.1 汉密尔顿中每条线的交互式可视化,这是Shirley Wu创建的 D3 项目


1.1.1 对可通过网络访问的数据可视化的需求

D3.js 的创建是为了满足对可通过网络访问的复杂数据可视化的迫切需求。假设您的公司正在使用商业智能工具,但它没有显示您的团队所需的数据模式。您必须构建一个自定义仪表板,根据您的特定领域量身定制,准确显示客户的行为方式。该仪表板需要快速、交互式且可在整个组织内共享。D3 将是此类项目的自然选择。

或者想象一下,您被雇用来实现一个网页,该网页以可视化方式展示 LGBTQ+ 群体(女同性恋、男同性恋、双性恋、跨性别者、酷儿等)的权利在过去几十年和全世界的演变情况。此页面包含许多随着用户滚动而变化的创意可视化效果。它们通过鼠标事件显示更多信息并适应屏幕大小。D3 将是构建此类项目的首选工具。

Mike Bostock 最初创建 D3 是为了利用新兴的 Web 标准,正如他所说,“避免了专有表示并提供了非凡的灵活性,暴露了 CSS3、HTML5 和 SVG 等 Web 标准的全部功能”(http: // d3js.org)。D3.js 版本 7 是这个流行库的最新版本,它通过模块化 D3 的各个部分来延续这一趋势,使其与 ECMAScript 模块(一种基于 JavaScript 的脚本语言)和现代应用程序开发完全兼容。

D3.js 使开发人员不仅能够制作丰富的交互式应用程序,而且能够制作样式和服务类似于传统 Web 内容的应用程序。这使得它们更可移植,更适合增长,并且更容易由其他团队成员可能不知道 D3 的具体语法的大型团体维护。

图 1.2 D3 开发人员可以访问各种数据表示形式,地图就是一个例子。这是由Christophe Viau创建的数字高程模型 (DEM) 地图。


Bostock 决定广泛处理数据并创建一个能够像图表一样简单、像网络一样简单、像列表一样简单地呈现地图的库,这也意味着开发人员不需要了解以下内容的抽象和语法:一个用于地图的库,另一个用于动态文本内容的库,还有另一个用于传统图形的库。相反,用于运行交互式网络可视化的代码接近于纯 JavaScript,并且也类似于表示 D3 地图上的动态点的代码。方法是相同的,但数据也可以是相同的,以一种方式制定用于网络的节点和链路,而以另一种方式制定用于地图上的地理空间表示。

D3 不仅可以创建复杂多样的图形,还可以嵌入用户期望的高水平交互性,这对于现代 Web 开发至关重要。使用 D3,每个图表的每个元素(从旋转的地球仪到饼图的切片)都以相同的方式进行交互。由于 D3 是由精通数据可视化实践的人编写的,因此它包含数据可视化和 Web 开发中标准的交互式组件和行为。

图 1.3 交互性是 D3 的核心。在此网络可视化中,鼠标交互揭示了不同组织之间的关系以及特定于所选节点的信息(https://amdufour.github.io/organizations-against-polization)。



1.1.2 什么时候使用D3.js?

数据可视化领域正在蓬勃发展,可用于生成数据绑定图形的工具数量在过去十年中呈爆炸式增长。我们拥有 Excel(数据可视化的常用入口)和 Power BI(用于构建仪表板的 Microsoft 解决方案)等商业智能工具。另一方面,更有经验的数据科学家通常会转向 R 的 ggplot2 或 Python 的 matplotlib。

基于浏览器的点击式工具(例如 Tableau、Flourish、DataWrapper、RAWGraphs 和 Google 图表)也占据了主导地位,允许用最少的技术知识创建令人惊叹的作品。

最后,HighCharts、Chart.js 和 D3.js 等 JavaScript 库专门用于开发基于 Web 的交互式可视化。

而且这个列表远非详尽无遗......

那么,D3 在数据可视化工具的海洋中处于什么位置呢?我们何时以及如何使用它?我们可以说,虽然 D3 完全可以构建此处列出的数据可视化库提供的任何图表,但它通常不是构建简单的传统图表或探索阶段(我们调查哪种类型的可视化是)的首选选项。最适合代表我们的数据。构建 D3 项目需要时间,而 D3 在复杂、交互式和定制的项目中真正表现出色。数据可视化不仅仅是折线图和散点图!虽然上面提到的工具通常专注于预定义的图表,但 D3 允许我们将数据绑定到任何图形元素,并通过以独特的方式组合这些视觉元素来打破常规。

图 1.4 D3 具有 SVG 和画布绘图功能,允许开发人员构建自定义可视化效果,例如Elijah Meeks的乐谱表示形式。


以下是我们如何在数据可视化项目范围内使用 D3 的示例。首先,我们从预先存在的数据集或手动收集的数据开始。在开始数据分析过程之前,我们通常会花费大量时间清理、格式化和准备数据。Python 和 R 等数据科学工具在这方面功能强大,可以帮助我们识别隐藏在数据中的故事。Excel 还可以完成简单的数据整理和数据分析工作,并且需要较少的技术背景。我们甚至可以使用 JavaScript 和 D3 进行基本数据探索,因为它们提供了我们将在本书后面讨论的统计方法。

一旦数据分析开始,通常会创建一些原型来帮助完善我们的故事。Tableau 和 RawGraphs 等工具使我们能够快速生成此类图表。这是非常重要的一步,在此阶段创建的可视化通常并不花哨或精致。我们不想在原型设计阶段过于执着于我们的想法,花费大量时间。我们可能会发现自己必须“杀死我们的宝贝”并重新开始几次,直到我们找到最适合我们想要讲述的故事的可视化效果。网络图可能是一个例外,直接跳到 D3 对于这些项目通常是有意义的。

最后,一旦我们知道要创建的可视化类型,就该卷起袖子,对其进行编码,并使用 D3 对其进行完善。如今,编码步骤通常发生在单页应用程序 (SPA) 中,使用 React 或 Svelte 等框架。

图 1.5 使用 D3 构建的自定义可视化的另一个示例,其中形状与每首歌曲的不同属性(例如持续时间、流派和节奏)成比例(https://amdufour.github.io/spotify-hits)。



1.1.3 D3.js 的工作原理

您可能已经尝试过 D3,并发现它并不容易上手。也许那是因为您希望它是一个简单的图表库。一个恰当的例子是创建条形图,我们将在第 2 章和第 3 章中进行此操作。D3 没有一个函数来创建条形图。相反,它有一个将<svg>容器附加到文档对象模型 (DOM) 的函数,以及另一组附加容器的函数。<rect>每个数据点的元素。然后,我们使用比例来计算构成直方图的矩形的长度并设置它们的属性。最后,我们调用另一组函数,将 x 轴和 y 轴添加到条形图中。如图 1.6 所示,这个过程比使用 Highcharts 等专用图表库要长得多。但 D3 处理数据和图形的明确方式也是它的优势。尽管其他图表库允许您方便地制作折线图和饼图,但当您想要创建不属于传统图表范围的可视化效果或实现自定义交互时,它们很快就会崩溃。不是D3。D3 允许您构建您可以想象的任何数据驱动图形和交互性。

图 1.6 使用 Highcharts 与 D3.js 生成的条形图。Highcharts 的代码更简单、更短,但 D3.js 更通用。



在图 1.7 中,您可以看到我们通常如何使用 D3 进行数据可视化编码的地图。我们从一个数据集(通常是 CSV 或 JSON 文件)开始,然后使用 d3-fetch 模块将此数据集加载到我们的项目中。我们通常需要执行一些操作来格式化数据。例如,我们确保数字和日期的格式正确。如果我们之前没有这样做,我们可能还想询问我们的数据集以找到其主要特征。例如,提前知道其最大值和最小值通常很有帮助。然后我们准备开始构建可视化,为此我们将结合我们将在本书中学习的不同 D3 函数。最后,我们通过监听鼠标事件来添加交互性,允许用户过滤数据或放大可视化。

图 1.7 如何使用 D3.js 实现数据可视化



1.2 D3 生态系统 - 入门所需了解的内容

D3.js 从来不会单独使用,而是我们结合起来创建丰富的 Web 界面的技术和工具生态系统的一部分。与任何网页一样,D3 项目是在 DOM(文档对象模型)内构建的,并利用 HTML5 的强大功能。尽管 D3 可以创建和操作传统的 HTML 元素,例如分区 ( <div>) 和列表 ( <ul>, <ol>),但我们主要使用 SVG 图形或在画布(从脚本渲染位图图像的 HTML 元素)内生成可视化效果。然后,我们还可以使用旧的 CSS 样式表,它可以增强 D3 项目并使其设计更易于维护,尤其是在广泛的团队中。

鉴于 D3 是一个 JavaScript 库,我们自然倾向于将 D3 方法与本机 JavaScript 函数结合起来来访问和操作数据。D3 现在完全支持 JavaScript 的 ECMAScript 2015 或 ES6 修订版以及大多数最新更新。D3 还作为模块提供,可以集成到我们构建 Web 项目所用的最新框架和库中。使用这些模块通常是首选方法,因为它不会污染我们应用程序的全局范围。

在本节中,我们将简要讨论这些技术及其在 D3 生态系统中的作用。由于 SVG 知识是理解 D3 的基础,因此我们将花时间更详细地解释您开始构建可视化所需理解的基础知识。如果您已经熟悉 HTML、SVG 元素、CSS、JavaScript 和 JavaScript 模块,请随意浏览或跳至第 1.3 节。

1.2.1 HTML 和 DOM

与 GIF 动画和框架成为网络动态内容顶峰的时代相比,我们已经走过了很长一段路。在图 1.8 中,您可以看到为什么 GIF 从未在强大的基于 Web 的数据可视化中流行起来。GIF 与设计用于使用 VML(矢量标记语言)的 infoviz 库一样,对于早期浏览器来说是必需的,但 D3 是为不再需要向后兼容性的现代浏览器而设计的。

图 1.8 20 世纪 90 年代的一些例子,比如dpgraph.com,仍然存在,让我们想起动画 GIF 无处不在的时代。



当您登陆网页时,要加载的第一个文件是超文本标记语言或 HTML 文件,如下例所示。浏览器解析 HTML 文件以构建文档对象模型或 DOM,这是用于 Web 文档的编程接口。我们经常将其称为 DOM 树,因为它由一组嵌套元素(也称为节点或标签)组成。在我们的示例中,<head>和 the<body>元素是<html>父元素的子元素。同样,标签是、the和标签<body>的父标签。标题也是该元素的同级元素。当您加载网页时,您在屏幕上看到的是标记中包含的元素。

<!DOCTYPE#nbsp;html>
<html>
  <head>
    <meta charset="UTF-8">  
    <title>A simple HTML file | D3.js in Action</title>
  </head>
  <body>
    <h1>I am a title</h1>
    <div>
      <p>I am a paragraph.</p>
      <p>I am another paragraph.</p>
    </div>
  </body>
</html>

在 DOM 中,每个元素的三类信息定义了其行为和外观:样式、属性和特性。样式决定颜色、大小、边框、不透明度等。属性包括类、id 和交互行为,尽管某些属性也可以确定外观,具体取决于您正在处理的元素类型。对于 SVG 元素,属性用于设置不同形状的位置、大小和比例。属性通常指的是状态,例如复选框的“checked”属性,如果该框被选中,则该属性为 true;如果该框未被选中,则该属性为 false。尽管术语“属性”和“属性”经常互换使用,但它们是两个不同的东西。呈现 DOM 时,属性显示为初始状态。属性是元素的当前状态,并且可以随着用户与界面交互而改变。在第2章中,我们将讨论用于生成或修改HTML和SVG元素的样式和属性的D3方法。

DOM 还决定元素在屏幕上的绘制顺序,子元素在父元素之后和内部绘制。尽管 CSS 属性z-index使我们能够部分控制传统 HTML 元素绘制到屏幕上的顺序,但 SVG 元素严格遵循它们在 DOM 中出现的顺序。根据画家的模型,之后绘制的内容出现在之前绘制的内容之上。

1.2.2 SVG——可缩放矢量图形

可扩展矢量图形 (SVG) 的引入确实改变了网络的面貌。几年之内,SVG 图形成为主要的 Web 开发工具。光栅图形(PNG 和 JPG)由微小的像素组成,当我们放大得太近时,这些像素就会变得可见,而矢量图形则是通过数学和几何图形构建的。它们在任何尺寸和任何屏幕分辨率下都能保持清晰的外观。SVG 图形的另一个显着优势是它们可以直接注入 DOM,允许开发人员操纵其元素并为其设置动画,并使屏幕阅读器可以访问它们。如果构建正确,SVG 也具有高性能,其文件大小仅为等效光栅图像的一小部分。

当使用 D3 创建数据可视化时,我们通常将 SVG 形状注入 DOM 并修改其属性以生成组成可视化的视觉元素。了解 SVG 的工作原理、主要 SVG 形状及其表示属性对于大多数 D3 项目至关重要。

如何访问代码文件

本书的每一章都包含旨在支持您的学习体验的代码练习。我们强烈建议您“做”这本书,而不仅仅是“读”这本书,这意味着在阅读章节时完成练习。通过这种方式,您将保留更多信息,并很快就能构建自己的 D3 项目!

对于每个练习和项目,您都可以访问现成的代码文件。您可以在本书的 Github 存储库(https://github.com/d3js-in-action-third-edition/code-files)上找到它们。如果您熟悉 Git,则可以将存储库克隆到您的计算机上。您还可以下载压缩文件。

从 Github 存储库下载代码文件


每一章都有自己的文件夹,其中包含一个或多个练习,按照每章中的部分编号。练习包括一个start文件夹,其中包含入门所需的所有文件。您将在文件夹中找到练习的完整解决方案end。当您完成一章的各个部分时,您可以继续在上一部分使用的文件中进行编码,或者使用专用于该部分的文件夹重新开始。两种选择都会导致相同的结果。

让我们开始探索矢量图形。转到本书提供的代码文件。找到end中的文件夹chapter_01/SVG_Shapes_Gallery并右键单击该文件index.html。在菜单中,转到打开方式并选择浏览器。我们建议使用 Chrome 或 Firefox,因为它们具有出色的检查器工具。该文件将在新的浏览器选项卡中打开,并且将出现您在图 1.9 中看到的矢量图形。您还可以在 Github 托管项目 ( https://d3js-in-action-third-edition.github.io/svg-shapes-gallery ) 上查看这些 SVG 形状。

图 1.9 我们将在本节中构建的基本 SVG 形状图库。


您正在查看的 SVG 图形包含创建 D3 可视化时最常使用的形状:线条、矩形、圆形、椭圆形、路径和文本。

使用 D3 时,您通常会告诉库应将哪些形状附加到 DOM。您还负责了解需要计算哪些表示属性才能使形状具有您正在寻找的尺寸、颜色和位置。在下面的练习中,您将编写创建图 1.9 中每个 SVG 元素的代码。我们将此练习称为SVG 形状图库。之后,您将了解入门所需的所有 SVG 基础知识。

在您选择的代码编辑器中打开练习文件夹index.html中的文件。我们推荐VS Code,这是一个免费、易于使用的代码编辑器,并且具有多种功能,对前端开发很有帮助。startSVG_Shapes_Gallery

正如您所看到的,index.html是一个简单的 HTML 文件。如果您在浏览器中打开此文件(右键单击该文件并在“打开方式”菜单中选择浏览器),您将只会看到一个空白页面。这是因为该<body>元素为空。在接下来的小节中,我们将向此<body>元素添加 SVG 形状。

清单 1.1.a SVG 形状库练习的起始 HTML 文件

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">  
  <title>SVG Shapes Gallery | D3.js in Action</title>
</head>
<body>
  
</body>
</html>

哪里可以找到更多信息

以下部分将介绍多个 SVG 元素及其属性。作为开发人员,我们在构建项目、使用我们不熟悉的 SVG 元素或寻找 JavaScript 函数来执行特定操作时严重依赖在线资源。在前端开发中,MDN Web Docs ( https://developer.mozilla.org/ ) 始终是可靠且全面的资源。它包含 HTML 元素及其属性、CSS 属性和 JavaScript 函数的易于理解且通常可编辑的示例。

响应式 svg 容器

在 SVG 图形的世界中,<svg></svg>容器是在其上绘制所有内容的白板。每个 SVG 形状都嵌套在<svg>父级中。要查看其实际效果,请在元素index.html内编辑并添加 SVG 容器<body>。在浏览器中重新加载页面。目前还看不到任何东西。

<body>
  <svg></svg>
</body>

打开浏览器的检查器工具(在浏览器窗口中右键单击并选择“检查”)。在检查器窗口中,您将看到组成页面的 DOM。找到<svg></svg>容器,也称为 SVG 节点。当您在检查器中将鼠标移到它上面时,SVG 元素会在页面上突出显示。您可以在图 1.5 中看到此效果。

图 1.10 在 DOM 树中选择并在视口中突出显示的 SVG 节点


默认情况下,浏览器为 SVG 容器提供宽度300px和高度。150px但我们也可以使用属性来分配这些值。属性用于提供有关 HTML 元素的附加信息。对于内联 SVG,我们主要使用属性来设置组成 SVG 图形的元素和形状的大小和位置。

例如,我们可以设置SVG 容器的宽度和高度属性。返回到文本编辑器,并将 awidth和 aheight属性添加到 SVG 容器。将它们的值设置为900和300并保存文件。

<svg width="900" height="300"></svg>

在浏览器中重新加载项目并在检查工具中找到 SVG 节点。请注意,宽度和高度属性现在显示在 SVG 容器的括号内。如果将鼠标移到检查工具 DOM 树中的 SVG 节点上,您还会看到视口中的 SVG 容器现在的大小为 900 像素 x 300 像素。

图1.11 SVG节点采用其属性指定的大小


为了帮助我们查看 SVG 容器,而不必从检查器中突出显示它,让我们给它一个边框。向 SVG 容器添加样式属性并插入 CSS 边框属性。在下一个代码片段中,我们使用 border 速记属性创建一个宽度为 1px 的黑色实心边框。

<svg width="900" height="300" style="border:1px solid black;"></svg>

保存文件,重新加载页面并确认 SVG 容器周围有边框。现在,调整浏览器窗口的大小,直到它小于 SVG 容器。您将观察到 SVG 容器保持固定的宽度和高度,并且不适应浏览器窗口的大小。让我们尝试使 SVG 容器具有响应能力。

之前,我们将 SVG 属性设置为绝对值 (900和300),浏览器将它们解释为以像素为单位的测量值 (900px和300px)。但我们也可以使用百分比。在文本编辑器中,将宽度属性更改为相对值“ 100%”,保存文件并重新加载页面。

<svg width="100%" height="300" style="border:1px solid black;"></svg>

再次调整浏览器大小,并注意 SVG 如何获取可用的完整宽度并保持 300 像素的固定高度。这更好,但我们失去了原来的纵横比。

为了使内联 SVG 具有响应能力,我们可以使用viewBox 属性。在代码编辑器中,从 SVG 容器中删除width和属性,并将它们替换为属性。给它一个值。heightviewBox"0 0 900 300"

<svg viewBox="0 0 900 300" style="border:1px solid black;"></svg>

再次调整浏览器窗口的大小。你注意到了什么?SVG 容器现在可以适应任何屏幕尺寸,同时保持其纵横比为 900:300。我们有一个响应式 SVG!

正如您所注意到的,viewBox 属性由四个值的列表组成。前两个数字指定 viewBox 坐标系的原点(x 和 y)。在本书中,我们将始终使用0 0,但很高兴知道这些值可用于更改 SVG 容器的哪一部分在屏幕上可见。viewBox 属性的最后两个数字是其宽度和高度。它们定义 SVG 的纵横比,并确保其完美缩放以适合任何容器而不变形。

安装在容器内是这里的关键。到目前为止,我们的内联 SVG 的容器是 HTML<body>元素,它通常会扩展以适应浏览器的视口。如果视口变得非常大,SVG 也会变得非常大。通常,我们希望 SVG 具有最大宽度,以便它不会大于页面上的其余内容。为此,请将 SVG 容器包装在宽度为 100%、最大宽度为 1200px 的 div 内。为简单起见,我们将这些属性设置为内联样式,但在实际项目中,这些属性将来自 CSS 文件。请注意,我们还添加了值边距“ 0 auto”,以使 SVG 在页面上水平居中。

<div style="width:100%; max-width:1200px; margin:0 auto;">
  <svg viewBox="0 0 900 300" style="border:1px solid black;"> ... </svg>
</div>

尝试再次调整浏览器的大小,看看我们的 SVG 如何优雅地适应任何屏幕尺寸,同时尊重其容器的最大宽度。该策略有助于将 D3 可视化注入响应式网页,我们将在本书中使用它。

svg坐标系

既然我们知道了如何使内联 SVG 具有响应性,那么解决 SVG 形状在 SVG 容器内的定位方式就很重要了。SVG 容器就像一张白纸,我们可以在上面绘制矢量形状。矢量形状是根据基本几何原理定义的,并参考 SVG 容器的坐标系进行定位。

SVG 坐标系与笛卡尔坐标系类似。其 2D 平面使用两个垂直轴来确定元素的位置,称为 x 和 y。这两个轴源自SVG 容器的左上角,如图 1.12 所示。意思是y轴的正方向是从上到下。记住这一点可以让你避免一些头痛!

图1.12 SVG容器的坐标系和元素的位置


为了在 SVG 容器内定位元素,我们从左上角的原点开始向右移动。这将为我们提供元素的水平 (x) 位置。对于垂直 (y) 位置,我们从顶部开始向下移动。这些位置由每个 SVG 形状的表示属性定义。

现在,我们将了解您在构建 D3 项目时经常遇到的 SVG 形状。我们还将讨论它们的主要表现属性。这里的目标绝不是编写 SVG 提供的所有形状和功能的综合指南,而是涵盖支持您的 D3 之旅的基础知识。

数据可视化技巧:几何基元

出色的艺术家可以用矢量图形绘制任何东西,但您可能不会因为您是一名艺术家而关注 D3。相反,您正在处理图形并考虑更务实的目标。从这个角度来看,理解几何基元(也称为图形基元)的概念至关重要。几何基元是简单的形状,例如点、线、圆和矩形。这些形状可以组合成更复杂的图形,特别方便直观地显示信息。

基元对于理解您在现实世界中看到的复杂信息可视化也很有用。树形布局,就像我们将在第 10 章中构建的那样,当您意识到它们只是圆形和直线时,它们就不那么令人生畏了。当您将交互式时间线视为矩形和点的集合时,它们更容易理解和创建。即使是主要以多边形、点和线形式出现的地理数据,当您将其分解为最基本的图形结构时,也不会那么混乱。

线

线条元素可能是所有 SVG 形状中最简单的。它获取两个点的位置,设置为属性,并在它们之间绘制一条直线。返回到该index.html文件,并在 SVG 容器内添加一个<line />元素。声明其属性x1和y1并分别赋予它们值 50 和 45。这意味着我们的线的起点位于(50, 45)SVG 容器的坐标系中。如果从 SVG 容器的左上角开始,向右移动 50 像素,向下移动 45 像素,您将遇到线条的起点。(140, 225)同样,使用属性x2和y2将线的端点设置为。

<svg>
  <line x1="50" y1="45" x2="140" y2="225" />
</svg>

图1.13 在SVG容器的坐标系中定位线元素


如果您保存并重新加载项目,您的线条将不可见,您可能想知道发生了什么。为了使 SVG 线条在屏幕上可见,我们还需要设置其描边属性,该属性控制线条的颜色。border 属性的值与 CSS color 属性类似。它可以是颜色名称 ( black, blue, ...)、RGB 颜色 ( rgb(255,0,0)) 或十六进制值 ( #808080)。向您的线条添加笔划属性,并为其指定您选择的颜色(我们使用黑色)。现在它应该在屏幕上可见。

<line x1="50" y1="45" x2="140" y2="225" stroke="black" />

如果我们想设置线条的宽度,我们可以使用描边宽度属性。此属性接受绝对数字(转换为像素)或相对值 (%)。例如,以下行的 a 为stroke-width3px。如果stroke-width未声明该属性,浏览器将应用默认值 1px。

<line x1="50" y1="45" x2="140" y2="225" stroke="black" stroke-width="3" />

打开浏览器的检查器工具并找到 SVG 节点及其包含的行。双击其中一个属性,更改其值并观察新值如何修改线的起点或终点。花时间尝试不同的值,以确认您了解属性x1、y1、x2和如何y2影响线条的位置和长度。

-20现在,为属性赋予 值x1。你看到线的起点是如何消失的吗?落在 SVG viewBox 之外的任何形状或形状部分在屏幕上都不可见。不过,该元素仍然存在于 DOM 中。我们可以访问和操纵它。如果 SVG 中的某个元素不可见,并且您不知道为什么首先要检查它是否在 SVG viewBox 之外!请记住,您始终可以通过使用开发人员工具检查 DOM 来找到它。正如我们之前所做的那样,如果将鼠标移到检查器工具中的元素上,即使它位于 SVG viewBox 之外,它也会在视口中突出显示。

图 1.14 SVG 线在 SVG 容器外部时部分隐藏



笔记

为了提高效率,大多数 SVG 元素只需要一个自闭合标签(我们使用 <line /> 而不是 <line></line>)。与其他一些 HTML 标签一样,SVG 元素的固有结构在自闭合标签中提供了所有必需的信息。这对于 SVG 文本元素有所不同,其中文本放置在开始标签和结束标签之间。

长方形

顾名思义,矩形元素<rect />在屏幕上绘制一个矩形形状。该<rect />元素需要四个属性才能可见。属性x和y声明矩形左上角的位置,而属性width和height分别控制其宽度和高度。<rect />在 SVG 容器中添加以下元素及其属性。

<rect x="260" y="25" width="120" height="60" />

在我们的示例中,矩形的左上角位于SVG 容器原点的260px右侧和下方。25px它的宽度为120px,高度为60px。与其他位置属性一样,我们可以使用百分比而不是绝对数字来设置它们的值。例如,如果我们将该width属性设置为50%,则矩形将扩展到 SVG 容器宽度的一半。

图 1.15 在 SVG 容器的坐标系中定位矩形并调整其大小


您可能已经注意到我们的矩形充满了黑色。默认情况下,浏览器对大多数 SVG 形状应用黑色填充。我们可以通过设置fill属性并为其指定任何 CSS 颜色来更改该颜色。如果我们想给矩形添加边框,我们添加一个描边属性。图 1.16 显示了一些示例。请注意,如果不声明属性,则矩形周围不会绘制边框stroke。另外,在最后一个矩形中,属性fill-opacity和border-opacity用于使fill和stroke半透明。与 CSS 中一样,不透明度可以设置为绝对值 ( 0.3) 或百分比 (30%)。与填充和描边相关的所有属性也可以从 CSS 文件设置或修改。

图 1.16 应用于矩形 SVG 形状的不同样式属性


如果您希望矩形具有圆角,则只需添加rxry属性,分别是水平和垂直角半径。这些属性接受绝对值(以像素为单位)和相对值(百分比)。例如,下面矩形的每个角的半径都是 20px。将此矩形添加到您的形状库中。

<rect x="260" y="100" width="120" height="60" rx="20" ry="20" />

此时,您可能想知道 SVG 中是否有一个可以绘制正方形的元素。我们不需要一个!在 SVG 中,我们<rect />通过赋予元素相等width和height属性来绘制带有元素的正方形。例如,以下<rect />元素将绘制一个 60px x 60px 的正方形。也将它添加到您的形状库中。

<rect x="260" y="175" width="60" height="60" />

作为参考,我们的形状库中现在有三种类型的 SVG 矩形:经典矩形、圆角矩形和正方形。为了好玩,我们给了它们颜色#6ba5d7并玩弄它们stroke和fill属性。请注意,只有笔划在正方形上可见,因为其fill属性值为transparent或none。您的矩形应该类似于图 1.17 中的矩形,除非您更改了它们的属性(我们鼓励您这样做)!

<rect x="260" y="25" width="120" height="60" fill="#6ba5d7" />
<rect x="260" y="100" width="120" height="60" rx="20" ry="20"
➥  fill="#6ba5d7" />
<rect x="260" y="175" width="60" height="60" fill="transparent"
➥  stroke="#6ba5d7" />

图1.17 三种SVG矩形


SVG笔画的位置

当您尝试在可视化中对齐形状时需要记住的一点是,笔划是均匀地绘制在 SVG 形状的内部和外部边界上的。如下图所示,如果矩形的width属性为 40px,则应用 ofstroke-width会1在视觉上向矩形的左侧添加 0.5px,向右侧添加 0.5px(而不是像我们本能地认为的那样,向每边添加 1px) ),实际总宽度为 41px。如果stroke-width是2,它会在每边添加 1px,依此类推。

笔划宽度对 SVG 形状实际宽度的影响


圆和椭圆

圆形形状经常用于数据可视化。它们自然地吸引眼球,并使可视化感觉更加友好和有趣。我们使用<circle />元素绘制 SVG 圆圈。其所需属性是圆心的位置 ( cx , cy ) 及其半径 ( r )。圆的半径是从圆心到其边界上任意点所绘制的直线的长度。将以下圆圈添加到您的形状库中。将其中心定位于(530, 80)并为其指定 50px 的半径。

<circle cx="530" cy="80" r="50" />

图 1.18 在 SVG 容器的坐标系中定位圆和椭圆并调整其大小


您还可以使用圆形的填充和描边属性。为了生成图 1.18 中的效果,我们使用了透明填充和 3px 的描边,颜色为#81c21c。

类似地,<ellipse />元素需要形状中心位置的属性 ( cx, cy)。圆形具有恒定的半径,而椭圆形的半径则不同,从而使其具有扁平形状。我们通过声明水平半径 ( rx ) 和垂直半径 ( ry ) 来创建这种扁平化效果。将下一个片段添加到您的图库中。它将在圆下方绘制一个椭圆,水平半径为 50px,垂直半径为 30px。

<ellipse cx="530" cy="205" rx="50" ry="30" />

路径

SVG路径是迄今为止所有 SVG 元素中最灵活的。它们在 D3 中广泛用于绘制几乎所有无法用迄今为止讨论的形状基元之一(直线、矩形、圆形和椭圆形)表示的复杂形状和曲线。

d我们通过声明其属性(代表“draw”)来指示浏览器如何绘制路径。该d属性包含一个命令列表,从开始绘制路径的位置到要使用的曲线类型,直到指定我们是否希望路径成为闭合形状。例如,将以下路径元素添加到您的库中。在此示例中,d属性以M680 150开头,表示“移动到坐标(680, 150)”。然后我们从当前点 (680, 150) 到字母 C 后面的第三个坐标 (755 150) 指定的端点绘制一条三次贝塞尔曲线。三次贝塞尔曲线需要控制点,即字母 C 后面、起点和终点之间的坐标((710, 80) 和 (725, 80))。这些控制点定义了曲线的陡峭程度。然后我们有字母 S,它代表“停止”。它的工作原理与字母 C 类似,只不过它通向曲线的端点。这里最后一条曲线的起点是(755 150),终点是(840, 150),控制点是(810, 220)。曲线可以由一个或两个控制点定义。

<path d="M680 150 C 710 80, 725 80, 755 150 S 810 220, 840 150" fill="none" 
➥  stroke="#773b9a" stroke-width="3" />

图 1.19 一个简单的 SVG 路径及其控制点


要深入了解 SVG 路径,请参阅 MDN 的教程:https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths。

手动编写d属性对于简单的形状是可行的,但随着形状变得复杂而变得乏味。幸运的是,D3 具有强大的形状生成器,我们将在第 4 章中讨论。

关于路径要记住的另一件重要的事情是浏览器将以黑色填充它们,除非我们将它们的fill属性设置为noneor transparent。即使路径未闭合(如我们的示例中所示),情况也是如此。

文本

<div>内联 SVG 图形的最大优点之一是它们可以包含可导航的文本,就像插入一个或一个元素中的任何其他 HTML 文本一样<p>。这对于可访问性来说是一个很大的优势。

由于数据可视化通常包含多个标签,因此有必要了解如何使用<text></text>元素操作 SVG 文本。让我们向形状库添加标签,以了解 SVG 文本的基本原理。

到目前为止讨论的 SVG 形状使用自闭合标签 ( <line />, <rect />, <path />, ...)。使用 SVGtext元素时,我们需要使用开始标签和结束标签。我们将要显示的文本放置在这两个标签之间。例如,让我们在 SVG 中添加一个表示“line”的文本元素。

<text>line</text>

保存文件并重新加载页面。您可能希望文本出现在 SVG 容器的左上角,但实际上却看不到......这是为什么?默认情况下,SVG 文本的位置是参考其基线计算的,由属性控制dominant-baseline。如果文本基线的坐标是(0, 0),您可以在图 1.20 中看到实际文本如何最终出现在 SVG 容器之外。由于位于 SVG 容器外部的任何元素都是不可见的,因此我们看不到文本。

图 1.20 位于 SVG 容器外部的文本



使用 SVG 文本时要考虑的另一点是文本的流动方式。常规 HTML 元素按照控制内容流的特定规则放置在页面上。如果您将一堆<div></div>元素插入页面中,它们会自然地堆叠在一起,并且它们的内容将重排,以便它永远不会超出其容器。SVG 文本根本不流动,每个 SVG 元素必须单独定位。一种方法是设置它们的x和y属性。如果我们使用这些属性将文本放置在 处(60, 260),标签“line”将出现在形状库中 SVG 线的下方。

<text x="60" y="260">line</text>

为了练习,创建一个新的文本元素,将标签“矩形”放置在矩形和正方形下方。

到目前为止,我们已经使用x和y属性来声明文本元素的左下角。但是如果我们想设置文本中点的位置怎么办?我们可以通过使用属性text-anchor并为其指定值来做到这一点middle。例如,我们可以使用此属性将圆形的文本标签居中。

<text x="530" y="155" text-anchor="middle">circle</text>

图1.21 text-anchor属性影响SVG文本的对齐方式。它的默认值是“开始”。为了根据中间对齐文本元素,我们应用“middle”的 text-andchor 属性。类似地,为了根据文本的结尾对齐文本,我们应用“end”的 text-andchor 属性。


最后为椭圆添加一个标签,为路径元素添加另一个标签。默认情况下,SVG 文本为黑色。您可以使用属性更改其颜色fill。

分组元素

我们将在本节中讨论的最后一个 SVG 元素是组元素。group 或<g></g>元素与我们迄今为止讨论的 SVG 元素不同,因为它没有图形表示,也不作为有界空间存在。相反,它是元素的逻辑分组。在创建由多个形状和文本元素组成的可视化效果时,您需要广泛使用组。

如果我们希望正方形和“rect”标签一起显示并在 SVG 容器中作为一个整体移动,我们可以将它们放置在一个<g>元素内,如下例所示。请注意元素的左上角如何<rect>更改为(0, 0)。位于<text>处以(0, 85)保持其低于<rect>。

<g>
  <rect x="0" y="0" width="60" height="60" />
  <text x="0" y="85">rect</text>
</g>

包含正方形及其标签的组现在显示在 SVG 容器的左上角。我们可以将这个组及其包含的所有元素移动到 SVG 容器中任何我们想要的位置,同时保持正方形与其标签之间的对齐。

在 SVG 容器中移动组是通过转换属性完成的。变换属性比目前讨论的属性有点吓人,但与 CSS 变换属性相同。它采用一个变换(平移、旋转、缩放等)或一堆变换作为值。为了移动一个组,我们使用translate(x, y)变换。如果我们想要将<rect>和<text>元素移回其原始位置,我们需要对元素应用向右 260 像素和向下 175 像素的平移。<g>为此,我们将其 Transform 属性设置为transform="translate(260,175)"。

<g transform="translate(260,175)">
  <rect x="0" y="0" width="60" height="60" />
  <text x="0" y="85">rect</text>
</g>

<g> 元素的另一个有用的方面是它的子元素继承它的属性。为了说明这一点,让我们将<g>元素中所有剩余的文本元素分组,除了标签“矩形”,我们已经将其与正方形分组。

<g>
  <text x="60" y="260">line</text>
  <text x="530" y="155" style="text-anchor:middle">circle</text>
  <text x="530" y="260" style="text-anchor:middle">ellipse</text>
  <text x="730" y="260">path</text>
</g>

#636466如果我们对组应用填充属性,<text>则该组内的每个元素将继承相同的颜色。同样,如果我们向组添加样式属性,例如使用font-family和font-size属性,则组内的文本将继承这些属性。

<g fill="#636466" style="font-size:16px; font-family:monospace">
  <text x="60" y="260">line</text>
  <text x="530" y="155" style="text-anchor:middle">circle</text>
  <text x="530" y="260" style="text-anchor:middle">ellipse</text>
  <text x="730" y="260">path</text>
</g>

最后重新加载页面并观察组内的标签如何继承组的颜色和字体,而保留在该组之外的标签则保持其原始外观。这种将共享属性应用于组元素的技术非常方便,可以帮助您将 DRY(不要重复自己)编码原则应用到您的工作中。当您需要更新这些属性时,它也会让您的生活更轻松。

恭喜您完成了本书的第一个练习!您可以在清单 1.1.b 和编码文件的末尾文件夹中找到形状库的完整代码。当您构建第一个 D3 项目时,请使用此练习作为参考。

清单 1.1.b 用于 SVG 形状图库练习的完整 HTML 文件

<!DOCTYPE html>
<html>
<head> [...] </head>
<body>
  <div style="width:100%; max-width:1200px; margin:0 auto;">
    <svg viewBox="0 0 900 300" style="border:1px solid black;">
 
      <line x1="50" y1="45" x2="140" y2="225" stroke="black" />
 
      <rect x="260" y="25" width="120" height="60" fill="#6ba5d7" />
      <rect x="260" y="100" width="120" height="60" rx="20" ry="20"  
      ➥  fill="#6ba5d7" /> 
      <g transform="translate(260, 175)">
        <rect x="0" y="0" width="60" height="60" fill="transparent"
        ➥  stroke="#6ba5d7" />
        <text x="0" y="85">rect</text>
      </g>
 
      <circle cx="530" cy="80" r="50" fill="none" stroke="#81c21c" stroke-
      ➥  width="3" />
      <ellipse cx="530" cy="205" rx="50" ry="30" fill="#81c21c" />
 
      <path d="M680 150 C 710 80, 725 80, 755 150 S 810 220, 840 150" 
      ➥  fill="none" stroke="#773b9a" stroke-width="3" />
 
      <g fill="#636466" style="font-size:16px; font-family:monospace">
        <text x="60" y="260">line</text>
        <text x="530" y="155" style="text-anchor:middle">circle</text>
        <text x="530" y="260" style="text-anchor:middle">ellipse</text>
        <text x="730" y="260">path</text>
      </g>
 
    </svg>
  </div>
</body>
</html>

练习:创建 SVG 图形

现在轮到你了!创建如下图所示的 SVG 图形。您可以在本章代码文件start内的文件夹中工作。02_SVG_exercise以下是一些指导原则:

· 创建一个宽度和高度均为 400 像素的响应式 SVG 容器(当屏幕上有足够的空间时)。

· 绘制一个宽度和高度均为 200 像素的正方形。将其置于 SVG 容器的中心,并为其提供透明填充和 5px 黑色描边。

· 在 SVG 容器的中心添加一个半径为 100px 的圆。将其填充属性设置为 CSS 颜色名称“plum”。

· 绘制两条对角黑线,笔划为 5 像素。一个从正方形的左上角到右下角。另一个从正方形的右上角到左下角。

· 添加文本“SVG 太棒了!” 位于正方形上方并将其置于 SVG 容器的中心。为文本指定以下样式属性:字体大小为 18px,字体系列为 sans-serif。

我们鼓励您构建此 SVG 图形来强化本节中讨论的概念。


02_SVG_exercise / end您可以在附录 D 的 D.1.1 节和本章代码文件的文件夹中找到解决方案。我们鼓励您尝试自己完成它。

1.2.3 画布和webGL

我们已经提到过,我们通常使用 SVG 元素构建 D3 项目。有时,我们可能需要从大型数据集创建复杂的可视化效果,而传统的 SVG 方法可能会产生性能问题。请务必记住,对于数据可视化中的每个图形细节,D3 都会将一个或多个 SVG 元素附加到 DOM。一个典型的例子是由数千个节点和链接组成的大型网络可视化。这些可能会让你的浏览器喘不过气来……尽管浏览器可以轻松处理的对象数量随着性能的提高而不断增加,但普遍接受的经验法则是,如果满足以下条件,我们应该考虑使用画布而不是 SVG:可视化包含 1000 多个元素。

Canvas 是一种客户端绘图 API,它使用脚本(通常是 JavaScript)来创建视觉效果和动画。它不会将 XML 元素添加到 DOM,这在从大型数据集构建可视化时可以显着提高性能。

Canvas 还允许您使用 WebGL API 来创建 3D 对象。尽管学习 WebGL 超出了本书的范围,但为 Web 创建 3D 数据可视化是可能的。目前主要用于实验项目。在第 15 章中,我们将介绍如何使用画布构建可视化并讨论其优点和缺点。

1.2.4 CSS

CSS 代表层叠样式表,是一种描述 DOM 元素如何在屏幕上显示及其外观的语言。从页面的整体网格布局到文本使用的字体系列,再到散点图中圆圈的颜色,CSS 可以将普通的 HTML 文件变成令人惊叹的网页。在 D3 项目中,我们通常使用内联样式或通过外部样式表应用 CSS 样式。

内联样式应用于具有该style属性的元素,如以下示例所示。该style属性可以在传统的 HTML 或 SVG 元素上使用,D3 有一个方便的方法来设置或修改该属性,我们将在第 2 章中讨论。

<div style="padding:10px; background:#00ced1;"> ... </div>
<text style="font-size:16px; font-family:serif;"> ... </text>

内联样式仅影响应用它们的元素。如果我们想要将相同的设计传播到多个元素,我们需要将相同的style属性应用于每个元素(或将所有元素包装在一起的 SVG 组)。它当然有效,但不是最有效的方法。

另一方面,外部 CSS 样式表非常适合全局应用样式。一种策略是要求 D3 将相同的类名添加到多个元素。然后,我们使用此类名称作为外部样式表中的选择器,并将相同的样式属性应用于目标元素组,如以下示例所示。这种方法效率更高,尤其是在维护大型项目时。它还遵循关注点分离原则,即我们将由 JavaScript 控制的行为与由 CSS 监管的样式分开。请注意,CSS 预处理器(例如 SASS 和 LESS)是此处描述的外部样式表方法的一部分。

在 CSS 样式表中:

.my-class {
  font-size: 16px;
  font-family: serif;
}
 
In the DOM:
<text class="my-class"> ... </text>

请记住,内联样式优先于从外部样式表应用的样式。在任何前端开发项目中,规划 CSS 样式的架构并考虑级联顺序非常重要。

1.2.5 JavaScript

D3 是一个 JavaScript 库。它在 JavaScript 现有核心功能的基础上添加了新方法。这意味着在使用 D3 时,具有一点 JavaScript 经验会很有帮助。这也意味着,在构建 D3 项目时,您可以访问所有现有的 JavaScript 功能。

在本节中,我们将解释 D3 项目中广泛使用的两个 JavaScript 主题:方法链和对象操作。

方法链接

如果您在网络上搜索 D3 项目的示例,您会发现在同一选择上会依次调用方法。这种技术就是我们所说的方法链,有助于保持代码简洁和可读。

我们可以将方法链视为汽车装配线。假设我们编写了运行这样一条装配线的脚本。正如您在下面的示例中看到的,我们首先声明一个car创建新Car()对象的变量。然后我们调用函数putOnHood(),将引擎盖放在汽车顶部,然后我们继续调用将放置车轮、轮胎和灯的函数。每个连续的调用都会添加一个元素到Car()对象,并且,一旦执行了所有方法,汽车就有了引擎盖、车轮、轮胎和车灯。每个方法都会将更新后的汽车对象传递给下一个方法,从而“链接”。请注意,每个调用都用点分隔,并且调用方法的顺序很重要。在我们的汽车装配线示例中,我们需要先安装车轮,然后才能将轮胎安装到车轮上。

let car = new Car().putOnHood().putOnWheels().putOnTires().putOnLights();

现在让我们看看如何在 D3 中使用方法链接。想象一下,我们想要从 DOM 中获取所有 div 并在每个 div 中添加一个段落元素。段落元素应具有类属性my-class并包含文本“Wow”。然后,我们要在每个段落中插入一个 span 元素,并将文本“Even More Wow”以粗体显示。如果没有方法链接,我们需要将每个操作存储到一个常量中,然后在执行下一个操作时调用该常量,如下所示。光是看着就已经很累了……

const mySelection = d3.selectAll("div");
const myParagraphs = mySelection.append("p");
const myParagraphsWithAClass = myParagraphs.attr("class", "my-class");
const myParagraphsWithText = myParagraphsWithAClass.text("Wow");
const mySpans = myParagraphsWithText.append("span");
const mySpansWithText = mySpans.text("Even More Wow")
const myBoldSpans = mySpansWithText.style("font-weight", "900");

由于方法链接,相同的示例变得更加简洁。

d3.selectAll("div").append("p").attr("class", "my-class").text("Wow")
   ➥ .append("span").text("Even More Wow").style("font-weight", "900");

在 D3 中,断行(JavaScript 会忽略这一点)以及缩进链接方法是很常见的。这使得代码更容易阅读,并且缩进可以帮助我们看到我们正在处理哪个元素。

d3.selectAll("div")
  .append("p")
    .attr("class", "my-class")
    .text("Wow")
  .append("span")
    .text("Even More Wow")
    .style("font-weight", "900");

不要担心理解前面的代码示例的作用,尽管您完全可以从不同方法的名称中猜出它!目前,我们只希望您熟悉如何在 JavaScript 中链接方法。我们将在第 2 章中介绍 D3 特定的术语。

数组和对象操作

D3 都是关于数据的,而数据通常被构造为 JavaScript 对象。了解这些对象的构造以及如何访问和操作它们包含的数据将为您构建可视化提供巨大帮助。

我们首先讨论简单数组,它是元素列表。在与数据相关的项目中,数组通常是数字或字符串的有序列表。

const arrayOfNumbers = [17, 82, 9, 500, 40];
const arrayOfStrings = ["blue", "red", "yellow", "orange"];

数组中的每个元素都有一个数字位置,称为索引,数组中第一个元素的索引为 0。

arrayOfNumbers[0]   // => 17
arrayOfStrings[2]   // => "yellow"

数组具有长度属性,对于非稀疏数组,该属性指定它们包含的元素数量。由于数组是零索引的,因此数组中最后一个元素的索引对应于数组长度减一。

arrayOfNumbers.length;                      // => 5
arrayOfStrings[arrayOfStrings.length - 1]   // => "orange"

我们还可以使用方法来确定数组是否包含特定值includes()。true如果数组中的元素之一与作为参数传递的值完全对应,则此方法返回。否则,它返回false。

arrayOfNumbers.includes(9)         // => true
arrayOfStrings.includes("pink")    // => false
arrayOfStrings.includes("ellow")   // => false

然而,大多数数据集并不是简单的数字或字符串列表,它们的每个数据点通常由多个属性组成。让我们想象一个虚构机构的员工数据库,如表 1.1 所示。该表包含四列:每个员工的 ID、姓名和职位,以及该员工是否在 D3 工作。

表 1.1 包含员工及其职位的小型数据集

ID

姓名

位置

与_d3一起工作

1

佐伊

数据分析师

错误的

2

詹姆士

前端开发人员

真的

3

爱丽丝

全栈开发人员

真的

4

休伯特

设计师

错误的

数据集中的每一行或数据点都可以由 JavaScript 对象表示,如下所示row1。

const row1 = {
               id:"1",
               name:"Zoe",
               position:"Data analyst",
               works_with_d3:false
             };

我们可以使用点符号轻松访问对象中每个属性的值。

row1.name            // => "Zoe"
row1.works_with_d3   // => false

我们还可以使用括号表示法访问这些值。如果属性名称包含空格等特殊字符,或者如果我们之前将属性名称保存在常量或变量中,那么括号表示法会很方便。

row1["position"]                      // => "Data analyst"
 
const myProperty = "works_with_d3";
row1[myProperty]                      // => false

在现实生活中,数据集通常被格式化为对象数组。例如,如果我们使用 D3 加载表 1.2 中包含的数据集(正如我们将在第 3 章中学习的那样),我们将获得以下对象数组,可以将其保存在名为 的常量中data。

const data = [
 {id:"1", name:"Zoe", position:"Data analyst", works_with_d3:false},
 {id:"2", name:"James", position:"Frontend developer", works_with_d3:true},
 {id:"3", name:"Alice", position:"Fullstack developer", works_with_d3:true},
 {id:"4", name:"Hubert", position:"Designer", works_with_d3:false}
];

data我们可以使用循环遍历数组中的每个元素或数据点。更具体地说,JavaScript forEach循环非常方便且易于编写和阅读。迭代数据集的一个常见用例是数据整理。当我们加载外部 CSV 文件时,数字通常被格式化为字符串。让我们以data数组为例,将属性的值id从字符串转换为数字。

在下面的示例中,数组迭代器使d我们能够访问每个对象。使用点符号,我们id使用运算符将​每个值转换为数字+。

data.forEach(d => {
  d.id = +d.id;
});

JavaScript 提供了许多数组迭代器方法,可以帮助我们与数据交互,甚至在需要时重塑数据。假设我们想要将数据集中的每个员工定位到可视化上。创建一个仅包含员工姓名的简单数组可能会派上用场,为此我们将使用map()方法。

data.map(d => d.name);   // => ["Zoe", "James", "Alice", "Hubert"]

同样,如果我们只想隔离使用 D3 的员工,我们可以使用这些filter()方法。

data.filter(d => d.works_with_d3);
 
// => [
   {id:2, name:"James", position:"Frontend developer", works_with_d3:true},
   {id:4, name:"Hubert", position:"Designer", works_with_d3:true}
  ];

最后,我们可以通过该方法找到id为3的员工find()。请注意,该find()方法在找到它要查找的值后停止迭代。我们只能在搜索单个数据点时使用此方法。

data.find(d => d.id === 3);
 
// => {id:"3", name:"Alice", position:"Fullstack developer", works_with_d3:true}

本节讨论的方法远未涵盖 JavaScript 提供的所有数组和对象操作技术。但在处理数据时,您可能会不断回想起它们。每当您需要找到另一种方法来访问或操作数据时,MDN Web 文档 ( https://developer.mozilla.org/ ) 始终是包含大量示例的可靠参考。

1.2.6 Node 和 JavaScript 框架

JavaScript 在过去十年中发生了重大变化。现代 JavaScript 的两个最重要的趋势是 Node.js 的兴起和 JavaScript 框架作为大多数项目标准的建立。

对于 D3 项目,我们想了解的主要 Node 技术是 NPM,即 Node Package Manager。NPM 允许您安装“模块”或小型 JavaScript 代码库以在应用程序中使用。您不必包含<script>对单个文件的一堆标记引用,并且如果模块已构建为不是一个整体结构,则可以减少应用程序中包含的代码量。

D3.js 版本 7 于 2021 年中期发布,利用了模块导入的优势。在本书中,您将看到以两种方式之一使用 D3 的示例。我们要么加载整个 D3 库,就像我们在第 2 章中所做的那样,要么只包含我们需要的 D3 的各个部分,正如您将在后面的示例中看到的那样。我们可以使用脚本标签来做到这一点,但从第 2 部分开始,我们将使用 NPM 导入 D3 模块,因为这被认为是当今的标准做法。如果您交付专业的 D3 项目,您可能需要熟悉它。

如果您已经参与了专业的 Web 项目,那么您也很有可能正在使用 JavaScript 框架,例如 React、Angular、Vue 或 Svelte。框架为开发人员提供了使用模块化、可重用且可测试的代码构建 Web 项目的基础。这些框架负责构建和更新 DOM,这也是 D3 库所做的事情。在第 8 章中,我们将讨论在 JavaScript 框架内构建 D3 可视化时避免冲突的策略。

最后,在专业的工作环境中,您可能会将 D3 与 TypeScript 结合使用。TypeScript 是 JavaScript 的语法超集,为代码添加了类型安全性。尽管我们不会在本书中详细讨论它,但 D3 方法的类型可以使用 NPM 包 @type/d3 ( https://www.npmjs.com/package/@types/d3 ) 进行安装。在第 8 章中,我们将在 Angular 项目中使用此类类型。

1.2.7 可观察的笔记本

如果您在网络上搜索 D3 项目的示例,您无疑会遇到 Observable 笔记本 ( observablehq.com )。Observable 是数据科学和可视化的协作平台,类似于 Python 项目的 Jupyter 环境。Observable 平台由 Mike Bostock 创建,取代了之前的在线 D3 沙箱bl.ocks.org 。所有官方的 D3 示例现在都位于 Observable 上,并且 D3 社区在那里非常活跃。

重要的是要知道 Observable 要求您学习一种处理特定于该平台的 D3 项目的方法。此外,您不能直接将 Observable Notebook 复制粘贴到前端开发环境中(但有多种方法可以导出和重用它们)。由于本书的重点是在类似于我们如何交付 D3 项目进行生产的环境中构建 D3 可视化,因此我们不会讨论 Observable 笔记本。如果您有兴趣学习 Observable,您可以在observablehq.com/tutorials找到一系列优秀的教程。您将在本书中学到的大部分技术和概念都可以转化为 Observable 笔记本。

1.3 D3.js 表达的数据可视化标准

数据可视化从未像今天这样流行。丰富的地图、图表以及系统和数据集的复杂表示不仅存在于工作场所,而且还存在于我们的娱乐和日常生活中。随着这种流行,使用视觉手段以及美学规则来表示数据和信息的类和子类库不断增长,以促进易读性和理解性。您的受众,无论是公众、学者还是决策者,已经习惯了我们曾经认为极其抽象和复杂的数据趋势表示。这使得 D3 这样的库不仅受到数据科学家的欢迎,而且还受到记者、艺术家、学者、IT 专业人士,甚至数据可视化爱好者的欢迎。

如此丰富的选项似乎让人不知所措,而且修改数据集以显示在流图、树状图或直方图中相对容易,这往往会促进这样一种观念:信息可视化更多地是关于风格而不是实质内容。幸运的是,完善的规则规定了针对不同系统的不同数据类型使用哪些图表和方法。本书并不旨在涵盖数据可视化中的所有最佳实践,但我们将介绍其中的一些。尽管开发人员使用 D3 彻底改变了颜色和布局的使用,但大多数人希望创建支持实际问题的数据的可视化表示。

当您构建第一个可视化项目时,如果有疑问,请简化 - 通常,呈现直方图比小提琴图更好,或者分层网络布局(如树状图)比力导向的网络布局更好。视觉上更复杂的数据显示方法往往会激发更多的兴奋,但也会导致观众关注图形的美观而不是数据。创建酷炫且令人瞠目结舌的可视化并没有什么错,但我们永远不应该忘记任何数据可视化的主要目标都是讲述一个故事。询问周围的人是否理解你的可视化以及他们如何解释它是至关重要的一步。他们需要解释吗?他们可以从与您的项目的互动中得出哪些结论?故事被讲述了吗?

尽管如此,为了正确部署信息可视化,您应该知道该做什么和不该做什么。您需要对您的数据和受众有深入的了解。D3 赋予我们巨大的灵活性,但正如俗话所说,“能力越大,责任越大”。虽然知道某些图表更适合表示特定类型的数据固然很好,但更重要的是要记住,如果不谨慎地从知情的角度构建数据可视化,则可能会携带错误信息。如果您打算设计自己的可视化,那么了解数据可视化最佳实践是至关重要的。了解这一点的最佳方法是回顾知名设计师和信息可视化从业者的工作。尽管整个图书馆的作品都在处理这些问题,以下是我们发现的一些有用的内容,可以帮助您了解基础知识。这些绝不是学习数据可视化的唯一文本,但它们是一个很好的起点。

  • 更好的数据可视化,乔纳森施瓦比什
  • 功能艺术真实艺术图表如何谎言,阿尔贝托·开罗
  • 数据可视化数据驱动设计手册,安迪·柯克
  • 定量信息的视觉显示设想信息,爱德华·塔夫特
  • 信息设计,伊莎贝尔·梅雷莱斯
  • 模式识别,克里斯蒂安·斯温哈特
  • 可视化分析与设计,Tamara Munzner

在阅读有关数据可视化的内容时要记住的一件事是,文献通常关注静态图表。使用 D3,您将进行交互式动态可视化。一些交互可以使可视化不仅更具可读性,而且更有吸引力。感觉自己是在探索而不是阅读的用户,即使只是将鼠标悬停在事件上几次或简单地单击进行缩放,也可能会发现可视化的内容比阅读静态等效内容更引人注目和更令人难忘。但这种增加的复杂性需要了解用户体验。我们将在第 7 章中更详细地讨论这一点。

我们的第一章到此结束!尽管我们还没有使用过 D3,但您现在已经掌握了入门所需的所有知识。当您不确定应该在可视化中使用哪个 SVG 元素或者需要提醒如何使用 JavaScript 操作数据时,请继续返回本章。从下一章开始,我们将卷起袖子,创建 D3 可视化。

1.4 总结

  • 当您希望在数据可视化方面拥有完全的创意和技术自由时,D3 是您的首选工具。
  • D3 应用程序的样式和服务与传统 Web 内容类似。
  • D3 从来不会单独使用,而是技术和工具生态系统的一部分,我们将这些技术和工具结合起来创建丰富的 Web 界面:HTML、CSS、JavaScript、SVG、Canvas 和 React 或 Svelte 等框架。
  • 我们在构建数据可视化时最常使用的 SVG 形状是直线、矩形、圆形、椭圆形、路径和文本。
  • 您需要对这些形状及其主要属性有基本的了解才能使用 D3。使用 D3 编写 JavaScript 时,您应该熟悉两个主题:方法链和对象操作。方法链接是一种模式,其中在同一对象上依次调用多个方法。在 D3 中,数据集通常被构造为对象数组。JavaScript 提供了多种方法来访问和操作这些结构中的数据。
  • 作为 D3 开发人员,深入了解数据可视化最佳实践非常重要。多种资源可以帮助您开始学习之旅。

众号:

CSS

1. 请解释CSS的盒模型是什么,并描述其组成部分。

答案:CSS的盒模型是用于布局和定位元素的概念。它由内容区域、内边距、边框和外边距组成,这些部分依次包裹在元素周围。

2. 解释CSS中的选择器及其优先级。

答案:CSS选择器用于选择要应用样式的HTML元素。选择器的优先级规则是:内联样式 > ID选择器 > 类选择器、属性选择器、伪类选择器 > 元素选择器 > 通用选择器。同时,使用!important可以提升样式的优先级。

3. 解释CSS中的浮动(float)是如何工作的,并提供一个示例。

答案:浮动(float)是CSS中用于实现元素的左浮动或右浮动,使其脱离文档流并环绕在其周围的元素。例如:

.float-example {
float: left;
width: 200px;
height: 200px;
}

4. 解释CSS中的定位(position)属性及其不同的取值。

答案:定位(position)属性用于控制元素的定位方式。常见的取值有:static(默认,按照文档流定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和sticky(粘性定位)。

5. 解释CSS中的层叠顺序(z-index)是如何工作的。

答案:层叠顺序(z-index)用于控制元素在垂直方向上的堆叠顺序。具有较高层叠顺序值的元素将显示在较低层叠顺序值的元素之上。默认情况下,层叠顺序值为auto。

6. 解释CSS中的伪类和伪元素的区别,并给出一个示例。

答案:伪类用于向选择器添加特殊的状态,如:hover、:active等。伪元素用于向选择器添加特殊的元素,如::before、::after等。例如:

/* 伪类示例 */
a:hover {
color: red;
}
/* 伪元素示例 */
p::before {
content: "前缀";
}

7. 解释CSS中的盒子模型的两种模式:标准模式和怪异模式。

答案:标准模式是按照W3C标准解析渲染页面的模式。怪异模式是兼容旧版本浏览器的解析渲染页面的模式。可以通过声明来指定使用哪种模式。

8. 解释CSS中的BFC是什么,它的作用是什么?

答案:BFC(块级格式化上下文)是CSS中的一种渲染模式,它创建了一个独立的渲染环境,其中的元素按照一定的规则进行布局和定位。BFC的作用包括:清除浮动、防止外边距重叠等。

9. 解释CSS中的flexbox布局是什么,它的优势是什么?

答案:flexbox布局是一种用于创建灵活的、响应式的布局的CSS模块。它通过flex容器和flex项目的组合来实现强大的布局能力。其优势包括简单易用、自适应性强、对齐和分布控制灵活等。

10.解释CSS中的媒体查询是什么,它的作用是什么?

答案:媒体查询是CSS中的一种技术,用于根据设备的特性和属性来应用不同的样式。通过媒体查询,可以根据屏幕尺寸、设备类型、分辨率等条件来优化页面的布局和样式。

JavaScript

1. 解释JavaScript的数据类型,并举例说明每种类型。

答案:JavaScript有七种数据类型:字符串(String)、数字(Number)、布尔值(Boolean)、对象(Object。Array/数组 和 function/函数 也属于对象的一种)、空值(Null)、未定义(Undefined)、Symbol(独一无二的值,ES6 新增)、BigInt (大整数,能够表示超过 Number 类型大小限制的整数,ES 2020新增)

例如:

let str = "Hello";
let num = 10;
let bool = true;
let obj = { name: "John" };
let arr = [1, 2, 3];
let n = null;
let undef;

2. 解释JavaScript中的变量提升(Hoisting)是什么。

答案:变量提升是指在JavaScript中,变量和函数声明会在代码执行之前被提升到作用域的顶部。这意味着可以在声明之前使用变量和函数。例如:

console.log(x); // 输出 undefined
var x = 5;

3. 解释JavaScript中的闭包(Closure)是什么,并举例说明。

答案:闭包是指函数可以访问并操作其词法作用域之外的变量。它通过在函数内部创建一个内部函数,并返回该内部函数来实现。例如:

function outer() {
let x = 10;
function inner() {
console.log(x);
}
return inner;
}
let closure = outer();
closure(); // 输出 10

4. 解释JavaScript中的事件冒泡(Event Bubbling)和事件捕获(Event Capturing)。

答案:事件冒泡是指事件从最具体的元素开始向父元素逐级触发,直到触发到根元素。事件捕获是指事件从根元素开始,逐级向最具体的元素触发。可以使用addEventListener方法的第三个参数来控制是使用事件冒泡还是事件捕获。

5. 解释JavaScript中的原型继承(Prototype Inheritance)是什么。

答案:原型继承是JavaScript中实现对象之间继承关系的一种机制。每个对象都有一个原型对象,它包含了共享的属性和方法。当访问对象的属性或方法时,如果对象本身没有,则会沿着原型链向上查找。可以使用Object.create()方法或设置对象的__proto__属性来实现原型继承。

6. 解释JavaScript中的异步编程,并提供一个异步操作的示例。

答案:异步编程是指在代码执行过程中,不会阻塞后续代码执行的一种编程方式。常见的异步操作包括网络请求、定时器等。例如:

console.log("开始");
setTimeout(function() {
console.log("异步操作");
}, 1000);
console.log("结束");

7. 解释JavaScript中的this关键字的作用和使用场景。

答案:this关键字在JavaScript中表示当前执行上下文的对象。它的具体取值根据函数的调用方式而定。在全局作用域中,this指向全局对象(浏览器环境中为window对象)。在函数中,this的指向取决于函数的调用方式,可以通过call、apply、bind等方法来显式地指定this的值。

8. 解释JavaScript中的事件委托(Event Delegation)是什么,并提供一个使用事件委托的示例。

答案:事件委托是指将事件处理程序绑定到父元素上,而不是直接绑定到每个子元素上。当事件触发时,事件会冒泡到父元素,然后通过判断事件的目标来执行相应的处理逻辑。这样可以减少事件处理程序的数量,提高性能。例如:

<ul id="list">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
<script>
document.getElementById("list").addEventListener("click", function(event) {
if (event.target.tagName === "LI") {
console.log(event.target.textContent);
}
});
</script>

9. 解释JavaScript中的模块化编程,并提供一个使用模块的示例。

答案:模块化编程是指将代码划分为独立的模块,每个模块负责特定的功能,并通过导入和导出来实现模块之间的依赖关系。ES6引入了模块化的语法,可以使用import和export关键字来导入和导出模块。例如:

// module.js
export function sayHello() {
console.log("Hello!");
}
// main.js
import { sayHello } from "./module.js";
sayHello(); // 输出 "Hello!"

10. 解释JavaScript中的严格模式(Strict Mode)。

答案:严格模式是一种JavaScript的执行模式,它提供了更严格的语法和错误检查。在严格模式下,一些不安全或不推荐的语法会被禁用,同时会引入一些新的特性,如变量必须先声明才能使用、禁止使用this指向全局对象等。

11. 解释JavaScript中的事件冒泡(Event Bubbling)和事件捕获(Event Capturing)。

答案:事件冒泡是指当一个事件在DOM树中触发时,它会从最内层的元素开始向外传播至最外层的元素。事件捕获是指当一个事件在DOM树中触发时,它会从最外层的元素开始向内传播至最内层的元素。

12. 什么是原型链(Prototype Chain)?如何利用原型链实现继承?

答案:原型链是JavaScript中对象之间的连接关系,每个对象都有一个指向其原型(prototype)的引用。通过原型链,对象可以继承其原型对象的属性和方法。可以使用原型链实现继承,通过将一个对象的原型指向另一个对象,从而使得该对象可以访问另一个对象的属性和方法。

13. 解释JavaScript中的防抖(Debounce)和节流(Throttle)。

答案:防抖和节流都是用于控制函数执行频率的技术。防抖指的是在某个时间段内,只执行最后一次触发的函数调用。节流指的是在某个时间段内,按照固定的时间间隔执行函数调用。

14. 什么是事件循环(Event Loop)?请解释JavaScript中的事件循环机制。

答案:事件循环是JavaScript中处理异步操作的机制。事件循环不断地从任务队列中取出任务并执行,直到任务队列为空。事件循环由主线程和任务队列组成,主线程负责执行同步任务,异步任务会被放入任务队列中,等待主线程空闲时被执行。

15. 解释JavaScript中的深拷贝和浅拷贝。

答案:深拷贝是指创建一个新对象,将原始对象的所有属性和嵌套对象的属性都复制到新对象中。浅拷贝是指创建一个新对象,将原始对象的属性复制到新对象中,但嵌套对象的引用仍然是共享的。

16. 什么是异步编程?请列举几种处理异步操作的方法。

答案:异步编程是一种处理可能耗时的操作而不阻塞主线程的编程方式。常见的处理异步操作的方法有回调函数、Promise、async/await和事件监听等。

17. 解释JavaScript中的Hoisting(变量提升)。

答案:变量提升是指在JavaScript中,变量和函数的声明会被提升到当前作用域的顶部。这意味着可以在声明之前使用变量和函数,但它们的赋值或定义仍然在原来的位置。

18. 什么是柯里化(Currying)?请给出一个柯里化的示例。

答案:柯里化是一种将接受多个参数的函数转换为接受一个参数并返回一个新函数的过程。示例:

function add(a) {
return function(b) {
return a + b;
}
}
var add5 = add(5);
console.log(add5(3)); // 输出:8

TypeScript

1. 解释TypeScript和JavaScript之间的关系。

答案:TypeScript是JavaScript的超集,它添加了静态类型和其他一些特性。TypeScript代码可以编译成JavaScript代码,因此可以在任何支持JavaScript的环境中运行。

2. TypeScript中的类型注解是什么?如何使用类型注解?

答案:类型注解是指在变量、函数参数、函数返回值等地方显式地声明类型信息。可以使用冒号(:)后跟类型来添加类型注解。例如:

let num: number = 10;
function add(a: number, b: number): number {
return a + b;
}

3. TypeScript中的接口是什么?如何定义和使用接口?

答案:接口是一种用于定义对象的结构和类型的语法。可以使用interface关键字来定义接口。例如:

interface Person {
name: string;
age: number;
}
function greet(person: Person) {
console.log(`Hello, ${person.name}!`);
}
let john: Person = { name: "John", age: 25 };
greet(john); // 输出 "Hello, John!"

4. TypeScript中的类是什么?如何定义和使用类?

答案:类是一种用于创建对象的蓝图,它包含属性和方法。可以使用class关键字来定义类。例如:

class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, ${this.name}!`);
}
}
let john = new Person("John", 25);
john.greet(); // 输出 "Hello, John!"

5. TypeScript中的泛型是什么?如何使用泛型?

答案:泛型是一种用于创建可重用代码的工具,它允许在定义函数、类或接口时使用占位符类型。可以使用尖括号(<>)来指定泛型类型。例如:

function identity<T>(value: T): T {
return value;
}
let result = identity<string>("Hello");
console.log(result); // 输出 "Hello"

6. TypeScript中的枚举是什么?如何定义和使用枚举?

答案:枚举是一种用于定义命名常量集合的语法。可以使用enum关键字来定义枚举。例如:

enum Color {
Red,
Green,
Blue,
}
let color: Color = Color.Green;
console.log(color); // 输出 1

7. TypeScript中的模块是什么?如何导出和导入模块?

答案:模块是用于组织和封装代码的单元。可以使用export关键字将模块中的变量、函数、类等导出,以便其他模块可以使用。可以使用import关键字来导入其他模块的导出。例如:

// module.ts
export function greet(name: string) {
console.log(`Hello, ${name}!`);
}
// main.ts
import { greet } from "./module";
greet("John"); // 输出 "Hello, John!"

8. TypeScript中的类型推断是什么?如何使用类型推断?

答案:类型推断是指TypeScript根据上下文自动推断变量的类型,而无需显式地添加类型注解。例如:

let num = 10; // 推断为 number 类型
let str = "Hello"; // 推断为 string 类型

9. TypeScript中的命名空间是什么?如何定义和使用命名空间?

答案:命名空间是一种用于组织和封装代码的机制,它避免了全局命名冲突。可以使用namespace关键字来定义命名空间。例如:

namespace MyNamespace {
export function greet(name: string) {
console.log(`Hello, ${name}!`);
}
}
MyNamespace.greet("John"); // 输出 "Hello, John!"

10. TypeScript中的类型别名是什么?如何定义和使用类型别名?

答案:类型别名是给类型起一个别名,以便在代码中更方便地引用。可以使用type关键字来定义类型别名。例如:

type Point = { x: number; y: number };
function printPoint(point: Point) {
console.log(`(${point.x}, ${point.y})`);
}
let p: Point = { x: 1, y: 2 };
printPoint(p); // 输出 "(1, 2)"

VUE2

1. Vue.js是什么?它有哪些特点?

答案:Vue.js是一个用于构建用户界面的JavaScript框架。它具有以下特点:

响应式数据绑定:通过使用Vue的数据绑定语法,可以实现数据的自动更新。 组件化开发:Vue允许将页面划分为独立的组件,提高了代码的可维护性和复用性。 虚拟DOM:Vue使用虚拟DOM来跟踪页面上的变化,并高效地更新实际的DOM。 指令系统:Vue提供了丰富的内置指令,用于处理常见的DOM操作和逻辑控制。 生态系统:Vue拥有庞大的生态系统,包括插件、工具和第三方库,可以满足各种开发需求。

2. Vue中的双向数据绑定是如何实现的?

答案:Vue中的双向数据绑定是通过v-model指令实现的。v-model可以在表单元素(如、、)上创建双向数据绑定。当用户输入改变表单元素的值时,数据模型会自动更新;反之,当数据模型的值改变时,表单元素也会自动更新。

3. Vue中的生命周期钩子有哪些?它们的执行顺序是怎样的?

答案:Vue中的生命周期钩子包括beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy和destroyed。它们的执行顺序如下:

beforeCreate created beforeMount mounted beforeUpdate updated beforeDestroy destroyed

4. Vue中的计算属性和监听器有什么区别?

答案:计算属性是基于依赖的属性,它根据其依赖的数据动态计算得出值。计算属性具有缓存机制,只有在依赖的数据发生变化时才会重新计算。监听器是用于监听数据的变化并执行相应的操作。当数据发生变化时,监听器会立即执行指定的回调函数。

5. Vue中的组件通信有哪些方式?

答案:Vue中的组件通信方式包括:

父子组件通信:通过props向子组件传递数据,子组件通过事件向父组件发送消息。 子父组件通信:子组件通过$emit触发事件,父组件通过监听事件并响应。 兄弟组件通信:通过共享的父组件来传递数据或通过事件总线(Event Bus)进行通信。 跨级组件通信:通过provide和inject来在祖先组件中提供数据,然后在后代组件中使用。

6. Vue中的路由是如何实现的?

答案:Vue中的路由是通过Vue Router实现的。Vue Router是Vue.js官方提供的路由管理器,它允许开发者在Vue应用中实现单页面应用(SPA)。Vue Router通过配置路由映射关系,将URL路径与组件进行关联,并提供导航功能,使用户可以在不刷新页面的情况下切换视图。

7. Vue中的指令有哪些?举例说明它们的用法。

答案:Vue中常用的指令包括:

v-if:根据表达式的值条件性地渲染元素。 v-for:根据数组或对象的数据进行循环渲染。 v-bind:用于动态绑定属性或响应式地更新属性。 v-on:用于监听DOM事件并执行相应的方法。 v-model:用于在表单元素上实现双向数据绑定。 例如:

<div v-if="show">显示内容</div>
<ul>
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
<img v-bind:src="imageUrl">
<button v-on:click="handleClick">点击按钮</button>
<input v-model="message">

8. Vue中的watch和computed有什么区别?

答案:watch和computed都可以用于监听数据的变化,但它们的用法和实现方式略有不同。watch用于监听指定的数据变化,并在数据变化时执行相应的操作。computed用于根据依赖的数据动态计算得出一个新的值,并将该值缓存起来,只有在依赖的数据发生变化时才会重新计算。

9. Vue中的mixin是什么?它有什么作用?

答案:Mixin是一种用于在多个组件之间共享代码的方式。Mixin可以包含组件选项(如数据、方法、生命周期钩子等),并将其合并到使用Mixin的组件中。这样可以实现代码的复用和组件的扩展,减少重复编写相似代码的工作。

10. Vue中的keep-alive是什么?它有什么作用?

答案:是Vue中的一个内置组件,用于缓存动态组件。当组件包裹在中时,组件的状态将被保留,包括它的实例、状态和DOM结构。这样可以避免在组件切换时重复创建和销毁组件,提高性能和用户体验。

11. 请解释Vue.js中的依赖注入(Dependency Injection)是什么?它在Vue中的应用场景是什么?

答案:依赖注入是一种设计模式,用于将依赖关系从一个组件传递到另一个组件。在Vue中,依赖注入通过provide和inject选项实现。父组件通过provide提供数据,然后子组件通过inject注入这些数据。它在跨多个层级的组件通信中非常有用。

12. Vue.js中的渲染函数(Render Function)是什么?它与模板(Template)有什么区别?

答案:渲染函数是一种用JavaScript代码编写组件的方式,它可以动态地生成虚拟DOM。与模板相比,渲染函数提供了更大的灵活性和控制力,可以处理更复杂的逻辑和动态渲染需求。

13. Vue.js中的插槽(Slot)是什么?请提供一个具有命名插槽和作用域插槽的示例。

答案:插槽是一种用于在组件中扩展内容的机制。命名插槽允许父组件向子组件插入具有特定名称的内容,而作用域插槽允许子组件将数据传递给父组件。示例:

<!-- 父组件 -->
<template>
<div>
<slot name="header"></slot>
<slot :data="data"></slot>
</div>
</template>
<!-- 子组件 -->
<template>
<div>
<slot name="header">默认标题</slot>
<slot :data="computedData">{{ computedData }}</slot>
</div>
</template>

14. Vue.js中的动画系统是如何工作的?请提供一个简单的动画示例。

答案:Vue.js的动画系统通过CSS过渡和动画类实现。通过在元素上添加过渡类或动画类,可以触发相应的过渡效果或动画效果。示例:

<transition name="fade">
<div v-if="show">显示内容</div>
</transition>
<!-- CSS样式 -->
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
</style>

15. Vue.js中的错误处理机制是什么?如何捕获和处理Vue组件中的错误?

答案:Vue.js提供了全局的错误处理机制和组件级别的错误处理机制。全局错误处理可以通过errorCaptured钩子函数捕获和处理错误。组件级别的错误处理可以通过errorCaptured钩子函数或errorHandler选项捕获和处理错误。

16. Vue.js中的服务端渲染(SSR)是什么?它有哪些优势和限制?

答案:服务端渲染是指在服务器上生成HTML内容并将其发送到浏览器进行渲染的过程。Vue.js可以进行服务端渲染,提供更好的首次加载性能和SEO优化。然而,服务端渲染也带来了一些限制,如增加了服务器负载和开发复杂性。

17. Vue.js中的响应式数组有哪些限制?如何解决这些限制?

答案:Vue.js的响应式系统对于数组的变异方法(如push、pop、splice等)是无法追踪的。为了解决这个限制,Vue提供了一些特殊的方法,如Vue.set、vm.$set和Array.prototype.splice。这些方法可以用于更新数组并保持响应式。

18. Vue.js中的性能优化有哪些常见的技巧?

答案:常见的Vue.js性能优化技巧包括:

使用v-if和v-for时注意避免不必要的渲染。 合理使用computed属性和watch监听器。 使用keep-alive组件缓存组件状态。 使用异步组件进行按需加载。 避免在模板中使用复杂的表达式。 使用key属性管理组件和元素的复用。 合理使用懒加载和分割代码。

19. Vue.js中的路由导航守卫有哪些?它们的执行顺序是怎样的?

答案:Vue.js中的路由导航守卫包括全局前置守卫、全局解析守卫、全局后置守卫、路由独享守卫和组件内守卫。它们的执行顺序如下:

全局前置守卫(beforeEach) 路由独享守卫(beforeEnter) 解析守卫(beforeResolve) 组件内守卫(beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave) 全局后置守卫(afterEach)

20. Vue.js中的单元测试是如何进行的?请提供一个简单的单元测试示例。

答案:Vue.js的单元测试可以使用工具如Jest或Mocha进行。示例:

// 组件代码
// MyComponent.vue
<template>
<div class="my-component">
<span>{{ message }}</span>
<button @click="increment">增加</button>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello',
count: 0
}
},
methods: {
increment() {
this.count++
}
}
}
</script>
// 单元测试代码
// MyComponent.spec.js
import { shallowMount } from '@vue/test-utils'
import MyComponent from './MyComponent.vue'
describe('MyComponent', () => {
it('renders message correctly', () => {
const wrapper = shallowMount(MyComponent)
expect(wrapper.find('span').text()).toBe('Hello')
})
it('increments count when button is clicked', () => {
const wrapper = shallowMount(MyComponent)
wrapper.find('button').trigger('click')
expect(wrapper.vm.count).toBe(1)
})
})

VUE3

1. Vue.js 3中的Composition API是什么?它与Options API有什么区别?

答案:Composition API是Vue.js 3中引入的一种新的组织组件逻辑的方式。它允许开发者通过函数的方式组织和重用逻辑,而不是通过选项对象。相比之下,Options API是Vue.js 2中常用的组织组件逻辑的方式,通过选项对象中的属性来定义组件的数据、方法等。

2. Vue.js 3中的Teleport是什么?请给出一个Teleport的示例。

答案:Teleport是Vue.js 3中引入的一种机制,用于将组件的内容渲染到DOM树中的任意位置。示例:

<template>
<div>
<button @click="showModal = true">打开模态框</button>
<teleport to="body">
<modal v-if="showModal" @close="showModal = false">模态框内容</modal>
</teleport>
</div>
</template>

3. Vue.js 3中的响应式系统是如何工作的?它与Vue.js 2中的响应式系统有什么区别?

答案:Vue.js 3中的响应式系统使用了Proxy对象来实现。与Vue.js 2中的响应式系统相比,Vue.js 3的响应式系统具有更好的性能和更细粒度的追踪,能够更准确地检测到数据的变化,并且支持嵌套的响应式数据。

4. Vue.js 3中的Suspense是什么?它的作用是什么?

答案:Suspense是Vue.js 3中引入的一种机制,用于处理异步组件的加载状态。它可以在异步组件加载完成之前显示一个占位符,并在加载完成后渲染异步组件的内容。这样可以更好地处理异步组件的加载过程,提供更好的用户体验。

5. Vue.js 3中的provide和inject有什么作用?请给出一个provide和inject的示例。

答案:provide和inject用于实现组件之间的依赖注入。通过在父组件中使用provide提供数据,然后在子组件中使用inject注入这些数据。示例:

// 父组件
const Parent = {
provide: {
message: 'Hello'
},
// ...
}
// 子组件
const Child = {
inject: ['message'],
created() {
console.log(this.message); // 输出:Hello
},
// ...
}

6. Vue.js 3中的动画系统有哪些改进?请列举几个改进之处。

答案:Vue.js 3中的动画系统相比Vue.js 2有以下改进之处:

更好的性能:Vue.js 3的动画系统使用了更高效的动画引擎,提供了更好的性能。 更简洁的语法:Vue.js 3的动画系统使用了更简洁的语法,使得动画的定义和使用更加直观和方便。 支持更多的动画特性:Vue.js 3的动画系统支持更多的动画特性,如交互式动画和更复杂的动画效果。 Vue.js 3中的静态提升(Static Tree Hoisting)是什么?它有什么优势? 答案:静态提升是Vue.js 3中的一项优化技术,通过在编译阶段将静态节点提升为常量,从而减少了运行时的开销。这项优化技术可以提高组件的渲染性能,并减少生成的代码体积。

7. Vue.js 3中的Fragment是什么?它的作用是什么?

答案:Fragment是Vue.js 3中引入的一种机制,用于在组件中返回多个根节点。在Vue.js 2中,组件的模板只能有一个 Vue.js 3中的Composition API中的ref和reactive有什么区别?什么时候使用哪个? 答案:ref用于创建一个响应式的基本数据类型,而reactive用于创建一个响应式的对象。当需要创建一个简单的响应式数据时,可以使用ref,当需要创建一个包含多个属性的响应式对象时,可以使用reactive。

8. Vue.js 3中的watchEffect和watch有什么区别?什么时候使用哪个?

答案:watchEffect用于监听响应式数据的变化,并在回调函数中执行相应的操作。它会自动追踪依赖,并在依赖变化时重新运行回调函数。watch用于监听指定的响应式数据,并在其变化时执行相应的操作。它可以精确地指定要监听的数据,并提供更多的配置选项。一般来说,如果只需要监听一个响应式数据的变化并执行相应操作,可以使用watchEffect;如果需要更细粒度的控制,可以使用watch。

9. Vue.js 3中的v-model指令在使用时有哪些注意事项?

答案:在使用v-model指令时,有以下注意事项:

v-model指令必须与一个表单元素一起使用,如、、等。 当使用自定义组件时,组件内部必须实现modelValue属性和update:modelValue事件,以支持v-model的双向绑定。 可以使用.lazy修饰符实现在输入框失去焦点时更新数据。 可以使用.trim修饰符自动去除输入框内容的首尾空格。 可以使用.number修饰符将输入框的值转换为数字类型。

10. Vue.js 3中的provide和inject是否支持响应式数据?

答案:默认情况下,provide和inject不支持响应式数据。如果需要在provide中提供一个响应式数据,可以使用ref或reactive将数据包装起来。然后在inject中使用toRefs或toRef将数据解构出来,以获取响应式的引用。

11. Vue.js 3中的nextTick方法有什么作用?在什么情况下使用它?

答案:nextTick方法用于在下次DOM更新循环结束之后执行回调函数。它可以用来确保在更新DOM后执行某些操作,如操作更新后的DOM元素或获取更新后的计算属性的值。通常在需要等待DOM更新完成后进行操作的情况下使用nextTick。

12. Vue.js 3中的和组件有什么区别?

答案:组件用于将组件的内容渲染到DOM树中的任意位置,而组件用于在组件进入或离开DOM树时应用过渡效果。主要用于组件的位置移动,而主要用于组件的显示和隐藏过渡。

13. Vue.js 3中的v-for指令中的key属性有什么作用?为什么要使用它?

答案:v-for指令中的key属性用于给每个迭代项设置一个唯一的标识符。它的作用是帮助Vue.js跟踪每个节点的身份,以便在数据发生变化时高效地更新DOM。使用key属性可以避免出现错误的节点更新或重新排序的问题。

React

1. 什么是React?它的核心概念是什么?

答案:React是一个用于构建用户界面的JavaScript库。它的核心概念是组件化和声明式编程。React将用户界面拆分为独立的可重用组件,并使用声明式语法描述组件的状态和UI的关系,使得构建复杂的UI变得简单和可维护。

2. 什么是JSX?它与HTML有什么区别?

答案:JSX是一种JavaScript的语法扩展,用于在React中描述UI的结构。它类似于HTML,但有一些区别:

3. 什么是React组件?它们有哪两种类型?

答案:React组件是构建用户界面的独立单元。React组件有两种类型:

函数组件:使用函数来定义组件,接收props作为参数,并返回一个React元素。 类组件:使用ES6类来定义组件,继承自React.Component类,通过render方法返回一个React元素。

4. 什么是状态(state)和属性(props)?它们之间有什么区别?

答案:状态(state)是组件自身管理的数据,可以通过setState方法来更新。属性(props)是从父组件传递给子组件的数据,子组件无法直接修改props,只能通过父组件的更新来改变props。

区别:

状态(state)是组件内部的数据,可以在组件中自由修改和管理。 属性(props)是从父组件传递给子组件的数据,子组件无法直接修改,只能接收和使用。

5. 什么是React生命周期方法?列举一些常用的生命周期方法。

答案:React生命周期方法是在组件不同阶段执行的特定方法。以下是一些常用的React生命周期方法:

componentDidMount:组件挂载后立即调用。 componentDidUpdate:组件更新后调用。 componentWillUnmount:组件卸载前调用。 shouldComponentUpdate:决定组件是否需要重新渲染。 getDerivedStateFromProps:根据props的变化来更新状态。

6. 什么是React Hooks?它们的作用是什么?

答案:React Hooks是React 16.8版本引入的一种特性,用于在函数组件中使用状态和其他React特性。Hooks提供了一种无需编写类组件的方式来管理状态和处理副作用,使得函数组件具有类组件的能力。

7. 什么是React Router?它的作用是什么?

答案:React Router是React中用于处理路由的库。它提供了一种在单页面应用中实现导航和路由功能的方式。React Router可以帮助开发者实现页面之间的切换、URL参数的传递、嵌套路由等功能。

8. 什么是React Context?它的作用是什么?

答案:React Context是一种用于在组件树中共享数据的机制。它可以避免通过props一层层传递数据,使得跨组件的数据共享变得更加简单和高效。React Context提供了一个Provider和Consumer组件,用于提供和消费共享的数据。

9. 什么是React的协调(Reconciliation)过程?它是如何工作的?

答案:React的协调过程是指React在进行组件更新时,通过比较新旧虚拟DOM树的差异,仅对需要更新的部分进行实际的DOM操作。协调过程的工作方式如下:

React会逐层比较新旧虚拟DOM树的节点,并找出差异。 对于每个差异,React会生成相应的DOM操作指令,如插入、更新或删除节点。 React会将所有的DOM操作指令批量执行,以减少对真实DOM的操作次数。

10. 什么是React的事件合成(SyntheticEvent)?它的作用是什么?

答案:React的事件合成是一种在React中处理事件的机制。它是React为了提高性能和跨浏览器兼容性而实现的一种事件系统。事件合成的作用包括:

提供了一种统一的方式来处理事件,无需考虑浏览器兼容性。 可以通过事件委托的方式将事件处理程序绑定到父组件,提高性能。 可以访问原生事件对象的属性和方法。

11. 什么是React的Fiber架构?它解决了什么问题?

答案:React的Fiber架构是React 16版本引入的一种新的协调算法和架构。它旨在解决长时间渲染阻塞主线程的问题,提高应用的性能和用户体验。Fiber架构通过将渲染过程分解为多个小任务,并使用优先级调度算法来动态分配时间片,使得React可以在每个帧中执行一部分任务,从而实现平滑的用户界面和更好的响应性能。

12. 什么是React的错误边界(Error Boundary)?它的作用是什么?

答案:React的错误边界是一种用于处理组件错误的机制。它允许组件捕获并处理其子组件中发生的JavaScript错误,以避免整个应用崩溃。错误边界的作用包括:

捕获并处理组件树中的错误,防止错误导致整个应用崩溃。 提供一种优雅的方式来显示错误信息或备用UI。 可以用于记录错误和发送错误报告。

网络

1. 什么是HTTP?它是如何工作的?

答案:HTTP(Hypertext Transfer Protocol)是一种用于在Web上传输数据的协议。它使用客户端-服务器模型,客户端发送HTTP请求到服务器,服务器返回HTTP响应。HTTP的工作流程如下:

客户端发送HTTP请求到指定的URL。 服务器接收请求并处理,然后返回HTTP响应。 客户端接收响应并解析,从中获取所需的数据。

2. 什么是HTTPS?与HTTP有什么区别?

答案:HTTPS(Hypertext Transfer Protocol Secure)是HTTP的安全版本,通过使用SSL(Secure Sockets Layer)或TLS(Transport Layer Security)协议对通信进行加密和身份验证。与HTTP相比,HTTPS具有以下区别:

数据在传输过程中通过加密进行保护,提供更高的安全性。 使用数字证书对服务器进行身份验证,防止中间人攻击。 使用默认端口443。

3. 什么是跨域请求?它是如何解决的?

答案:跨域请求是指在浏览器中向不同域名、端口或协议发送的请求。由于浏览器的同源策略(Same-Origin Policy)限制,跨域请求会受到限制。为了解决跨域问题,可以使用以下方法:

JSONP(JSON with Padding):通过动态创建

4. 什么是缓存?在前端中如何使用缓存来提高性能?

答案:缓存是将数据或资源存储在临时存储中,以便在后续请求中重复使用,从而提高性能和减少网络流量。在前端中,可以使用以下方式来利用缓存:

HTTP缓存:通过设置适当的缓存头(如Cache-Control和Expires)来指示浏览器缓存响应。 资源缓存:使用文件指纹或版本号来重命名静态资源文件,以便在文件内容变化时使浏览器重新下载。 数据缓存:使用内存缓存、浏览器本地存储(如localStorage)或服务端缓存(如Redis)来存储数据,避免重复请求。

5. 什么是CDN?它的作用是什么?

答案:CDN(Content Delivery Network)是一种分布式网络架构,用于在全球各地提供高性能、低延迟的内容传输服务。CDN的作用包括:

将静态资源(如图片、样式表、脚本等)缓存到离用户更近的服务器上,提供更快的加载速度。 分发网络流量,减轻源服务器的负载压力。 提供内容压缩、数据压缩和缓存等优化技术,提高用户体验。

6. 什么是网页加载性能优化?可以采取哪些措施来改善网页加载性能?

答案:网页加载性能优化是指通过各种技术手段来减少网页加载时间并提高用户体验。可以采取以下措施来改善网页加载性能:

压缩和合并资源文件(如CSS和JavaScript),减少文件大小和请求数量。 使用图像压缩和适当的格式选择来减小图像文件大小。 使用浏览器缓存和HTTP缓存头来缓存静态资源。 使用懒加载延迟加载非关键资源,提高初始加载速度。 使用CDN(内容分发网络)来分发静态资源,减少网络延迟。 优化关键渲染路径,尽早呈现页面内容。

7. 什么是网页性能监测和分析?可以使用哪些工具来监测和分析网页性能?

答案:网页性能监测和分析是指通过测量和收集有关网页加载和交互性能的数据,以便识别性能瓶颈并进行优化。可以使用以下工具来监测和分析网页性能:

Web性能API:浏览器提供的JavaScript API,可通过performance对象来收集性能数据。 Lighthouse:一种开源工具,可提供关于网页性能、可访问性和最佳实践的综合报告。 WebPagetest:在线工具,可测量网页加载时间并提供详细的性能分析报告。 Chrome开发者工具:浏览器内置的开发者工具,提供了性能分析、网络监控和页面审查等功能。

8. 什么是渐进式图像加载(Progressive Image Loading)?它如何改善网页加载性能?

答案:渐进式图像加载是一种技术,通过逐步加载图像的模糊或低分辨率版本,然后逐渐提高图像的清晰度,以改善网页加载性能和用户体验。渐进式图像加载的好处包括:

用户可以更快地看到页面内容,提高感知速度。 逐步加载图像可以减少网页整体的加载时间。 渐进式图像加载可以提供平滑的过渡效果,避免页面内容突然闪烁或变化。

9. 什么是前端资源优先级(Resource Prioritization)?如何设置资源的优先级?

答案:前端资源优先级是指为不同类型的资源分配加载优先级,以优化网页加载性能。可以使用以下方法设置资源的优先级:

使用标签来指定资源的预加载,以确保关键资源尽早加载。 使用标签来指定可能在未来页面中使用的资源,以提前加载。 使用标签来指定要预解析的域名,以减少DNS查找时间。 使用标签来指定要预连接的域名,以减少建立连接的时间。

浏览器

1.解释一下浏览器的工作原理。

答案:浏览器的工作原理包括以下几个关键步骤:

解析:浏览器将接收到的HTML、CSS和JavaScript代码解析成DOM树、CSSOM树和JavaScript引擎可执行的代码。 渲染:浏览器使用DOM树和CSSOM树构建渲染树,然后根据渲染树进行布局(计算元素的位置和大小)和绘制(将元素绘制到屏幕上)。 布局和绘制:浏览器根据渲染树的变化进行布局和绘制,然后将最终的页面呈现给用户。 JavaScript引擎执行:浏览器的JavaScript引擎解释和执行JavaScript代码,并根据需要更新渲染树和重新渲染页面。

2. 什么是重绘(Repaint)和重排(Reflow)?它们之间有什么区别?

答案:重绘是指当元素的外观(如颜色、背景等)发生改变,但布局不受影响时的更新过程。重绘不会导致元素的位置或大小发生变化。

重排是指当元素的布局属性(如宽度、高度、位置等)发生改变时的更新过程。重排会导致浏览器重新计算渲染树和重新绘制页面的一部分或全部。

区别在于重绘只涉及外观的更改,而重排涉及布局的更改。重排比重绘更消耗性能,因为它需要重新计算布局和绘制整个页面。

3. 什么是事件冒泡和事件捕获?它们之间有什么区别?

答案:事件冒泡和事件捕获是指浏览器处理事件时的两种不同的传播方式。

事件冒泡是指事件从最内层的元素开始触发,然后逐级向上传播到父元素,直到传播到最外层的元素。

事件捕获是指事件从最外层的元素开始触发,然后逐级向下传播到最内层的元素。

区别在于传播方向的不同。事件冒泡是从内向外传播,而事件捕获是从外向内传播。

4. 解释一下同步和异步的JavaScript代码执行方式。

答案:同步代码是按照顺序执行的代码,每个任务必须等待前一个任务完成后才能执行。同步代码会阻塞后续代码的执行,直到当前任务完成。

异步代码是不按照顺序执行的代码,它会在后台执行,不会阻塞后续代码的执行。异步代码通常使用回调函数、Promise、async/await等方式来处理异步操作的结果。

通过异步执行,可以避免阻塞主线程,提高页面的响应性能。

5. 什么是事件循环(Event Loop)?它在JavaScript中的作用是什么?

答案:事件循环是JavaScript中处理异步代码执行的机制。它负责管理调度和执行异步任务,并将它们添加到执行队列中。

在JavaScript中,事件循环的作用是确保异步任务按照正确的顺序执行,并且不会阻塞主线程。它通过不断地从执行队列中取出任务并执行,以实现非阻塞的异步操作。

6. 解释一下浏览器的垃圾回收机制是如何工作的。

答案:浏览器的垃圾回收机制是一种自动管理内存的机制,用于检测和回收不再使用的对象,以释放内存资源。

垃圾回收机制通过标记-清除算法实现。它的工作原理如下:

标记阶段:垃圾回收器会从根对象(如全局对象)开始,递归遍历所有对象,并标记仍然可访问的对象。 清除阶段:垃圾回收器会扫描堆内存,清除未被标记的对象,并回收它们所占用的内存空间。 垃圾回收机制的目标是识别和回收不再使用的对象,以避免内存泄漏和提高内存利用率。

7. 解释一下浏览器的同源策略(Same-Origin Policy)及其限制。

答案:同源策略是浏览器的一项安全机制,用于限制来自不同源的网页之间的交互。同源是指协议、域名和端口号完全相同。

同源策略的限制包括:

脚本访问限制:不同源的脚本无法直接访问彼此的数据和操作。 DOM访问限制:不同源的网页无法通过JavaScript访问彼此的DOM元素。 Cookie限制:不同源的网页无法读取或修改彼此的Cookie。 AJAX请求限制:不同源的网页无法通过AJAX请求访问彼此的数据。 同源策略的存在可以防止恶意网站获取用户的敏感信息或进行恶意操作。

8. 什么是Web Workers?它们在浏览器中的作用是什么?

答案:Web Workers是一种浏览器提供的JavaScript API,用于在后台线程中执行耗时的计算任务,以避免阻塞主线程。

Web Workers的作用是提高浏览器的响应性能,使得在执行复杂计算或处理大量数据时,不会影响用户界面的流畅性。

Web Workers通过将任务委托给后台线程来实现并行处理,从而充分利用多核处理器的能力。它们可以与主线程进行通信,但不能直接访问DOM或执行UI相关的操作。

9. 解释一下浏览器缓存(Browser Cache)是什么,以及它的作用是什么?

答案:浏览器缓存是浏览器在本地存储Web页面和资源的副本,以便在后续访问时可以快速加载。它的作用是减少对服务器的请求次数和网络传输量,提高页面加载速度和用户体验。

浏览器缓存通过在首次请求时将资源保存到本地,并在后续请求时检查资源是否已经存在并且没有过期来工作。如果资源已经存在且未过期,浏览器会直接从缓存中加载资源,而不是从服务器重新下载。

10. 什么是重定向(Redirect)?它在浏览器中的作用是什么?

答案:重定向是指当浏览器请求一个URL时,服务器返回一个不同的URL,从而将浏览器的请求重定向到新的URL上。

重定向在浏览器中的作用是实现页面的跳转、URL的修改或资源的重定向。它可以用于多种情况,例如处理旧链接的跳转、实现URL的规范化、处理用户认证等。

重定向通过在HTTP响应中设置特定的状态码(如301永久重定向、302临时重定向)和Location头部字段来实现。

11. 什么是浏览器存储(Browser Storage)?它有哪些不同的存储机制?

答案:浏览器存储是浏览器提供的一种在客户端存储数据的机制,用于在不同的网页间共享数据或持久保存数据。

浏览器存储有以下不同的存储机制:

Cookie:小型文本文件,可以存储少量数据,并在每次HTTP请求中自动发送到服务器。 Web Storage(localStorage和sessionStorage):可以存储较大量的数据,以键值对的形式存储在浏览器中。 IndexedDB:一种高级的客户端数据库,可以存储大量结构化数据,并支持索引和事务操作。 Cache API:用于缓存网络请求的响应,以便离线访问或提高页面加载速度。 不同的存储机制适用于不同的需求,开发者可以根据具体情况选择合适的存储方式。


原文链接:https://juejin.cn/post/7276407803618656295

2023金九银十必看前端面试题! 金九银十黄金期来了 想要跳槽的小伙伴快来看啊

CSS

1. 请解释CSS的盒模型是什么,并描述其组成部分。

答案:CSS的盒模型是用于布局和定位元素的概念。它由内容区域、内边距、边框和外边距组成,这些部分依次包裹在元素周围。

2. 解释CSS中的选择器及其优先级。

答案:CSS选择器用于选择要应用样式的HTML元素。选择器的优先级规则是:内联样式 > ID选择器 > 类选择器、属性选择器、伪类选择器 > 元素选择器 > 通用选择器。同时,使用!important可以提升样式的优先级。

3. 解释CSS中的浮动(float)是如何工作的,并提供一个示例。

答案:浮动(float)是CSS中用于实现元素的左浮动或右浮动,使其脱离文档流并环绕在其周围的元素。例如:

.float-example {
  float: left;
  width: 200px;
  height: 200px;
}

4. 解释CSS中的定位(position)属性及其不同的取值。

答案:定位(position)属性用于控制元素的定位方式。常见的取值有:static(默认,按照文档流定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和sticky(粘性定位)。

5. 解释CSS中的层叠顺序(z-index)是如何工作的。

答案:层叠顺序(z-index)用于控制元素在垂直方向上的堆叠顺序。具有较高层叠顺序值的元素将显示在较低层叠顺序值的元素之上。默认情况下,层叠顺序值为auto。

6. 解释CSS中的伪类和伪元素的区别,并给出一个示例。

答案:伪类用于向选择器添加特殊的状态,如:hover、:active等。伪元素用于向选择器添加特殊的元素,如::before、::after等。例如:

/* 伪类示例 */
a:hover {
  color: red;
}

/* 伪元素示例 */
p::before {
  content: "前缀";
}

7. 解释CSS中的盒子模型的两种模式:标准模式和怪异模式。

答案:标准模式是按照W3C标准解析渲染页面的模式。怪异模式是兼容旧版本浏览器的解析渲染页面的模式。可以通过声明来指定使用哪种模式。

8. 解释CSS中的BFC是什么,它的作用是什么?

答案:BFC(块级格式化上下文)是CSS中的一种渲染模式,它创建了一个独立的渲染环境,其中的元素按照一定的规则进行布局和定位。BFC的作用包括:清除浮动、防止外边距重叠等。

9. 解释CSS中的flexbox布局是什么,它的优势是什么?

答案:flexbox布局是一种用于创建灵活的、响应式的布局的CSS模块。它通过flex容器和flex项目的组合来实现强大的布局能力。其优势包括简单易用、自适应性强、对齐和分布控制灵活等。

10.解释CSS中的媒体查询是什么,它的作用是什么?

答案:媒体查询是CSS中的一种技术,用于根据设备的特性和属性来应用不同的样式。通过媒体查询,可以根据屏幕尺寸、设备类型、分辨率等条件来优化页面的布局和样式。

JavaScript

1. 解释JavaScript的数据类型,并举例说明每种类型。

答案:JavaScript有七种数据类型:字符串(String)、数字(Number)、布尔值(Boolean)、对象(Object)、数组(Array)、空值(Null)和未定义(Undefined)。例如:

let str = "Hello";
let num = 10;
let bool = true;
let obj = { name: "John" };
let arr = [1, 2, 3];
let n = null;
let undef;

2. 解释JavaScript中的变量提升(Hoisting)是什么。

答案:变量提升是指在JavaScript中,变量和函数声明会在代码执行之前被提升到作用域的顶部。这意味着可以在声明之前使用变量和函数。例如:

console.log(x); // 输出 undefined
var x = 5;

3. 解释JavaScript中的闭包(Closure)是什么,并举例说明。

答案:闭包是指函数可以访问并操作其词法作用域之外的变量。它通过在函数内部创建一个内部函数,并返回该内部函数来实现。例如:

function outer() {
  let x = 10;
  function inner() {
    console.log(x);
  }
  return inner;
}

let closure = outer();
closure(); // 输出 10

4. 解释JavaScript中的事件冒泡(Event Bubbling)和事件捕获(Event Capturing)。

答案:事件冒泡是指事件从最具体的元素开始向父元素逐级触发,直到触发到根元素。事件捕获是指事件从根元素开始,逐级向最具体的元素触发。可以使用addEventListener方法的第三个参数来控制是使用事件冒泡还是事件捕获。

5. 解释JavaScript中的原型继承(Prototype Inheritance)是什么。

答案:原型继承是JavaScript中实现对象之间继承关系的一种机制。每个对象都有一个原型对象,它包含了共享的属性和方法。当访问对象的属性或方法时,如果对象本身没有,则会沿着原型链向上查找。可以使用Object.create()方法或设置对象的__proto__属性来实现原型继承。

6. 解释JavaScript中的异步编程,并提供一个异步操作的示例。

答案:异步编程是指在代码执行过程中,不会阻塞后续代码执行的一种编程方式。常见的异步操作包括网络请求、定时器等。例如:

console.log("开始");
setTimeout(function() {
  console.log("异步操作");
}, 1000);
console.log("结束");

7. 解释JavaScript中的闭包(Closure)是什么,并举例说明。

答案:闭包是指函数可以访问并操作其词法作用域之外的变量。它通过在函数内部创建一个内部函数,并返回该内部函数来实现。例如:

function outer() {
  let x = 10;
  function inner() {
    console.log(x);
  }
  return inner;
}

let closure = outer();
closure(); // 输出 10

8. 解释JavaScript中的this关键字的作用和使用场景。

答案:this关键字在JavaScript中表示当前执行上下文的对象。它的具体取值根据函数的调用方式而定。在全局作用域中,this指向全局对象(浏览器环境中为window对象)。在函数中,this的指向取决于函数的调用方式,可以通过call、apply、bind等方法来显式地指定this的值。

9. 解释JavaScript中的事件委托(Event Delegation)是什么,并提供一个使用事件委托的示例。

答案:事件委托是指将事件处理程序绑定到父元素上,而不是直接绑定到每个子元素上。当事件触发时,事件会冒泡到父元素,然后通过判断事件的目标来执行相应的处理逻辑。这样可以减少事件处理程序的数量,提高性能。例如:

<ul id="list">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>

<script>
document.getElementById("list").addEventListener("click", function(event) {
  if (event.target.tagName === "LI") {
    console.log(event.target.textContent);
  }
});
</script>

10. 解释JavaScript中的模块化编程,并提供一个使用模块的示例。

答案:模块化编程是指将代码划分为独立的模块,每个模块负责特定的功能,并通过导入和导出来实现模块之间的依赖关系。ES6引入了模块化的语法,可以使用import和export关键字来导入和导出模块。例如:

// module.js
export function sayHello() {
  console.log("Hello!");
}

// main.js
import { sayHello } from "./module.js";
sayHello(); // 输出 "Hello!"

11. 解释JavaScript中的事件冒泡(Event Bubbling)和事件捕获(Event Capturing)。

答案:事件冒泡是指当一个事件在DOM树中触发时,它会从最内层的元素开始向外传播至最外层的元素。事件捕获是指当一个事件在DOM树中触发时,它会从最外层的元素开始向内传播至最内层的元素。

12. 什么是原型链(Prototype Chain)?如何利用原型链实现继承?

答案:原型链是JavaScript中对象之间的连接关系,每个对象都有一个指向其原型(prototype)的引用。通过原型链,对象可以继承其原型对象的属性和方法。可以使用原型链实现继承,通过将一个对象的原型指向另一个对象,从而使得该对象可以访问另一个对象的属性和方法。

13. 解释JavaScript中的防抖(Debounce)和节流(Throttle)。

答案:防抖和节流都是用于控制函数执行频率的技术。防抖指的是在某个时间段内,只执行最后一次触发的函数调用。节流指的是在某个时间段内,按照固定的时间间隔执行函数调用。

14. 什么是事件循环(Event Loop)?请解释JavaScript中的事件循环机制。

答案:事件循环是JavaScript中处理异步操作的机制。事件循环不断地从任务队列中取出任务并执行,直到任务队列为空。事件循环由主线程和任务队列组成,主线程负责执行同步任务,异步任务会被放入任务队列中,等待主线程空闲时被执行。

15. 解释JavaScript中的深拷贝和浅拷贝。

答案:深拷贝是指创建一个新对象,将原始对象的所有属性和嵌套对象的属性都复制到新对象中。浅拷贝是指创建一个新对象,将原始对象的属性复制到新对象中,但嵌套对象的引用仍然是共享的。

16. 什么是异步编程?请列举几种处理异步操作的方法。

答案:异步编程是一种处理可能耗时的操作而不阻塞主线程的编程方式。常见的处理异步操作的方法有回调函数、Promise、async/await和事件监听等。

17. 解释JavaScript中的Hoisting(变量提升)。

答案:变量提升是指在JavaScript中,变量和函数的声明会被提升到当前作用域的顶部。这意味着可以在声明之前使用变量和函数,但它们的赋值或定义仍然在原来的位置。

18. 什么是柯里化(Currying)?请给出一个柯里化的示例。

答案:柯里化是一种将接受多个参数的函数转换为接受一个参数并返回一个新函数的过程。示例:

function add(a) {
  return function(b) {
    return a + b;
  }
}

var add5 = add(5);
console.log(add5(3)); // 输出:8

19. 解释JavaScript中的严格模式(Strict Mode)。

答案:严格模式是一种JavaScript的执行模式,它提供了更严格的语法和错误检查。在严格模式下,一些不安全或不推荐的语法会被禁用,同时会引入一些新的特性,如变量必须先声明才能使用、禁止使用this指向全局对象等。

TypeScript

1. 解释TypeScript和JavaScript之间的关系。

答案:TypeScript是JavaScript的超集,它添加了静态类型和其他一些特性。TypeScript代码可以编译成JavaScript代码,因此可以在任何支持JavaScript的环境中运行。

2. TypeScript中的类型注解是什么?如何使用类型注解?

答案:类型注解是指在变量、函数参数、函数返回值等地方显式地声明类型信息。可以使用冒号(:)后跟类型来添加类型注解。例如:

let num: number = 10;

function add(a: number, b: number): number {
  return a + b;
}

3. TypeScript中的接口是什么?如何定义和使用接口?

答案:接口是一种用于定义对象的结构和类型的语法。可以使用interface关键字来定义接口。例如:

interface Person {
  name: string;
  age: number;
}

function greet(person: Person) {
  console.log(`Hello, ${person.name}!`);
}

let john: Person = { name: "John", age: 25 };
greet(john); // 输出 "Hello, John!"

4. TypeScript中的类是什么?如何定义和使用类?

答案:类是一种用于创建对象的蓝图,它包含属性和方法。可以使用class关键字来定义类。例如:

class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log(`Hello, ${this.name}!`);
  }
}

let john = new Person("John", 25);
john.greet(); // 输出 "Hello, John!"

5. TypeScript中的泛型是什么?如何使用泛型?

答案:泛型是一种用于创建可重用代码的工具,它允许在定义函数、类或接口时使用占位符类型。可以使用尖括号(<>)来指定泛型类型。例如:

function identity<T>(value: T): T {
  return value;
}

let result = identity<string>("Hello");
console.log(result); // 输出 "Hello"

6. TypeScript中的枚举是什么?如何定义和使用枚举?

答案:枚举是一种用于定义命名常量集合的语法。可以使用enum关键字来定义枚举。例如:

enum Color {
  Red,
  Green,
  Blue,
}

let color: Color = Color.Green;
console.log(color); // 输出 1

7. TypeScript中的模块是什么?如何导出和导入模块?

答案:模块是用于组织和封装代码的单元。可以使用export关键字将模块中的变量、函数、类等导出,以便其他模块可以使用。可以使用import关键字来导入其他模块的导出。例如:

// module.ts
export function greet(name: string) {
  console.log(`Hello, ${name}!`);
}

// main.ts
import { greet } from "./module";
greet("John"); // 输出 "Hello, John!"

8. TypeScript中的类型推断是什么?如何使用类型推断?

答案:类型推断是指TypeScript根据上下文自动推断变量的类型,而无需显式地添加类型注解。例如:

let num = 10; // 推断为 number 类型
let str = "Hello"; // 推断为 string 类型

9. TypeScript中的命名空间是什么?如何定义和使用命名空间?

答案:命名空间是一种用于组织和封装代码的机制,它避免了全局命名冲突。可以使用namespace关键字来定义命名空间。例如:

namespace MyNamespace {
  export function greet(name: string) {
    console.log(`Hello, ${name}!`);
  }
}

MyNamespace.greet("John"); // 输出 "Hello, John!"

10. TypeScript中的类型别名是什么?如何定义和使用类型别名?

答案:类型别名是给类型起一个别名,以便在代码中更方便地引用。可以使用type关键字来定义类型别名。例如:

type Point = { x: number; y: number };

function printPoint(point: Point) {
  console.log(`(${point.x}, ${point.y})`);
}

let p: Point = { x: 1, y: 2 };
printPoint(p); // 输出 "(1, 2)"

VUE2

1. Vue.js是什么?它有哪些特点?

答案:Vue.js是一个用于构建用户界面的JavaScript框架。它具有以下特点:

响应式数据绑定:通过使用Vue的数据绑定语法,可以实现数据的自动更新。 组件化开发:Vue允许将页面划分为独立的组件,提高了代码的可维护性和复用性。 虚拟DOM:Vue使用虚拟DOM来跟踪页面上的变化,并高效地更新实际的DOM。 指令系统:Vue提供了丰富的内置指令,用于处理常见的DOM操作和逻辑控制。 生态系统:Vue拥有庞大的生态系统,包括插件、工具和第三方库,可以满足各种开发需求。

2. Vue中的双向数据绑定是如何实现的?

答案:Vue中的双向数据绑定是通过v-model指令实现的。v-model可以在表单元素(如、、)上创建双向数据绑定。当用户输入改变表单元素的值时,数据模型会自动更新;反之,当数据模型的值改变时,表单元素也会自动更新。

3. Vue中的生命周期钩子有哪些?它们的执行顺序是怎样的?

答案:Vue中的生命周期钩子包括beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy和destroyed。它们的执行顺序如下:

beforeCreate created beforeMount mounted beforeUpdate updated beforeDestroy destroyed

4. Vue中的计算属性和监听器有什么区别?

答案:计算属性是基于依赖的属性,它根据其依赖的数据动态计算得出值。计算属性具有缓存机制,只有在依赖的数据发生变化时才会重新计算。监听器是用于监听数据的变化并执行相应的操作。当数据发生变化时,监听器会立即执行指定的回调函数。

5. Vue中的组件通信有哪些方式?

答案:Vue中的组件通信方式包括:

父子组件通信:通过props向子组件传递数据,子组件通过事件向父组件发送消息。 子父组件通信:子组件通过$emit触发事件,父组件通过监听事件并响应。 兄弟组件通信:通过共享的父组件来传递数据或通过事件总线(Event Bus)进行通信。 跨级组件通信:通过provide和inject来在祖先组件中提供数据,然后在后代组件中使用。

6. Vue中的路由是如何实现的?

答案:Vue中的路由是通过Vue Router实现的。Vue Router是Vue.js官方提供的路由管理器,它允许开发者在Vue应用中实现单页面应用(SPA)。Vue Router通过配置路由映射关系,将URL路径与组件进行关联,并提供导航功能,使用户可以在不刷新页面的情况下切换视图。

7. Vue中的指令有哪些?举例说明它们的用法。

答案:Vue中常用的指令包括:

v-if:根据表达式的值条件性地渲染元素。 v-for:根据数组或对象的数据进行循环渲染。 v-bind:用于动态绑定属性或响应式地更新属性。 v-on:用于监听DOM事件并执行相应的方法。 v-model:用于在表单元素上实现双向数据绑定。 例如:

<div v-if="show">显示内容</div>

<ul>
  <li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>

<img v-bind:src="imageUrl">

<button v-on:click="handleClick">点击按钮</button>

<input v-model="message">

8. Vue中的watch和computed有什么区别?

答案:watch和computed都可以用于监听数据的变化,但它们的用法和实现方式略有不同。watch用于监听指定的数据变化,并在数据变化时执行相应的操作。computed用于根据依赖的数据动态计算得出一个新的值,并将该值缓存起来,只有在依赖的数据发生变化时才会重新计算。

9. Vue中的mixin是什么?它有什么作用?

答案:Mixin是一种用于在多个组件之间共享代码的方式。Mixin可以包含组件选项(如数据、方法、生命周期钩子等),并将其合并到使用Mixin的组件中。这样可以实现代码的复用和组件的扩展,减少重复编写相似代码的工作。

10. Vue中的keep-alive是什么?它有什么作用?

答案:是Vue中的一个内置组件,用于缓存动态组件。当组件包裹在中时,组件的状态将被保留,包括它的实例、状态和DOM结构。这样可以避免在组件切换时重复创建和销毁组件,提高性能和用户体验。

11. 请解释Vue.js中的依赖注入(Dependency Injection)是什么?它在Vue中的应用场景是什么?

答案:依赖注入是一种设计模式,用于将依赖关系从一个组件传递到另一个组件。在Vue中,依赖注入通过provide和inject选项实现。父组件通过provide提供数据,然后子组件通过inject注入这些数据。它在跨多个层级的组件通信中非常有用。

12. Vue.js中的渲染函数(Render Function)是什么?它与模板(Template)有什么区别?

答案:渲染函数是一种用JavaScript代码编写组件的方式,它可以动态地生成虚拟DOM。与模板相比,渲染函数提供了更大的灵活性和控制力,可以处理更复杂的逻辑和动态渲染需求。

13. Vue.js中的插槽(Slot)是什么?请提供一个具有命名插槽和作用域插槽的示例。

答案:插槽是一种用于在组件中扩展内容的机制。命名插槽允许父组件向子组件插入具有特定名称的内容,而作用域插槽允许子组件将数据传递给父组件。示例:

<!-- 父组件 -->
<template>
  <div>
    <slot name="header"></slot>
    <slot :data="data"></slot>
  </div>
</template>

<!-- 子组件 -->
<template>
  <div>
    <slot name="header">默认标题</slot>
    <slot :data="computedData">{{ computedData }}</slot>
  </div>
</template>

14. Vue.js中的动画系统是如何工作的?请提供一个简单的动画示例。

答案:Vue.js的动画系统通过CSS过渡和动画类实现。通过在元素上添加过渡类或动画类,可以触发相应的过渡效果或动画效果。示例:

<transition name="fade">
  <div v-if="show">显示内容</div>
</transition>

<!-- CSS样式 -->
<style>
.fade-enter-active, .fade-leave-active {
  transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to {
  opacity: 0;
}
</style>

15. Vue.js中的错误处理机制是什么?如何捕获和处理Vue组件中的错误?

答案:Vue.js提供了全局的错误处理机制和组件级别的错误处理机制。全局错误处理可以通过errorCaptured钩子函数捕获和处理错误。组件级别的错误处理可以通过errorCaptured钩子函数或errorHandler选项捕获和处理错误。

16. Vue.js中的服务端渲染(SSR)是什么?它有哪些优势和限制?

答案:服务端渲染是指在服务器上生成HTML内容并将其发送到浏览器进行渲染的过程。Vue.js可以进行服务端渲染,提供更好的首次加载性能和SEO优化。然而,服务端渲染也带来了一些限制,如增加了服务器负载和开发复杂性。

17. Vue.js中的响应式数组有哪些限制?如何解决这些限制?

答案:Vue.js的响应式系统对于数组的变异方法(如push、pop、splice等)是无法追踪的。为了解决这个限制,Vue提供了一些特殊的方法,如Vue.set、vm.$set和Array.prototype.splice。这些方法可以用于更新数组并保持响应式。

18. Vue.js中的性能优化有哪些常见的技巧?

答案:常见的Vue.js性能优化技巧包括:

使用v-if和v-for时注意避免不必要的渲染。 合理使用computed属性和watch监听器。 使用keep-alive组件缓存组件状态。 使用异步组件进行按需加载。 避免在模板中使用复杂的表达式。 使用key属性管理组件和元素的复用。 合理使用懒加载和分割代码。

19. Vue.js中的路由导航守卫有哪些?它们的执行顺序是怎样的?

答案:Vue.js中的路由导航守卫包括全局前置守卫、全局解析守卫、全局后置守卫、路由独享守卫和组件内守卫。它们的执行顺序如下:

全局前置守卫(beforeEach) 路由独享守卫(beforeEnter) 解析守卫(beforeResolve) 组件内守卫(beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave) 全局后置守卫(afterEach)

20. Vue.js中的单元测试是如何进行的?请提供一个简单的单元测试示例。

答案:Vue.js的单元测试可以使用工具如Jest或Mocha进行。示例:

// 组件代码
// MyComponent.vue
<template>
  <div class="my-component">
    <span>{{ message }}</span>
    <button @click="increment">增加</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello',
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>

// 单元测试代码
// MyComponent.spec.js
import { shallowMount } from '@vue/test-utils'
import MyComponent from './MyComponent.vue'

describe('MyComponent', () => {
  it('renders message correctly', () => {
    const wrapper = shallowMount(MyComponent)
    expect(wrapper.find('span').text()).toBe('Hello')
  })

  it('increments count when button is clicked', () => {
    const wrapper = shallowMount(MyComponent)
    wrapper.find('button').trigger('click')
    expect(wrapper.vm.count).toBe(1)
  })
})

VUE3

1. Vue.js 3中的Composition API是什么?它与Options API有什么区别?

答案:Composition API是Vue.js 3中引入的一种新的组织组件逻辑的方式。它允许开发者通过函数的方式组织和重用逻辑,而不是通过选项对象。相比之下,Options API是Vue.js 2中常用的组织组件逻辑的方式,通过选项对象中的属性来定义组件的数据、方法等。

2. Vue.js 3中的Teleport是什么?请给出一个Teleport的示例。

答案:Teleport是Vue.js 3中引入的一种机制,用于将组件的内容渲染到DOM树中的任意位置。示例:

<template>
  <div>
    <button @click="showModal = true">打开模态框</button>
    <teleport to="body">
      <modal v-if="showModal" @close="showModal = false">模态框内容</modal>
    </teleport>
  </div>
</template>

3. Vue.js 3中的响应式系统是如何工作的?它与Vue.js 2中的响应式系统有什么区别?

答案:Vue.js 3中的响应式系统使用了Proxy对象来实现。与Vue.js 2中的响应式系统相比,Vue.js 3的响应式系统具有更好的性能和更细粒度的追踪,能够更准确地检测到数据的变化,并且支持嵌套的响应式数据。

4. Vue.js 3中的Suspense是什么?它的作用是什么?

答案:Suspense是Vue.js 3中引入的一种机制,用于处理异步组件的加载状态。它可以在异步组件加载完成之前显示一个占位符,并在加载完成后渲染异步组件的内容。这样可以更好地处理异步组件的加载过程,提供更好的用户体验。

5. Vue.js 3中的provide和inject有什么作用?请给出一个provide和inject的示例。

答案:provide和inject用于实现组件之间的依赖注入。通过在父组件中使用provide提供数据,然后在子组件中使用inject注入这些数据。示例:

// 父组件
const Parent = {
  provide: {
    message: 'Hello'
  },
  // ...
}

// 子组件
const Child = {
  inject: ['message'],
  created() {
    console.log(this.message); // 输出:Hello
  },
  // ...
}

6. Vue.js 3中的动画系统有哪些改进?请列举几个改进之处。

答案:Vue.js 3中的动画系统相比Vue.js 2有以下改进之处:

更好的性能:Vue.js 3的动画系统使用了更高效的动画引擎,提供了更好的性能。 更简洁的语法:Vue.js 3的动画系统使用了更简洁的语法,使得动画的定义和使用更加直观和方便。 支持更多的动画特性:Vue.js 3的动画系统支持更多的动画特性,如交互式动画和更复杂的动画效果。 Vue.js 3中的静态提升(Static Tree Hoisting)是什么?它有什么优势? 答案:静态提升是Vue.js 3中的一项优化技术,通过在编译阶段将静态节点提升为常量,从而减少了运行时的开销。这项优化技术可以提高组件的渲染性能,并减少生成的代码体积。

7. Vue.js 3中的Fragment是什么?它的作用是什么?

答案:Fragment是Vue.js 3中引入的一种机制,用于在组件中返回多个根节点。在Vue.js 2中,组件的模板只能有一个 Vue.js 3中的Composition API中的ref和reactive有什么区别?什么时候使用哪个? 答案:ref用于创建一个响应式的基本数据类型,而reactive用于创建一个响应式的对象。当需要创建一个简单的响应式数据时,可以使用ref,当需要创建一个包含多个属性的响应式对象时,可以使用reactive。

8. Vue.js 3中的watchEffect和watch有什么区别?什么时候使用哪个?

答案:watchEffect用于监听响应式数据的变化,并在回调函数中执行相应的操作。它会自动追踪依赖,并在依赖变化时重新运行回调函数。watch用于监听指定的响应式数据,并在其变化时执行相应的操作。它可以精确地指定要监听的数据,并提供更多的配置选项。一般来说,如果只需要监听一个响应式数据的变化并执行相应操作,可以使用watchEffect;如果需要更细粒度的控制,可以使用watch。

9. Vue.js 3中的v-model指令在使用时有哪些注意事项?

答案:在使用v-model指令时,有以下注意事项:

v-model指令必须与一个表单元素一起使用,如、、等。 当使用自定义组件时,组件内部必须实现modelValue属性和update:modelValue事件,以支持v-model的双向绑定。 可以使用.lazy修饰符实现在输入框失去焦点时更新数据。 可以使用.trim修饰符自动去除输入框内容的首尾空格。 可以使用.number修饰符将输入框的值转换为数字类型。

10. Vue.js 3中的provide和inject是否支持响应式数据?

答案:默认情况下,provide和inject不支持响应式数据。如果需要在provide中提供一个响应式数据,可以使用ref或reactive将数据包装起来。然后在inject中使用toRefs或toRef将数据解构出来,以获取响应式的引用。

11. Vue.js 3中的nextTick方法有什么作用?在什么情况下使用它?

答案:nextTick方法用于在下次DOM更新循环结束之后执行回调函数。它可以用来确保在更新DOM后执行某些操作,如操作更新后的DOM元素或获取更新后的计算属性的值。通常在需要等待DOM更新完成后进行操作的情况下使用nextTick。

12. Vue.js 3中的和组件有什么区别?

答案:组件用于将组件的内容渲染到DOM树中的任意位置,而组件用于在组件进入或离开DOM树时应用过渡效果。主要用于组件的位置移动,而主要用于组件的显示和隐藏过渡。

13. Vue.js 3中的v-for指令中的key属性有什么作用?为什么要使用它?

答案:v-for指令中的key属性用于给每个迭代项设置一个唯一的标识符。它的作用是帮助Vue.js跟踪每个节点的身份,以便在数据发生变化时高效地更新DOM。使用key属性可以避免出现错误的节点更新或重新排序的问题。

React

1. 什么是React?它的核心概念是什么?

答案:React是一个用于构建用户界面的JavaScript库。它的核心概念是组件化和声明式编程。React将用户界面拆分为独立的可重用组件,并使用声明式语法描述组件的状态和UI的关系,使得构建复杂的UI变得简单和可维护。

2. 什么是JSX?它与HTML有什么区别?

答案:JSX是一种JavaScript的语法扩展,用于在React中描述UI的结构。它类似于HTML,但有一些区别:

3. 什么是React组件?它们有哪两种类型?

答案:React组件是构建用户界面的独立单元。React组件有两种类型:

函数组件:使用函数来定义组件,接收props作为参数,并返回一个React元素。 类组件:使用ES6类来定义组件,继承自React.Component类,通过render方法返回一个React元素。

4. 什么是状态(state)和属性(props)?它们之间有什么区别?

答案:状态(state)是组件自身管理的数据,可以通过setState方法来更新。属性(props)是从父组件传递给子组件的数据,子组件无法直接修改props,只能通过父组件的更新来改变props。

区别:

状态(state)是组件内部的数据,可以在组件中自由修改和管理。 属性(props)是从父组件传递给子组件的数据,子组件无法直接修改,只能接收和使用。

5. 什么是React生命周期方法?列举一些常用的生命周期方法。

答案:React生命周期方法是在组件不同阶段执行的特定方法。以下是一些常用的React生命周期方法:

componentDidMount:组件挂载后立即调用。 componentDidUpdate:组件更新后调用。 componentWillUnmount:组件卸载前调用。 shouldComponentUpdate:决定组件是否需要重新渲染。 getDerivedStateFromProps:根据props的变化来更新状态。

6. 什么是React Hooks?它们的作用是什么?

答案:React Hooks是React 16.8版本引入的一种特性,用于在函数组件中使用状态和其他React特性。Hooks提供了一种无需编写类组件的方式来管理状态和处理副作用,使得函数组件具有类组件的能力。

7. 什么是React Router?它的作用是什么?

答案:React Router是React中用于处理路由的库。它提供了一种在单页面应用中实现导航和路由功能的方式。React Router可以帮助开发者实现页面之间的切换、URL参数的传递、嵌套路由等功能。

8. 什么是React Context?它的作用是什么?

答案:React Context是一种用于在组件树中共享数据的机制。它可以避免通过props一层层传递数据,使得跨组件的数据共享变得更加简单和高效。React Context提供了一个Provider和Consumer组件,用于提供和消费共享的数据。

9. 什么是React的协调(Reconciliation)过程?它是如何工作的?

答案:React的协调过程是指React在进行组件更新时,通过比较新旧虚拟DOM树的差异,仅对需要更新的部分进行实际的DOM操作。协调过程的工作方式如下:

React会逐层比较新旧虚拟DOM树的节点,并找出差异。 对于每个差异,React会生成相应的DOM操作指令,如插入、更新或删除节点。 React会将所有的DOM操作指令批量执行,以减少对真实DOM的操作次数。

10. 什么是React的事件合成(SyntheticEvent)?它的作用是什么?

答案:React的事件合成是一种在React中处理事件的机制。它是React为了提高性能和跨浏览器兼容性而实现的一种事件系统。事件合成的作用包括:

提供了一种统一的方式来处理事件,无需考虑浏览器兼容性。 可以通过事件委托的方式将事件处理程序绑定到父组件,提高性能。 可以访问原生事件对象的属性和方法。

11. 什么是React的Fiber架构?它解决了什么问题?

答案:React的Fiber架构是React 16版本引入的一种新的协调算法和架构。它旨在解决长时间渲染阻塞主线程的问题,提高应用的性能和用户体验。Fiber架构通过将渲染过程分解为多个小任务,并使用优先级调度算法来动态分配时间片,使得React可以在每个帧中执行一部分任务,从而实现平滑的用户界面和更好的响应性能。

12. 什么是React的错误边界(Error Boundary)?它的作用是什么?

答案:React的错误边界是一种用于处理组件错误的机制。它允许组件捕获并处理其子组件中发生的JavaScript错误,以避免整个应用崩溃。错误边界的作用包括:

捕获并处理组件树中的错误,防止错误导致整个应用崩溃。 提供一种优雅的方式来显示错误信息或备用UI。 可以用于记录错误和发送错误报告。

网络

1. 什么是HTTP?它是如何工作的?

答案:HTTP(Hypertext Transfer Protocol)是一种用于在Web上传输数据的协议。它使用客户端-服务器模型,客户端发送HTTP请求到服务器,服务器返回HTTP响应。HTTP的工作流程如下:

客户端发送HTTP请求到指定的URL。 服务器接收请求并处理,然后返回HTTP响应。 客户端接收响应并解析,从中获取所需的数据。

2. 什么是HTTPS?与HTTP有什么区别?

答案:HTTPS(Hypertext Transfer Protocol Secure)是HTTP的安全版本,通过使用SSL(Secure Sockets Layer)或TLS(Transport Layer Security)协议对通信进行加密和身份验证。与HTTP相比,HTTPS具有以下区别:

数据在传输过程中通过加密进行保护,提供更高的安全性。 使用数字证书对服务器进行身份验证,防止中间人攻击。 使用默认端口443。

3. 什么是跨域请求?它是如何解决的?

答案:跨域请求是指在浏览器中向不同域名、端口或协议发送的请求。由于浏览器的同源策略(Same-Origin Policy)限制,跨域请求会受到限制。为了解决跨域问题,可以使用以下方法:

JSONP(JSON with Padding):通过动态创建

4. 什么是缓存?在前端中如何使用缓存来提高性能?

答案:缓存是将数据或资源存储在临时存储中,以便在后续请求中重复使用,从而提高性能和减少网络流量。在前端中,可以使用以下方式来利用缓存:

HTTP缓存:通过设置适当的缓存头(如Cache-Control和Expires)来指示浏览器缓存响应。 资源缓存:使用文件指纹或版本号来重命名静态资源文件,以便在文件内容变化时使浏览器重新下载。 数据缓存:使用内存缓存、浏览器本地存储(如localStorage)或服务端缓存(如Redis)来存储数据,避免重复请求。

5. 什么是CDN?它的作用是什么?

答案:CDN(Content Delivery Network)是一种分布式网络架构,用于在全球各地提供高性能、低延迟的内容传输服务。CDN的作用包括:

将静态资源(如图片、样式表、脚本等)缓存到离用户更近的服务器上,提供更快的加载速度。 分发网络流量,减轻源服务器的负载压力。 提供内容压缩、数据压缩和缓存等优化技术,提高用户体验。

6. 什么是网页加载性能优化?可以采取哪些措施来改善网页加载性能?

答案:网页加载性能优化是指通过各种技术手段来减少网页加载时间并提高用户体验。可以采取以下措施来改善网页加载性能:

压缩和合并资源文件(如CSS和JavaScript),减少文件大小和请求数量。 使用图像压缩和适当的格式选择来减小图像文件大小。 使用浏览器缓存和HTTP缓存头来缓存静态资源。 使用懒加载延迟加载非关键资源,提高初始加载速度。 使用CDN(内容分发网络)来分发静态资源,减少网络延迟。 优化关键渲染路径,尽早呈现页面内容。

7. 什么是网页性能监测和分析?可以使用哪些工具来监测和分析网页性能?

答案:网页性能监测和分析是指通过测量和收集有关网页加载和交互性能的数据,以便识别性能瓶颈并进行优化。可以使用以下工具来监测和分析网页性能:

Web性能API:浏览器提供的JavaScript API,可通过performance对象来收集性能数据。 Lighthouse:一种开源工具,可提供关于网页性能、可访问性和最佳实践的综合报告。 WebPagetest:在线工具,可测量网页加载时间并提供详细的性能分析报告。 Chrome开发者工具:浏览器内置的开发者工具,提供了性能分析、网络监控和页面审查等功能。

8. 什么是渐进式图像加载(Progressive Image Loading)?它如何改善网页加载性能?

答案:渐进式图像加载是一种技术,通过逐步加载图像的模糊或低分辨率版本,然后逐渐提高图像的清晰度,以改善网页加载性能和用户体验。渐进式图像加载的好处包括:

用户可以更快地看到页面内容,提高感知速度。 逐步加载图像可以减少网页整体的加载时间。 渐进式图像加载可以提供平滑的过渡效果,避免页面内容突然闪烁或变化。

9. 什么是前端资源优先级(Resource Prioritization)?如何设置资源的优先级?

答案:前端资源优先级是指为不同类型的资源分配加载优先级,以优化网页加载性能。可以使用以下方法设置资源的优先级:

使用标签来指定资源的预加载,以确保关键资源尽早加载。 使用标签来指定可能在未来页面中使用的资源,以提前加载。 使用标签来指定要预解析的域名,以减少DNS查找时间。 使用标签来指定要预连接的域名,以减少建立连接的时间。

浏览器

1.解释一下浏览器的工作原理。

答案:浏览器的工作原理包括以下几个关键步骤:

解析:浏览器将接收到的HTML、CSS和JavaScript代码解析成DOM树、CSSOM树和JavaScript引擎可执行的代码。 渲染:浏览器使用DOM树和CSSOM树构建渲染树,然后根据渲染树进行布局(计算元素的位置和大小)和绘制(将元素绘制到屏幕上)。 布局和绘制:浏览器根据渲染树的变化进行布局和绘制,然后将最终的页面呈现给用户。 JavaScript引擎执行:浏览器的JavaScript引擎解释和执行JavaScript代码,并根据需要更新渲染树和重新渲染页面。

2. 什么是重绘(Repaint)和重排(Reflow)?它们之间有什么区别?

答案:重绘是指当元素的外观(如颜色、背景等)发生改变,但布局不受影响时的更新过程。重绘不会导致元素的位置或大小发生变化。

重排是指当元素的布局属性(如宽度、高度、位置等)发生改变时的更新过程。重排会导致浏览器重新计算渲染树和重新绘制页面的一部分或全部。

区别在于重绘只涉及外观的更改,而重排涉及布局的更改。重排比重绘更消耗性能,因为它需要重新计算布局和绘制整个页面。

3. 什么是事件冒泡和事件捕获?它们之间有什么区别?

答案:事件冒泡和事件捕获是指浏览器处理事件时的两种不同的传播方式。

事件冒泡是指事件从最内层的元素开始触发,然后逐级向上传播到父元素,直到传播到最外层的元素。

事件捕获是指事件从最外层的元素开始触发,然后逐级向下传播到最内层的元素。

区别在于传播方向的不同。事件冒泡是从内向外传播,而事件捕获是从外向内传播。

4. 解释一下同步和异步的JavaScript代码执行方式。

答案:同步代码是按照顺序执行的代码,每个任务必须等待前一个任务完成后才能执行。同步代码会阻塞后续代码的执行,直到当前任务完成。

异步代码是不按照顺序执行的代码,它会在后台执行,不会阻塞后续代码的执行。异步代码通常使用回调函数、Promise、async/await等方式来处理异步操作的结果。

通过异步执行,可以避免阻塞主线程,提高页面的响应性能。

5. 什么是事件循环(Event Loop)?它在JavaScript中的作用是什么?

答案:事件循环是JavaScript中处理异步代码执行的机制。它负责管理调度和执行异步任务,并将它们添加到执行队列中。

在JavaScript中,事件循环的作用是确保异步任务按照正确的顺序执行,并且不会阻塞主线程。它通过不断地从执行队列中取出任务并执行,以实现非阻塞的异步操作。

6. 解释一下浏览器的垃圾回收机制是如何工作的。

答案:浏览器的垃圾回收机制是一种自动管理内存的机制,用于检测和回收不再使用的对象,以释放内存资源。

垃圾回收机制通过标记-清除算法实现。它的工作原理如下:

标记阶段:垃圾回收器会从根对象(如全局对象)开始,递归遍历所有对象,并标记仍然可访问的对象。 清除阶段:垃圾回收器会扫描堆内存,清除未被标记的对象,并回收它们所占用的内存空间。 垃圾回收机制的目标是识别和回收不再使用的对象,以避免内存泄漏和提高内存利用率。

7. 解释一下浏览器的同源策略(Same-Origin Policy)及其限制。

答案:同源策略是浏览器的一项安全机制,用于限制来自不同源的网页之间的交互。同源是指协议、域名和端口号完全相同。

同源策略的限制包括:

脚本访问限制:不同源的脚本无法直接访问彼此的数据和操作。 DOM访问限制:不同源的网页无法通过JavaScript访问彼此的DOM元素。 Cookie限制:不同源的网页无法读取或修改彼此的Cookie。 AJAX请求限制:不同源的网页无法通过AJAX请求访问彼此的数据。 同源策略的存在可以防止恶意网站获取用户的敏感信息或进行恶意操作。

8. 什么是Web Workers?它们在浏览器中的作用是什么?

答案:Web Workers是一种浏览器提供的JavaScript API,用于在后台线程中执行耗时的计算任务,以避免阻塞主线程。

Web Workers的作用是提高浏览器的响应性能,使得在执行复杂计算或处理大量数据时,不会影响用户界面的流畅性。

Web Workers通过将任务委托给后台线程来实现并行处理,从而充分利用多核处理器的能力。它们可以与主线程进行通信,但不能直接访问DOM或执行UI相关的操作。

9. 解释一下浏览器缓存(Browser Cache)是什么,以及它的作用是什么?

答案:浏览器缓存是浏览器在本地存储Web页面和资源的副本,以便在后续访问时可以快速加载。它的作用是减少对服务器的请求次数和网络传输量,提高页面加载速度和用户体验。

浏览器缓存通过在首次请求时将资源保存到本地,并在后续请求时检查资源是否已经存在并且没有过期来工作。如果资源已经存在且未过期,浏览器会直接从缓存中加载资源,而不是从服务器重新下载。

10. 什么是重定向(Redirect)?它在浏览器中的作用是什么?

答案:重定向是指当浏览器请求一个URL时,服务器返回一个不同的URL,从而将浏览器的请求重定向到新的URL上。

重定向在浏览器中的作用是实现页面的跳转、URL的修改或资源的重定向。它可以用于多种情况,例如处理旧链接的跳转、实现URL的规范化、处理用户认证等。

重定向通过在HTTP响应中设置特定的状态码(如301永久重定向、302临时重定向)和Location头部字段来实现。

11. 什么是浏览器存储(Browser Storage)?它有哪些不同的存储机制?

答案:浏览器存储是浏览器提供的一种在客户端存储数据的机制,用于在不同的网页间共享数据或持久保存数据。

浏览器存储有以下不同的存储机制:

Cookie:小型文本文件,可以存储少量数据,并在每次HTTP请求中自动发送到服务器。 Web Storage(localStorage和sessionStorage):可以存储较大量的数据,以键值对的形式存储在浏览器中。 IndexedDB:一种高级的客户端数据库,可以存储大量结构化数据,并支持索引和事务操作。 Cache API:用于缓存网络请求的响应,以便离线访问或提高页面加载速度。 不同的存储机制适用于不同的需求,开发者可以根据具体情况选择合适的存储方式。

祝想跳槽的小伙伴们都能成功!需要更多完整面试题可以私信我!