D3.js 是網(wǎng)絡(luò)上幾乎所有最具創(chuàng)新性和令人興奮的信息可視化的幕后黑手。D3 代表數(shù)據(jù)驅(qū)動(dòng)文檔,它是一個(gè)品牌名稱,也是多年來(lái)以某種形式提供的一類應(yīng)用程序。我們可以使用這個(gè)庫(kù)來(lái)構(gòu)建各種數(shù)據(jù)驅(qū)動(dòng)的項(xiàng)目,從簡(jiǎn)單的條形圖到動(dòng)態(tài)地圖,再到復(fù)雜的空間和時(shí)間探索。當(dāng)您希望在數(shù)據(jù)可視化方面獲得完全的創(chuàng)意和技術(shù)自由時(shí),無(wú)論您是構(gòu)建用于研究的交互式原型、頂級(jí)科技公司廣泛且完全響應(yīng)的數(shù)據(jù)儀表板,還是揭示數(shù)據(jù)故事的長(zhǎng)篇文章,D3 都是您的首選工具當(dāng)用戶滾動(dòng)時(shí)。
D3 是一個(gè)開(kāi)源 JavaScript 庫(kù),由 Mike Bostock 于 2011 年創(chuàng)建,用于為 Web 生成動(dòng)態(tài)和交互式數(shù)據(jù)可視化。盡管過(guò)去幾年推出了許多新的數(shù)據(jù)可視化庫(kù),但它們通常在底層使用 D3。這是因?yàn)?D3 與 JavaScript 一樣,非常靈活且強(qiáng)大。
圖 1.1 漢密爾頓中每條線的交互式可視化,這是Shirley Wu創(chuàng)建的 D3 項(xiàng)目
D3.js 的創(chuàng)建是為了滿足對(duì)可通過(guò)網(wǎng)絡(luò)訪問(wèn)的復(fù)雜數(shù)據(jù)可視化的迫切需求。假設(shè)您的公司正在使用商業(yè)智能工具,但它沒(méi)有顯示您的團(tuán)隊(duì)所需的數(shù)據(jù)模式。您必須構(gòu)建一個(gè)自定義儀表板,根據(jù)您的特定領(lǐng)域量身定制,準(zhǔn)確顯示客戶的行為方式。該儀表板需要快速、交互式且可在整個(gè)組織內(nèi)共享。D3 將是此類項(xiàng)目的自然選擇。
或者想象一下,您被雇用來(lái)實(shí)現(xiàn)一個(gè)網(wǎng)頁(yè),該網(wǎng)頁(yè)以可視化方式展示 LGBTQ+ 群體(女同性戀、男同性戀、雙性戀、跨性別者、酷兒等)的權(quán)利在過(guò)去幾十年和全世界的演變情況。此頁(yè)面包含許多隨著用戶滾動(dòng)而變化的創(chuàng)意可視化效果。它們通過(guò)鼠標(biāo)事件顯示更多信息并適應(yīng)屏幕大小。D3 將是構(gòu)建此類項(xiàng)目的首選工具。
Mike Bostock 最初創(chuàng)建 D3 是為了利用新興的 Web 標(biāo)準(zhǔn),正如他所說(shuō),“避免了專有表示并提供了非凡的靈活性,暴露了 CSS3、HTML5 和 SVG 等 Web 標(biāo)準(zhǔn)的全部功能”(http: // d3js.org)。D3.js 版本 7 是這個(gè)流行庫(kù)的最新版本,它通過(guò)模塊化 D3 的各個(gè)部分來(lái)延續(xù)這一趨勢(shì),使其與 ECMAScript 模塊(一種基于 JavaScript 的腳本語(yǔ)言)和現(xiàn)代應(yīng)用程序開(kāi)發(fā)完全兼容。
D3.js 使開(kāi)發(fā)人員不僅能夠制作豐富的交互式應(yīng)用程序,而且能夠制作樣式和服務(wù)類似于傳統(tǒng) Web 內(nèi)容的應(yīng)用程序。這使得它們更可移植,更適合增長(zhǎng),并且更容易由其他團(tuán)隊(duì)成員可能不知道 D3 的具體語(yǔ)法的大型團(tuán)體維護(hù)。
圖 1.2 D3 開(kāi)發(fā)人員可以訪問(wèn)各種數(shù)據(jù)表示形式,地圖就是一個(gè)例子。這是由Christophe Viau創(chuàng)建的數(shù)字高程模型 (DEM) 地圖。
Bostock 決定廣泛處理數(shù)據(jù)并創(chuàng)建一個(gè)能夠像圖表一樣簡(jiǎn)單、像網(wǎng)絡(luò)一樣簡(jiǎn)單、像列表一樣簡(jiǎn)單地呈現(xiàn)地圖的庫(kù),這也意味著開(kāi)發(fā)人員不需要了解以下內(nèi)容的抽象和語(yǔ)法:一個(gè)用于地圖的庫(kù),另一個(gè)用于動(dòng)態(tài)文本內(nèi)容的庫(kù),還有另一個(gè)用于傳統(tǒng)圖形的庫(kù)。相反,用于運(yùn)行交互式網(wǎng)絡(luò)可視化的代碼接近于純 JavaScript,并且也類似于表示 D3 地圖上的動(dòng)態(tài)點(diǎn)的代碼。方法是相同的,但數(shù)據(jù)也可以是相同的,以一種方式制定用于網(wǎng)絡(luò)的節(jié)點(diǎn)和鏈路,而以另一種方式制定用于地圖上的地理空間表示。
D3 不僅可以創(chuàng)建復(fù)雜多樣的圖形,還可以嵌入用戶期望的高水平交互性,這對(duì)于現(xiàn)代 Web 開(kāi)發(fā)至關(guān)重要。使用 D3,每個(gè)圖表的每個(gè)元素(從旋轉(zhuǎn)的地球儀到餅圖的切片)都以相同的方式進(jìn)行交互。由于 D3 是由精通數(shù)據(jù)可視化實(shí)踐的人編寫的,因此它包含數(shù)據(jù)可視化和 Web 開(kāi)發(fā)中標(biāo)準(zhǔn)的交互式組件和行為。
圖 1.3 交互性是 D3 的核心。在此網(wǎng)絡(luò)可視化中,鼠標(biāo)交互揭示了不同組織之間的關(guān)系以及特定于所選節(jié)點(diǎn)的信息(https://amdufour.github.io/organizations-against-polization)。
數(shù)據(jù)可視化領(lǐng)域正在蓬勃發(fā)展,可用于生成數(shù)據(jù)綁定圖形的工具數(shù)量在過(guò)去十年中呈爆炸式增長(zhǎng)。我們擁有 Excel(數(shù)據(jù)可視化的常用入口)和 Power BI(用于構(gòu)建儀表板的 Microsoft 解決方案)等商業(yè)智能工具。另一方面,更有經(jīng)驗(yàn)的數(shù)據(jù)科學(xué)家通常會(huì)轉(zhuǎn)向 R 的 ggplot2 或 Python 的 matplotlib。
基于瀏覽器的點(diǎn)擊式工具(例如 Tableau、Flourish、DataWrapper、RAWGraphs 和 Google 圖表)也占據(jù)了主導(dǎo)地位,允許用最少的技術(shù)知識(shí)創(chuàng)建令人驚嘆的作品。
最后,HighCharts、Chart.js 和 D3.js 等 JavaScript 庫(kù)專門用于開(kāi)發(fā)基于 Web 的交互式可視化。
而且這個(gè)列表遠(yuǎn)非詳盡無(wú)遺......
那么,D3 在數(shù)據(jù)可視化工具的海洋中處于什么位置呢?我們何時(shí)以及如何使用它?我們可以說(shuō),雖然 D3 完全可以構(gòu)建此處列出的數(shù)據(jù)可視化庫(kù)提供的任何圖表,但它通常不是構(gòu)建簡(jiǎn)單的傳統(tǒng)圖表或探索階段(我們調(diào)查哪種類型的可視化是)的首選選項(xiàng)。最適合代表我們的數(shù)據(jù)。構(gòu)建 D3 項(xiàng)目需要時(shí)間,而 D3 在復(fù)雜、交互式和定制的項(xiàng)目中真正表現(xiàn)出色。數(shù)據(jù)可視化不僅僅是折線圖和散點(diǎn)圖!雖然上面提到的工具通常專注于預(yù)定義的圖表,但 D3 允許我們將數(shù)據(jù)綁定到任何圖形元素,并通過(guò)以獨(dú)特的方式組合這些視覺(jué)元素來(lái)打破常規(guī)。
圖 1.4 D3 具有 SVG 和畫(huà)布繪圖功能,允許開(kāi)發(fā)人員構(gòu)建自定義可視化效果,例如Elijah Meeks的樂(lè)譜表示形式。
以下是我們?nèi)绾卧跀?shù)據(jù)可視化項(xiàng)目范圍內(nèi)使用 D3 的示例。首先,我們從預(yù)先存在的數(shù)據(jù)集或手動(dòng)收集的數(shù)據(jù)開(kāi)始。在開(kāi)始數(shù)據(jù)分析過(guò)程之前,我們通常會(huì)花費(fèi)大量時(shí)間清理、格式化和準(zhǔn)備數(shù)據(jù)。Python 和 R 等數(shù)據(jù)科學(xué)工具在這方面功能強(qiáng)大,可以幫助我們識(shí)別隱藏在數(shù)據(jù)中的故事。Excel 還可以完成簡(jiǎn)單的數(shù)據(jù)整理和數(shù)據(jù)分析工作,并且需要較少的技術(shù)背景。我們甚至可以使用 JavaScript 和 D3 進(jìn)行基本數(shù)據(jù)探索,因?yàn)樗鼈兲峁┝宋覀儗⒃诒緯?shū)后面討論的統(tǒng)計(jì)方法。
一旦數(shù)據(jù)分析開(kāi)始,通常會(huì)創(chuàng)建一些原型來(lái)幫助完善我們的故事。Tableau 和 RawGraphs 等工具使我們能夠快速生成此類圖表。這是非常重要的一步,在此階段創(chuàng)建的可視化通常并不花哨或精致。我們不想在原型設(shè)計(jì)階段過(guò)于執(zhí)著于我們的想法,花費(fèi)大量時(shí)間。我們可能會(huì)發(fā)現(xiàn)自己必須“殺死我們的寶貝”并重新開(kāi)始幾次,直到我們找到最適合我們想要講述的故事的可視化效果。網(wǎng)絡(luò)圖可能是一個(gè)例外,直接跳到 D3 對(duì)于這些項(xiàng)目通常是有意義的。
最后,一旦我們知道要?jiǎng)?chuàng)建的可視化類型,就該卷起袖子,對(duì)其進(jìn)行編碼,并使用 D3 對(duì)其進(jìn)行完善。如今,編碼步驟通常發(fā)生在單頁(yè)應(yīng)用程序 (SPA) 中,使用 React 或 Svelte 等框架。
圖 1.5 使用 D3 構(gòu)建的自定義可視化的另一個(gè)示例,其中形狀與每首歌曲的不同屬性(例如持續(xù)時(shí)間、流派和節(jié)奏)成比例(https://amdufour.github.io/spotify-hits)。
您可能已經(jīng)嘗試過(guò) D3,并發(fā)現(xiàn)它并不容易上手。也許那是因?yàn)槟M且粋€(gè)簡(jiǎn)單的圖表庫(kù)。一個(gè)恰當(dāng)?shù)睦邮莿?chuàng)建條形圖,我們將在第 2 章和第 3 章中進(jìn)行此操作。D3 沒(méi)有一個(gè)函數(shù)來(lái)創(chuàng)建條形圖。相反,它有一個(gè)將<svg>容器附加到文檔對(duì)象模型 (DOM) 的函數(shù),以及另一組附加容器的函數(shù)。<rect>每個(gè)數(shù)據(jù)點(diǎn)的元素。然后,我們使用比例來(lái)計(jì)算構(gòu)成直方圖的矩形的長(zhǎng)度并設(shè)置它們的屬性。最后,我們調(diào)用另一組函數(shù),將 x 軸和 y 軸添加到條形圖中。如圖 1.6 所示,這個(gè)過(guò)程比使用 Highcharts 等專用圖表庫(kù)要長(zhǎng)得多。但 D3 處理數(shù)據(jù)和圖形的明確方式也是它的優(yōu)勢(shì)。盡管其他圖表庫(kù)允許您方便地制作折線圖和餅圖,但當(dāng)您想要?jiǎng)?chuàng)建不屬于傳統(tǒng)圖表范圍的可視化效果或?qū)崿F(xiàn)自定義交互時(shí),它們很快就會(huì)崩潰。不是D3。D3 允許您構(gòu)建您可以想象的任何數(shù)據(jù)驅(qū)動(dòng)圖形和交互性。
圖 1.6 使用 Highcharts 與 D3.js 生成的條形圖。Highcharts 的代碼更簡(jiǎn)單、更短,但 D3.js 更通用。
在圖 1.7 中,您可以看到我們通常如何使用 D3 進(jìn)行數(shù)據(jù)可視化編碼的地圖。我們從一個(gè)數(shù)據(jù)集(通常是 CSV 或 JSON 文件)開(kāi)始,然后使用 d3-fetch 模塊將此數(shù)據(jù)集加載到我們的項(xiàng)目中。我們通常需要執(zhí)行一些操作來(lái)格式化數(shù)據(jù)。例如,我們確保數(shù)字和日期的格式正確。如果我們之前沒(méi)有這樣做,我們可能還想詢問(wèn)我們的數(shù)據(jù)集以找到其主要特征。例如,提前知道其最大值和最小值通常很有幫助。然后我們準(zhǔn)備開(kāi)始構(gòu)建可視化,為此我們將結(jié)合我們將在本書(shū)中學(xué)習(xí)的不同 D3 函數(shù)。最后,我們通過(guò)監(jiān)聽(tīng)鼠標(biāo)事件來(lái)添加交互性,允許用戶過(guò)濾數(shù)據(jù)或放大可視化。
圖 1.7 如何使用 D3.js 實(shí)現(xiàn)數(shù)據(jù)可視化
D3.js 從來(lái)不會(huì)單獨(dú)使用,而是我們結(jié)合起來(lái)創(chuàng)建豐富的 Web 界面的技術(shù)和工具生態(tài)系統(tǒng)的一部分。與任何網(wǎng)頁(yè)一樣,D3 項(xiàng)目是在 DOM(文檔對(duì)象模型)內(nèi)構(gòu)建的,并利用 HTML5 的強(qiáng)大功能。盡管 D3 可以創(chuàng)建和操作傳統(tǒng)的 HTML 元素,例如分區(qū) ( <div>) 和列表 ( <ul>, <ol>),但我們主要使用 SVG 圖形或在畫(huà)布(從腳本渲染位圖圖像的 HTML 元素)內(nèi)生成可視化效果。然后,我們還可以使用舊的 CSS 樣式表,它可以增強(qiáng) D3 項(xiàng)目并使其設(shè)計(jì)更易于維護(hù),尤其是在廣泛的團(tuán)隊(duì)中。
鑒于 D3 是一個(gè) JavaScript 庫(kù),我們自然傾向于將 D3 方法與本機(jī) JavaScript 函數(shù)結(jié)合起來(lái)來(lái)訪問(wèn)和操作數(shù)據(jù)。D3 現(xiàn)在完全支持 JavaScript 的 ECMAScript 2015 或 ES6 修訂版以及大多數(shù)最新更新。D3 還作為模塊提供,可以集成到我們構(gòu)建 Web 項(xiàng)目所用的最新框架和庫(kù)中。使用這些模塊通常是首選方法,因?yàn)樗粫?huì)污染我們應(yīng)用程序的全局范圍。
在本節(jié)中,我們將簡(jiǎn)要討論這些技術(shù)及其在 D3 生態(tài)系統(tǒng)中的作用。由于 SVG 知識(shí)是理解 D3 的基礎(chǔ),因此我們將花時(shí)間更詳細(xì)地解釋您開(kāi)始構(gòu)建可視化所需理解的基礎(chǔ)知識(shí)。如果您已經(jīng)熟悉 HTML、SVG 元素、CSS、JavaScript 和 JavaScript 模塊,請(qǐng)隨意瀏覽或跳至第 1.3 節(jié)。
與 GIF 動(dòng)畫(huà)和框架成為網(wǎng)絡(luò)動(dòng)態(tài)內(nèi)容頂峰的時(shí)代相比,我們已經(jīng)走過(guò)了很長(zhǎng)一段路。在圖 1.8 中,您可以看到為什么 GIF 從未在強(qiáng)大的基于 Web 的數(shù)據(jù)可視化中流行起來(lái)。GIF 與設(shè)計(jì)用于使用 VML(矢量標(biāo)記語(yǔ)言)的 infoviz 庫(kù)一樣,對(duì)于早期瀏覽器來(lái)說(shuō)是必需的,但 D3 是為不再需要向后兼容性的現(xiàn)代瀏覽器而設(shè)計(jì)的。
圖 1.8 20 世紀(jì) 90 年代的一些例子,比如dpgraph.com,仍然存在,讓我們想起動(dòng)畫(huà) GIF 無(wú)處不在的時(shí)代。
當(dāng)您登陸網(wǎng)頁(yè)時(shí),要加載的第一個(gè)文件是超文本標(biāo)記語(yǔ)言或 HTML 文件,如下例所示。瀏覽器解析 HTML 文件以構(gòu)建文檔對(duì)象模型或 DOM,這是用于 Web 文檔的編程接口。我們經(jīng)常將其稱為 DOM 樹(shù),因?yàn)樗梢唤M嵌套元素(也稱為節(jié)點(diǎn)或標(biāo)簽)組成。在我們的示例中,<head>和 the<body>元素是<html>父元素的子元素。同樣,標(biāo)簽是、the和標(biāo)簽<body>的父標(biāo)簽。標(biāo)題也是該元素的同級(jí)元素。當(dāng)您加載網(wǎng)頁(yè)時(shí),您在屏幕上看到的是標(biāo)記中包含的元素。
<!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 中,每個(gè)元素的三類信息定義了其行為和外觀:樣式、屬性和特性。樣式?jīng)Q定顏色、大小、邊框、不透明度等。屬性包括類、id 和交互行為,盡管某些屬性也可以確定外觀,具體取決于您正在處理的元素類型。對(duì)于 SVG 元素,屬性用于設(shè)置不同形狀的位置、大小和比例。屬性通常指的是狀態(tài),例如復(fù)選框的“checked”屬性,如果該框被選中,則該屬性為 true;如果該框未被選中,則該屬性為 false。盡管術(shù)語(yǔ)“屬性”和“屬性”經(jīng)常互換使用,但它們是兩個(gè)不同的東西。呈現(xiàn) DOM 時(shí),屬性顯示為初始狀態(tài)。屬性是元素的當(dāng)前狀態(tài),并且可以隨著用戶與界面交互而改變。在第2章中,我們將討論用于生成或修改HTML和SVG元素的樣式和屬性的D3方法。
DOM 還決定元素在屏幕上的繪制順序,子元素在父元素之后和內(nèi)部繪制。盡管 CSS 屬性z-index使我們能夠部分控制傳統(tǒng) HTML 元素繪制到屏幕上的順序,但 SVG 元素嚴(yán)格遵循它們?cè)?DOM 中出現(xiàn)的順序。根據(jù)畫(huà)家的模型,之后繪制的內(nèi)容出現(xiàn)在之前繪制的內(nèi)容之上。
可擴(kuò)展矢量圖形 (SVG) 的引入確實(shí)改變了網(wǎng)絡(luò)的面貌。幾年之內(nèi),SVG 圖形成為主要的 Web 開(kāi)發(fā)工具。光柵圖形(PNG 和 JPG)由微小的像素組成,當(dāng)我們放大得太近時(shí),這些像素就會(huì)變得可見(jiàn),而矢量圖形則是通過(guò)數(shù)學(xué)和幾何圖形構(gòu)建的。它們?cè)谌魏纬叽绾腿魏纹聊环直媛氏露寄鼙3智逦耐庥^。SVG 圖形的另一個(gè)顯著優(yōu)勢(shì)是它們可以直接注入 DOM,允許開(kāi)發(fā)人員操縱其元素并為其設(shè)置動(dòng)畫(huà),并使屏幕閱讀器可以訪問(wèn)它們。如果構(gòu)建正確,SVG 也具有高性能,其文件大小僅為等效光柵圖像的一小部分。
當(dāng)使用 D3 創(chuàng)建數(shù)據(jù)可視化時(shí),我們通常將 SVG 形狀注入 DOM 并修改其屬性以生成組成可視化的視覺(jué)元素。了解 SVG 的工作原理、主要 SVG 形狀及其表示屬性對(duì)于大多數(shù) D3 項(xiàng)目至關(guān)重要。
本書(shū)的每一章都包含旨在支持您的學(xué)習(xí)體驗(yàn)的代碼練習(xí)。我們強(qiáng)烈建議您“做”這本書(shū),而不僅僅是“讀”這本書(shū),這意味著在閱讀章節(jié)時(shí)完成練習(xí)。通過(guò)這種方式,您將保留更多信息,并很快就能構(gòu)建自己的 D3 項(xiàng)目!
對(duì)于每個(gè)練習(xí)和項(xiàng)目,您都可以訪問(wèn)現(xiàn)成的代碼文件。您可以在本書(shū)的 Github 存儲(chǔ)庫(kù)(https://github.com/d3js-in-action-third-edition/code-files)上找到它們。如果您熟悉 Git,則可以將存儲(chǔ)庫(kù)克隆到您的計(jì)算機(jī)上。您還可以下載壓縮文件。
從 Github 存儲(chǔ)庫(kù)下載代碼文件
每一章都有自己的文件夾,其中包含一個(gè)或多個(gè)練習(xí),按照每章中的部分編號(hào)。練習(xí)包括一個(gè)start文件夾,其中包含入門所需的所有文件。您將在文件夾中找到練習(xí)的完整解決方案end。當(dāng)您完成一章的各個(gè)部分時(shí),您可以繼續(xù)在上一部分使用的文件中進(jìn)行編碼,或者使用專用于該部分的文件夾重新開(kāi)始。兩種選擇都會(huì)導(dǎo)致相同的結(jié)果。
讓我們開(kāi)始探索矢量圖形。轉(zhuǎn)到本書(shū)提供的代碼文件。找到end中的文件夾chapter_01/SVG_Shapes_Gallery并右鍵單擊該文件index.html。在菜單中,轉(zhuǎn)到打開(kāi)方式并選擇瀏覽器。我們建議使用 Chrome 或 Firefox,因?yàn)樗鼈兙哂谐錾臋z查器工具。該文件將在新的瀏覽器選項(xiàng)卡中打開(kāi),并且將出現(xiàn)您在圖 1.9 中看到的矢量圖形。您還可以在 Github 托管項(xiàng)目 ( https://d3js-in-action-third-edition.github.io/svg-shapes-gallery ) 上查看這些 SVG 形狀。
圖 1.9 我們將在本節(jié)中構(gòu)建的基本 SVG 形狀圖庫(kù)。
您正在查看的 SVG 圖形包含創(chuàng)建 D3 可視化時(shí)最常使用的形狀:線條、矩形、圓形、橢圓形、路徑和文本。
使用 D3 時(shí),您通常會(huì)告訴庫(kù)應(yīng)將哪些形狀附加到 DOM。您還負(fù)責(zé)了解需要計(jì)算哪些表示屬性才能使形狀具有您正在尋找的尺寸、顏色和位置。在下面的練習(xí)中,您將編寫創(chuàng)建圖 1.9 中每個(gè) SVG 元素的代碼。我們將此練習(xí)稱為SVG 形狀圖庫(kù)。之后,您將了解入門所需的所有 SVG 基礎(chǔ)知識(shí)。
在您選擇的代碼編輯器中打開(kāi)練習(xí)文件夾index.html中的文件。我們推薦VS Code,這是一個(gè)免費(fèi)、易于使用的代碼編輯器,并且具有多種功能,對(duì)前端開(kāi)發(fā)很有幫助。startSVG_Shapes_Gallery
正如您所看到的,index.html是一個(gè)簡(jiǎn)單的 HTML 文件。如果您在瀏覽器中打開(kāi)此文件(右鍵單擊該文件并在“打開(kāi)方式”菜單中選擇瀏覽器),您將只會(huì)看到一個(gè)空白頁(yè)面。這是因?yàn)樵?lt;body>元素為空。在接下來(lái)的小節(jié)中,我們將向此<body>元素添加 SVG 形狀。
清單 1.1.a SVG 形狀庫(kù)練習(xí)的起始 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>
以下部分將介紹多個(gè) SVG 元素及其屬性。作為開(kāi)發(fā)人員,我們?cè)跇?gòu)建項(xiàng)目、使用我們不熟悉的 SVG 元素或?qū)ふ?JavaScript 函數(shù)來(lái)執(zhí)行特定操作時(shí)嚴(yán)重依賴在線資源。在前端開(kāi)發(fā)中,MDN Web Docs ( https://developer.mozilla.org/ ) 始終是可靠且全面的資源。它包含 HTML 元素及其屬性、CSS 屬性和 JavaScript 函數(shù)的易于理解且通常可編輯的示例。
在 SVG 圖形的世界中,<svg></svg>容器是在其上繪制所有內(nèi)容的白板。每個(gè) SVG 形狀都嵌套在<svg>父級(jí)中。要查看其實(shí)際效果,請(qǐng)?jiān)谠豬ndex.html內(nèi)編輯并添加 SVG 容器<body>。在瀏覽器中重新加載頁(yè)面。目前還看不到任何東西。
<body>
<svg></svg>
</body>
打開(kāi)瀏覽器的檢查器工具(在瀏覽器窗口中右鍵單擊并選擇“檢查”)。在檢查器窗口中,您將看到組成頁(yè)面的 DOM。找到<svg></svg>容器,也稱為 SVG 節(jié)點(diǎn)。當(dāng)您在檢查器中將鼠標(biāo)移到它上面時(shí),SVG 元素會(huì)在頁(yè)面上突出顯示。您可以在圖 1.5 中看到此效果。
圖 1.10 在 DOM 樹(shù)中選擇并在視口中突出顯示的 SVG 節(jié)點(diǎn)
默認(rèn)情況下,瀏覽器為 SVG 容器提供寬度300px和高度。150px但我們也可以使用屬性來(lái)分配這些值。屬性用于提供有關(guān) HTML 元素的附加信息。對(duì)于內(nèi)聯(lián) SVG,我們主要使用屬性來(lái)設(shè)置組成 SVG 圖形的元素和形狀的大小和位置。
例如,我們可以設(shè)置SVG 容器的寬度和高度屬性。返回到文本編輯器,并將 awidth和 aheight屬性添加到 SVG 容器。將它們的值設(shè)置為900和300并保存文件。
<svg width="900" height="300"></svg>
在瀏覽器中重新加載項(xiàng)目并在檢查工具中找到 SVG 節(jié)點(diǎn)。請(qǐng)注意,寬度和高度屬性現(xiàn)在顯示在 SVG 容器的括號(hào)內(nèi)。如果將鼠標(biāo)移到檢查工具 DOM 樹(shù)中的 SVG 節(jié)點(diǎn)上,您還會(huì)看到視口中的 SVG 容器現(xiàn)在的大小為 900 像素 x 300 像素。
圖1.11 SVG節(jié)點(diǎn)采用其屬性指定的大小
為了幫助我們查看 SVG 容器,而不必從檢查器中突出顯示它,讓我們給它一個(gè)邊框。向 SVG 容器添加樣式屬性并插入 CSS 邊框?qū)傩浴T谙乱粋€(gè)代碼片段中,我們使用 border 速記屬性創(chuàng)建一個(gè)寬度為 1px 的黑色實(shí)心邊框。
<svg width="900" height="300" style="border:1px solid black;"></svg>
保存文件,重新加載頁(yè)面并確認(rèn) SVG 容器周圍有邊框。現(xiàn)在,調(diào)整瀏覽器窗口的大小,直到它小于 SVG 容器。您將觀察到 SVG 容器保持固定的寬度和高度,并且不適應(yīng)瀏覽器窗口的大小。讓我們嘗試使 SVG 容器具有響應(yīng)能力。
之前,我們將 SVG 屬性設(shè)置為絕對(duì)值 (900和300),瀏覽器將它們解釋為以像素為單位的測(cè)量值 (900px和300px)。但我們也可以使用百分比。在文本編輯器中,將寬度屬性更改為相對(duì)值“ 100%”,保存文件并重新加載頁(yè)面。
<svg width="100%" height="300" style="border:1px solid black;"></svg>
再次調(diào)整瀏覽器大小,并注意 SVG 如何獲取可用的完整寬度并保持 300 像素的固定高度。這更好,但我們失去了原來(lái)的縱橫比。
為了使內(nèi)聯(lián) SVG 具有響應(yīng)能力,我們可以使用viewBox 屬性。在代碼編輯器中,從 SVG 容器中刪除width和屬性,并將它們替換為屬性。給它一個(gè)值。heightviewBox"0 0 900 300"
<svg viewBox="0 0 900 300" style="border:1px solid black;"></svg>
再次調(diào)整瀏覽器窗口的大小。你注意到了什么?SVG 容器現(xiàn)在可以適應(yīng)任何屏幕尺寸,同時(shí)保持其縱橫比為 900:300。我們有一個(gè)響應(yīng)式 SVG!
正如您所注意到的,viewBox 屬性由四個(gè)值的列表組成。前兩個(gè)數(shù)字指定 viewBox 坐標(biāo)系的原點(diǎn)(x 和 y)。在本書(shū)中,我們將始終使用0 0,但很高興知道這些值可用于更改 SVG 容器的哪一部分在屏幕上可見(jiàn)。viewBox 屬性的最后兩個(gè)數(shù)字是其寬度和高度。它們定義 SVG 的縱橫比,并確保其完美縮放以適合任何容器而不變形。
安裝在容器內(nèi)是這里的關(guān)鍵。到目前為止,我們的內(nèi)聯(lián) SVG 的容器是 HTML<body>元素,它通常會(huì)擴(kuò)展以適應(yīng)瀏覽器的視口。如果視口變得非常大,SVG 也會(huì)變得非常大。通常,我們希望 SVG 具有最大寬度,以便它不會(huì)大于頁(yè)面上的其余內(nèi)容。為此,請(qǐng)將 SVG 容器包裝在寬度為 100%、最大寬度為 1200px 的 div 內(nèi)。為簡(jiǎn)單起見(jiàn),我們將這些屬性設(shè)置為內(nèi)聯(lián)樣式,但在實(shí)際項(xiàng)目中,這些屬性將來(lái)自 CSS 文件。請(qǐng)注意,我們還添加了值邊距“ 0 auto”,以使 SVG 在頁(yè)面上水平居中。
<div style="width:100%; max-width:1200px; margin:0 auto;">
<svg viewBox="0 0 900 300" style="border:1px solid black;"> ... </svg>
</div>
嘗試再次調(diào)整瀏覽器的大小,看看我們的 SVG 如何優(yōu)雅地適應(yīng)任何屏幕尺寸,同時(shí)尊重其容器的最大寬度。該策略有助于將 D3 可視化注入響應(yīng)式網(wǎng)頁(yè),我們將在本書(shū)中使用它。
既然我們知道了如何使內(nèi)聯(lián) SVG 具有響應(yīng)性,那么解決 SVG 形狀在 SVG 容器內(nèi)的定位方式就很重要了。SVG 容器就像一張白紙,我們可以在上面繪制矢量形狀。矢量形狀是根據(jù)基本幾何原理定義的,并參考 SVG 容器的坐標(biāo)系進(jìn)行定位。
SVG 坐標(biāo)系與笛卡爾坐標(biāo)系類似。其 2D 平面使用兩個(gè)垂直軸來(lái)確定元素的位置,稱為 x 和 y。這兩個(gè)軸源自SVG 容器的左上角,如圖 1.12 所示。意思是y軸的正方向是從上到下。記住這一點(diǎn)可以讓你避免一些頭痛!
圖1.12 SVG容器的坐標(biāo)系和元素的位置
為了在 SVG 容器內(nèi)定位元素,我們從左上角的原點(diǎn)開(kāi)始向右移動(dòng)。這將為我們提供元素的水平 (x) 位置。對(duì)于垂直 (y) 位置,我們從頂部開(kāi)始向下移動(dòng)。這些位置由每個(gè) SVG 形狀的表示屬性定義。
現(xiàn)在,我們將了解您在構(gòu)建 D3 項(xiàng)目時(shí)經(jīng)常遇到的 SVG 形狀。我們還將討論它們的主要表現(xiàn)屬性。這里的目標(biāo)絕不是編寫 SVG 提供的所有形狀和功能的綜合指南,而是涵蓋支持您的 D3 之旅的基礎(chǔ)知識(shí)。
出色的藝術(shù)家可以用矢量圖形繪制任何東西,但您可能不會(huì)因?yàn)槟且幻囆g(shù)家而關(guān)注 D3。相反,您正在處理圖形并考慮更務(wù)實(shí)的目標(biāo)。從這個(gè)角度來(lái)看,理解幾何基元(也稱為圖形基元)的概念至關(guān)重要。幾何基元是簡(jiǎn)單的形狀,例如點(diǎn)、線、圓和矩形。這些形狀可以組合成更復(fù)雜的圖形,特別方便直觀地顯示信息。
基元對(duì)于理解您在現(xiàn)實(shí)世界中看到的復(fù)雜信息可視化也很有用。樹(shù)形布局,就像我們將在第 10 章中構(gòu)建的那樣,當(dāng)您意識(shí)到它們只是圓形和直線時(shí),它們就不那么令人生畏了。當(dāng)您將交互式時(shí)間線視為矩形和點(diǎn)的集合時(shí),它們更容易理解和創(chuàng)建。即使是主要以多邊形、點(diǎn)和線形式出現(xiàn)的地理數(shù)據(jù),當(dāng)您將其分解為最基本的圖形結(jié)構(gòu)時(shí),也不會(huì)那么混亂。
線條元素可能是所有 SVG 形狀中最簡(jiǎn)單的。它獲取兩個(gè)點(diǎn)的位置,設(shè)置為屬性,并在它們之間繪制一條直線。返回到該index.html文件,并在 SVG 容器內(nèi)添加一個(gè)<line />元素。聲明其屬性x1和y1并分別賦予它們值 50 和 45。這意味著我們的線的起點(diǎn)位于(50, 45)SVG 容器的坐標(biāo)系中。如果從 SVG 容器的左上角開(kāi)始,向右移動(dòng) 50 像素,向下移動(dòng) 45 像素,您將遇到線條的起點(diǎn)。(140, 225)同樣,使用屬性x2和y2將線的端點(diǎn)設(shè)置為。
<svg>
<line x1="50" y1="45" x2="140" y2="225" />
</svg>
圖1.13 在SVG容器的坐標(biāo)系中定位線元素
如果您保存并重新加載項(xiàng)目,您的線條將不可見(jiàn),您可能想知道發(fā)生了什么。為了使 SVG 線條在屏幕上可見(jiàn),我們還需要設(shè)置其描邊屬性,該屬性控制線條的顏色。border 屬性的值與 CSS color 屬性類似。它可以是顏色名稱 ( black, blue, ...)、RGB 顏色 ( rgb(255,0,0)) 或十六進(jìn)制值 ( #808080)。向您的線條添加筆劃屬性,并為其指定您選擇的顏色(我們使用黑色)。現(xiàn)在它應(yīng)該在屏幕上可見(jiàn)。
<line x1="50" y1="45" x2="140" y2="225" stroke="black" />
如果我們想設(shè)置線條的寬度,我們可以使用描邊寬度屬性。此屬性接受絕對(duì)數(shù)字(轉(zhuǎn)換為像素)或相對(duì)值 (%)。例如,以下行的 a 為stroke-width3px。如果stroke-width未聲明該屬性,瀏覽器將應(yīng)用默認(rèn)值 1px。
<line x1="50" y1="45" x2="140" y2="225" stroke="black" stroke-width="3" />
打開(kāi)瀏覽器的檢查器工具并找到 SVG 節(jié)點(diǎn)及其包含的行。雙擊其中一個(gè)屬性,更改其值并觀察新值如何修改線的起點(diǎn)或終點(diǎn)。花時(shí)間嘗試不同的值,以確認(rèn)您了解屬性x1、y1、x2和如何y2影響線條的位置和長(zhǎng)度。
-20現(xiàn)在,為屬性賦予 值x1。你看到線的起點(diǎn)是如何消失的嗎?落在 SVG viewBox 之外的任何形狀或形狀部分在屏幕上都不可見(jiàn)。不過(guò),該元素仍然存在于 DOM 中。我們可以訪問(wèn)和操縱它。如果 SVG 中的某個(gè)元素不可見(jiàn),并且您不知道為什么首先要檢查它是否在 SVG viewBox 之外!請(qǐng)記住,您始終可以通過(guò)使用開(kāi)發(fā)人員工具檢查 DOM 來(lái)找到它。正如我們之前所做的那樣,如果將鼠標(biāo)移到檢查器工具中的元素上,即使它位于 SVG viewBox 之外,它也會(huì)在視口中突出顯示。
圖 1.14 SVG 線在 SVG 容器外部時(shí)部分隱藏
為了提高效率,大多數(shù) SVG 元素只需要一個(gè)自閉合標(biāo)簽(我們使用 <line /> 而不是 <line></line>)。與其他一些 HTML 標(biāo)簽一樣,SVG 元素的固有結(jié)構(gòu)在自閉合標(biāo)簽中提供了所有必需的信息。這對(duì)于 SVG 文本元素有所不同,其中文本放置在開(kāi)始標(biāo)簽和結(jié)束標(biāo)簽之間。
顧名思義,矩形元素<rect />在屏幕上繪制一個(gè)矩形形狀。該<rect />元素需要四個(gè)屬性才能可見(jiàn)。屬性x和y聲明矩形左上角的位置,而屬性width和height分別控制其寬度和高度。<rect />在 SVG 容器中添加以下元素及其屬性。
<rect x="260" y="25" width="120" height="60" />
在我們的示例中,矩形的左上角位于SVG 容器原點(diǎn)的260px右側(cè)和下方。25px它的寬度為120px,高度為60px。與其他位置屬性一樣,我們可以使用百分比而不是絕對(duì)數(shù)字來(lái)設(shè)置它們的值。例如,如果我們將該width屬性設(shè)置為50%,則矩形將擴(kuò)展到 SVG 容器寬度的一半。
圖 1.15 在 SVG 容器的坐標(biāo)系中定位矩形并調(diào)整其大小
您可能已經(jīng)注意到我們的矩形充滿了黑色。默認(rèn)情況下,瀏覽器對(duì)大多數(shù) SVG 形狀應(yīng)用黑色填充。我們可以通過(guò)設(shè)置fill屬性并為其指定任何 CSS 顏色來(lái)更改該顏色。如果我們想給矩形添加邊框,我們添加一個(gè)描邊屬性。圖 1.16 顯示了一些示例。請(qǐng)注意,如果不聲明屬性,則矩形周圍不會(huì)繪制邊框stroke。另外,在最后一個(gè)矩形中,屬性fill-opacity和border-opacity用于使fill和stroke半透明。與 CSS 中一樣,不透明度可以設(shè)置為絕對(duì)值 ( 0.3) 或百分比 (30%)。與填充和描邊相關(guān)的所有屬性也可以從 CSS 文件設(shè)置或修改。
圖 1.16 應(yīng)用于矩形 SVG 形狀的不同樣式屬性
如果您希望矩形具有圓角,則只需添加rx和ry屬性,分別是水平和垂直角半徑。這些屬性接受絕對(duì)值(以像素為單位)和相對(duì)值(百分比)。例如,下面矩形的每個(gè)角的半徑都是 20px。將此矩形添加到您的形狀庫(kù)中。
<rect x="260" y="100" width="120" height="60" rx="20" ry="20" />
此時(shí),您可能想知道 SVG 中是否有一個(gè)可以繪制正方形的元素。我們不需要一個(gè)!在 SVG 中,我們<rect />通過(guò)賦予元素相等width和height屬性來(lái)繪制帶有元素的正方形。例如,以下<rect />元素將繪制一個(gè) 60px x 60px 的正方形。也將它添加到您的形狀庫(kù)中。
<rect x="260" y="175" width="60" height="60" />
作為參考,我們的形狀庫(kù)中現(xiàn)在有三種類型的 SVG 矩形:經(jīng)典矩形、圓角矩形和正方形。為了好玩,我們給了它們顏色#6ba5d7并玩弄它們stroke和fill屬性。請(qǐng)注意,只有筆劃在正方形上可見(jiàn),因?yàn)槠鋐ill屬性值為transparent或none。您的矩形應(yīng)該類似于圖 1.17 中的矩形,除非您更改了它們的屬性(我們鼓勵(lì)您這樣做)!
<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" />
當(dāng)您嘗試在可視化中對(duì)齊形狀時(shí)需要記住的一點(diǎn)是,筆劃是均勻地繪制在 SVG 形狀的內(nèi)部和外部邊界上的。如下圖所示,如果矩形的width屬性為 40px,則應(yīng)用 ofstroke-width會(huì)1在視覺(jué)上向矩形的左側(cè)添加 0.5px,向右側(cè)添加 0.5px(而不是像我們本能地認(rèn)為的那樣,向每邊添加 1px) ),實(shí)際總寬度為 41px。如果stroke-width是2,它會(huì)在每邊添加 1px,依此類推。
筆劃寬度對(duì) SVG 形狀實(shí)際寬度的影響
圓形形狀經(jīng)常用于數(shù)據(jù)可視化。它們自然地吸引眼球,并使可視化感覺(jué)更加友好和有趣。我們使用<circle />元素繪制 SVG 圓圈。其所需屬性是圓心的位置 ( cx , cy ) 及其半徑 ( r )。圓的半徑是從圓心到其邊界上任意點(diǎn)所繪制的直線的長(zhǎng)度。將以下圓圈添加到您的形狀庫(kù)中。將其中心定位于(530, 80)并為其指定 50px 的半徑。
<circle cx="530" cy="80" r="50" />
圖 1.18 在 SVG 容器的坐標(biāo)系中定位圓和橢圓并調(diào)整其大小
您還可以使用圓形的填充和描邊屬性。為了生成圖 1.18 中的效果,我們使用了透明填充和 3px 的描邊,顏色為#81c21c。
類似地,<ellipse />元素需要形狀中心位置的屬性 ( cx, cy)。圓形具有恒定的半徑,而橢圓形的半徑則不同,從而使其具有扁平形狀。我們通過(guò)聲明水平半徑 ( rx ) 和垂直半徑 ( ry ) 來(lái)創(chuàng)建這種扁平化效果。將下一個(gè)片段添加到您的圖庫(kù)中。它將在圓下方繪制一個(gè)橢圓,水平半徑為 50px,垂直半徑為 30px。
<ellipse cx="530" cy="205" rx="50" ry="30" />
SVG路徑是迄今為止所有 SVG 元素中最靈活的。它們?cè)?D3 中廣泛用于繪制幾乎所有無(wú)法用迄今為止討論的形狀基元之一(直線、矩形、圓形和橢圓形)表示的復(fù)雜形狀和曲線。
d我們通過(guò)聲明其屬性(代表“draw”)來(lái)指示瀏覽器如何繪制路徑。該d屬性包含一個(gè)命令列表,從開(kāi)始繪制路徑的位置到要使用的曲線類型,直到指定我們是否希望路徑成為閉合形狀。例如,將以下路徑元素添加到您的庫(kù)中。在此示例中,d屬性以M680 150開(kāi)頭,表示“移動(dòng)到坐標(biāo)(680, 150)”。然后我們從當(dāng)前點(diǎn) (680, 150) 到字母 C 后面的第三個(gè)坐標(biāo) (755 150) 指定的端點(diǎn)繪制一條三次貝塞爾曲線。三次貝塞爾曲線需要控制點(diǎn),即字母 C 后面、起點(diǎn)和終點(diǎn)之間的坐標(biāo)((710, 80) 和 (725, 80))。這些控制點(diǎn)定義了曲線的陡峭程度。然后我們有字母 S,它代表“停止”。它的工作原理與字母 C 類似,只不過(guò)它通向曲線的端點(diǎn)。這里最后一條曲線的起點(diǎn)是(755 150),終點(diǎn)是(840, 150),控制點(diǎn)是(810, 220)。曲線可以由一個(gè)或兩個(gè)控制點(diǎn)定義。
<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 一個(gè)簡(jiǎn)單的 SVG 路徑及其控制點(diǎn)
要深入了解 SVG 路徑,請(qǐng)參閱 MDN 的教程:https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths。
手動(dòng)編寫d屬性對(duì)于簡(jiǎn)單的形狀是可行的,但隨著形狀變得復(fù)雜而變得乏味。幸運(yùn)的是,D3 具有強(qiáng)大的形狀生成器,我們將在第 4 章中討論。
關(guān)于路徑要記住的另一件重要的事情是瀏覽器將以黑色填充它們,除非我們將它們的fill屬性設(shè)置為noneor transparent。即使路徑未閉合(如我們的示例中所示),情況也是如此。
<div>內(nèi)聯(lián) SVG 圖形的最大優(yōu)點(diǎn)之一是它們可以包含可導(dǎo)航的文本,就像插入一個(gè)或一個(gè)元素中的任何其他 HTML 文本一樣<p>。這對(duì)于可訪問(wèn)性來(lái)說(shuō)是一個(gè)很大的優(yōu)勢(shì)。
由于數(shù)據(jù)可視化通常包含多個(gè)標(biāo)簽,因此有必要了解如何使用<text></text>元素操作 SVG 文本。讓我們向形狀庫(kù)添加標(biāo)簽,以了解 SVG 文本的基本原理。
到目前為止討論的 SVG 形狀使用自閉合標(biāo)簽 ( <line />, <rect />, <path />, ...)。使用 SVGtext元素時(shí),我們需要使用開(kāi)始標(biāo)簽和結(jié)束標(biāo)簽。我們將要顯示的文本放置在這兩個(gè)標(biāo)簽之間。例如,讓我們?cè)?SVG 中添加一個(gè)表示“l(fā)ine”的文本元素。
<text>line</text>
保存文件并重新加載頁(yè)面。您可能希望文本出現(xiàn)在 SVG 容器的左上角,但實(shí)際上卻看不到......這是為什么?默認(rèn)情況下,SVG 文本的位置是參考其基線計(jì)算的,由屬性控制dominant-baseline。如果文本基線的坐標(biāo)是(0, 0),您可以在圖 1.20 中看到實(shí)際文本如何最終出現(xiàn)在 SVG 容器之外。由于位于 SVG 容器外部的任何元素都是不可見(jiàn)的,因此我們看不到文本。
圖 1.20 位于 SVG 容器外部的文本
使用 SVG 文本時(shí)要考慮的另一點(diǎn)是文本的流動(dòng)方式。常規(guī) HTML 元素按照控制內(nèi)容流的特定規(guī)則放置在頁(yè)面上。如果您將一堆<div></div>元素插入頁(yè)面中,它們會(huì)自然地堆疊在一起,并且它們的內(nèi)容將重排,以便它永遠(yuǎn)不會(huì)超出其容器。SVG 文本根本不流動(dòng),每個(gè) SVG 元素必須單獨(dú)定位。一種方法是設(shè)置它們的x和y屬性。如果我們使用這些屬性將文本放置在 處(60, 260),標(biāo)簽“l(fā)ine”將出現(xiàn)在形狀庫(kù)中 SVG 線的下方。
<text x="60" y="260">line</text>
為了練習(xí),創(chuàng)建一個(gè)新的文本元素,將標(biāo)簽“矩形”放置在矩形和正方形下方。
到目前為止,我們已經(jīng)使用x和y屬性來(lái)聲明文本元素的左下角。但是如果我們想設(shè)置文本中點(diǎn)的位置怎么辦?我們可以通過(guò)使用屬性text-anchor并為其指定值來(lái)做到這一點(diǎn)middle。例如,我們可以使用此屬性將圓形的文本標(biāo)簽居中。
<text x="530" y="155" text-anchor="middle">circle</text>
圖1.21 text-anchor屬性影響SVG文本的對(duì)齊方式。它的默認(rèn)值是“開(kāi)始”。為了根據(jù)中間對(duì)齊文本元素,我們應(yīng)用“middle”的 text-andchor 屬性。類似地,為了根據(jù)文本的結(jié)尾對(duì)齊文本,我們應(yīng)用“end”的 text-andchor 屬性。
最后為橢圓添加一個(gè)標(biāo)簽,為路徑元素添加另一個(gè)標(biāo)簽。默認(rèn)情況下,SVG 文本為黑色。您可以使用屬性更改其顏色fill。
我們將在本節(jié)中討論的最后一個(gè) SVG 元素是組元素。group 或<g></g>元素與我們迄今為止討論的 SVG 元素不同,因?yàn)樗鼪](méi)有圖形表示,也不作為有界空間存在。相反,它是元素的邏輯分組。在創(chuàng)建由多個(gè)形狀和文本元素組成的可視化效果時(shí),您需要廣泛使用組。
如果我們希望正方形和“rect”標(biāo)簽一起顯示并在 SVG 容器中作為一個(gè)整體移動(dòng),我們可以將它們放置在一個(gè)<g>元素內(nèi),如下例所示。請(qǐng)注意元素的左上角如何<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>
包含正方形及其標(biāo)簽的組現(xiàn)在顯示在 SVG 容器的左上角。我們可以將這個(gè)組及其包含的所有元素移動(dòng)到 SVG 容器中任何我們想要的位置,同時(shí)保持正方形與其標(biāo)簽之間的對(duì)齊。
在 SVG 容器中移動(dòng)組是通過(guò)轉(zhuǎn)換屬性完成的。變換屬性比目前討論的屬性有點(diǎn)嚇人,但與 CSS 變換屬性相同。它采用一個(gè)變換(平移、旋轉(zhuǎn)、縮放等)或一堆變換作為值。為了移動(dòng)一個(gè)組,我們使用translate(x, y)變換。如果我們想要將<rect>和<text>元素移回其原始位置,我們需要對(duì)元素應(yīng)用向右 260 像素和向下 175 像素的平移。<g>為此,我們將其 Transform 屬性設(shè)置為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è)有用的方面是它的子元素繼承它的屬性。為了說(shuō)明這一點(diǎn),讓我們將<g>元素中所有剩余的文本元素分組,除了標(biāo)簽“矩形”,我們已經(jīng)將其與正方形分組。
<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如果我們對(duì)組應(yīng)用填充屬性,<text>則該組內(nèi)的每個(gè)元素將繼承相同的顏色。同樣,如果我們向組添加樣式屬性,例如使用font-family和font-size屬性,則組內(nèi)的文本將繼承這些屬性。
<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>
最后重新加載頁(yè)面并觀察組內(nèi)的標(biāo)簽如何繼承組的顏色和字體,而保留在該組之外的標(biāo)簽則保持其原始外觀。這種將共享屬性應(yīng)用于組元素的技術(shù)非常方便,可以幫助您將 DRY(不要重復(fù)自己)編碼原則應(yīng)用到您的工作中。當(dāng)您需要更新這些屬性時(shí),它也會(huì)讓您的生活更輕松。
恭喜您完成了本書(shū)的第一個(gè)練習(xí)!您可以在清單 1.1.b 和編碼文件的末尾文件夾中找到形狀庫(kù)的完整代碼。當(dāng)您構(gòu)建第一個(gè) D3 項(xiàng)目時(shí),請(qǐng)使用此練習(xí)作為參考。
清單 1.1.b 用于 SVG 形狀圖庫(kù)練習(xí)的完整 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>
現(xiàn)在輪到你了!創(chuàng)建如下圖所示的 SVG 圖形。您可以在本章代碼文件start內(nèi)的文件夾中工作。02_SVG_exercise以下是一些指導(dǎo)原則:
· 創(chuàng)建一個(gè)寬度和高度均為 400 像素的響應(yīng)式 SVG 容器(當(dāng)屏幕上有足夠的空間時(shí))。
· 繪制一個(gè)寬度和高度均為 200 像素的正方形。將其置于 SVG 容器的中心,并為其提供透明填充和 5px 黑色描邊。
· 在 SVG 容器的中心添加一個(gè)半徑為 100px 的圓。將其填充屬性設(shè)置為 CSS 顏色名稱“plum”。
· 繪制兩條對(duì)角黑線,筆劃為 5 像素。一個(gè)從正方形的左上角到右下角。另一個(gè)從正方形的右上角到左下角。
· 添加文本“SVG 太棒了!” 位于正方形上方并將其置于 SVG 容器的中心。為文本指定以下樣式屬性:字體大小為 18px,字體系列為 sans-serif。
我們鼓勵(lì)您構(gòu)建此 SVG 圖形來(lái)強(qiáng)化本節(jié)中討論的概念。
02_SVG_exercise / end您可以在附錄 D 的 D.1.1 節(jié)和本章代碼文件的文件夾中找到解決方案。我們鼓勵(lì)您嘗試自己完成它。
我們已經(jīng)提到過(guò),我們通常使用 SVG 元素構(gòu)建 D3 項(xiàng)目。有時(shí),我們可能需要從大型數(shù)據(jù)集創(chuàng)建復(fù)雜的可視化效果,而傳統(tǒng)的 SVG 方法可能會(huì)產(chǎn)生性能問(wèn)題。請(qǐng)務(wù)必記住,對(duì)于數(shù)據(jù)可視化中的每個(gè)圖形細(xì)節(jié),D3 都會(huì)將一個(gè)或多個(gè) SVG 元素附加到 DOM。一個(gè)典型的例子是由數(shù)千個(gè)節(jié)點(diǎn)和鏈接組成的大型網(wǎng)絡(luò)可視化。這些可能會(huì)讓你的瀏覽器喘不過(guò)氣來(lái)……盡管瀏覽器可以輕松處理的對(duì)象數(shù)量隨著性能的提高而不斷增加,但普遍接受的經(jīng)驗(yàn)法則是,如果滿足以下條件,我們應(yīng)該考慮使用畫(huà)布而不是 SVG:可視化包含 1000 多個(gè)元素。
Canvas 是一種客戶端繪圖 API,它使用腳本(通常是 JavaScript)來(lái)創(chuàng)建視覺(jué)效果和動(dòng)畫(huà)。它不會(huì)將 XML 元素添加到 DOM,這在從大型數(shù)據(jù)集構(gòu)建可視化時(shí)可以顯著提高性能。
Canvas 還允許您使用 WebGL API 來(lái)創(chuàng)建 3D 對(duì)象。盡管學(xué)習(xí) WebGL 超出了本書(shū)的范圍,但為 Web 創(chuàng)建 3D 數(shù)據(jù)可視化是可能的。目前主要用于實(shí)驗(yàn)項(xiàng)目。在第 15 章中,我們將介紹如何使用畫(huà)布構(gòu)建可視化并討論其優(yōu)點(diǎn)和缺點(diǎn)。
CSS 代表層疊樣式表,是一種描述 DOM 元素如何在屏幕上顯示及其外觀的語(yǔ)言。從頁(yè)面的整體網(wǎng)格布局到文本使用的字體系列,再到散點(diǎn)圖中圓圈的顏色,CSS 可以將普通的 HTML 文件變成令人驚嘆的網(wǎng)頁(yè)。在 D3 項(xiàng)目中,我們通常使用內(nèi)聯(lián)樣式或通過(guò)外部樣式表應(yīng)用 CSS 樣式。
內(nèi)聯(lián)樣式應(yīng)用于具有該style屬性的元素,如以下示例所示。該style屬性可以在傳統(tǒng)的 HTML 或 SVG 元素上使用,D3 有一個(gè)方便的方法來(lái)設(shè)置或修改該屬性,我們將在第 2 章中討論。
<div style="padding:10px; background:#00ced1;"> ... </div>
<text style="font-size:16px; font-family:serif;"> ... </text>
內(nèi)聯(lián)樣式僅影響應(yīng)用它們的元素。如果我們想要將相同的設(shè)計(jì)傳播到多個(gè)元素,我們需要將相同的style屬性應(yīng)用于每個(gè)元素(或?qū)⑺性匕b在一起的 SVG 組)。它當(dāng)然有效,但不是最有效的方法。
另一方面,外部 CSS 樣式表非常適合全局應(yīng)用樣式。一種策略是要求 D3 將相同的類名添加到多個(gè)元素。然后,我們使用此類名稱作為外部樣式表中的選擇器,并將相同的樣式屬性應(yīng)用于目標(biāo)元素組,如以下示例所示。這種方法效率更高,尤其是在維護(hù)大型項(xiàng)目時(shí)。它還遵循關(guān)注點(diǎn)分離原則,即我們將由 JavaScript 控制的行為與由 CSS 監(jiān)管的樣式分開(kāi)。請(qǐng)注意,CSS 預(yù)處理器(例如 SASS 和 LESS)是此處描述的外部樣式表方法的一部分。
在 CSS 樣式表中:
.my-class {
font-size: 16px;
font-family: serif;
}
In the DOM:
<text class="my-class"> ... </text>
請(qǐng)記住,內(nèi)聯(lián)樣式優(yōu)先于從外部樣式表應(yīng)用的樣式。在任何前端開(kāi)發(fā)項(xiàng)目中,規(guī)劃 CSS 樣式的架構(gòu)并考慮級(jí)聯(lián)順序非常重要。
D3 是一個(gè) JavaScript 庫(kù)。它在 JavaScript 現(xiàn)有核心功能的基礎(chǔ)上添加了新方法。這意味著在使用 D3 時(shí),具有一點(diǎn) JavaScript 經(jīng)驗(yàn)會(huì)很有幫助。這也意味著,在構(gòu)建 D3 項(xiàng)目時(shí),您可以訪問(wèn)所有現(xiàn)有的 JavaScript 功能。
在本節(jié)中,我們將解釋 D3 項(xiàng)目中廣泛使用的兩個(gè) JavaScript 主題:方法鏈和對(duì)象操作。
如果您在網(wǎng)絡(luò)上搜索 D3 項(xiàng)目的示例,您會(huì)發(fā)現(xiàn)在同一選擇上會(huì)依次調(diào)用方法。這種技術(shù)就是我們所說(shuō)的方法鏈,有助于保持代碼簡(jiǎn)潔和可讀。
我們可以將方法鏈視為汽車裝配線。假設(shè)我們編寫了運(yùn)行這樣一條裝配線的腳本。正如您在下面的示例中看到的,我們首先聲明一個(gè)car創(chuàng)建新Car()對(duì)象的變量。然后我們調(diào)用函數(shù)putOnHood(),將引擎蓋放在汽車頂部,然后我們繼續(xù)調(diào)用將放置車輪、輪胎和燈的函數(shù)。每個(gè)連續(xù)的調(diào)用都會(huì)添加一個(gè)元素到Car()對(duì)象,并且,一旦執(zhí)行了所有方法,汽車就有了引擎蓋、車輪、輪胎和車燈。每個(gè)方法都會(huì)將更新后的汽車對(duì)象傳遞給下一個(gè)方法,從而“鏈接”。請(qǐng)注意,每個(gè)調(diào)用都用點(diǎn)分隔,并且調(diào)用方法的順序很重要。在我們的汽車裝配線示例中,我們需要先安裝車輪,然后才能將輪胎安裝到車輪上。
let car = new Car().putOnHood().putOnWheels().putOnTires().putOnLights();
現(xiàn)在讓我們看看如何在 D3 中使用方法鏈接。想象一下,我們想要從 DOM 中獲取所有 div 并在每個(gè) div 中添加一個(gè)段落元素。段落元素應(yīng)具有類屬性my-class并包含文本“Wow”。然后,我們要在每個(gè)段落中插入一個(gè) span 元素,并將文本“Even More Wow”以粗體顯示。如果沒(méi)有方法鏈接,我們需要將每個(gè)操作存儲(chǔ)到一個(gè)常量中,然后在執(zhí)行下一個(gè)操作時(shí)調(diào)用該常量,如下所示。光是看著就已經(jīng)很累了……
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");
由于方法鏈接,相同的示例變得更加簡(jiǎn)潔。
d3.selectAll("div").append("p").attr("class", "my-class").text("Wow")
? .append("span").text("Even More Wow").style("font-weight", "900");
在 D3 中,斷行(JavaScript 會(huì)忽略這一點(diǎn))以及縮進(jìn)鏈接方法是很常見(jiàn)的。這使得代碼更容易閱讀,并且縮進(jìn)可以幫助我們看到我們正在處理哪個(gè)元素。
d3.selectAll("div")
.append("p")
.attr("class", "my-class")
.text("Wow")
.append("span")
.text("Even More Wow")
.style("font-weight", "900");
不要擔(dān)心理解前面的代碼示例的作用,盡管您完全可以從不同方法的名稱中猜出它!目前,我們只希望您熟悉如何在 JavaScript 中鏈接方法。我們將在第 2 章中介紹 D3 特定的術(shù)語(yǔ)。
D3 都是關(guān)于數(shù)據(jù)的,而數(shù)據(jù)通常被構(gòu)造為 JavaScript 對(duì)象。了解這些對(duì)象的構(gòu)造以及如何訪問(wèn)和操作它們包含的數(shù)據(jù)將為您構(gòu)建可視化提供巨大幫助。
我們首先討論簡(jiǎn)單數(shù)組,它是元素列表。在與數(shù)據(jù)相關(guān)的項(xiàng)目中,數(shù)組通常是數(shù)字或字符串的有序列表。
const arrayOfNumbers = [17, 82, 9, 500, 40];
const arrayOfStrings = ["blue", "red", "yellow", "orange"];
數(shù)組中的每個(gè)元素都有一個(gè)數(shù)字位置,稱為索引,數(shù)組中第一個(gè)元素的索引為 0。
arrayOfNumbers[0] // => 17
arrayOfStrings[2] // => "yellow"
數(shù)組具有長(zhǎng)度屬性,對(duì)于非稀疏數(shù)組,該屬性指定它們包含的元素?cái)?shù)量。由于數(shù)組是零索引的,因此數(shù)組中最后一個(gè)元素的索引對(duì)應(yīng)于數(shù)組長(zhǎng)度減一。
arrayOfNumbers.length; // => 5
arrayOfStrings[arrayOfStrings.length - 1] // => "orange"
我們還可以使用方法來(lái)確定數(shù)組是否包含特定值includes()。true如果數(shù)組中的元素之一與作為參數(shù)傳遞的值完全對(duì)應(yīng),則此方法返回。否則,它返回false。
arrayOfNumbers.includes(9) // => true
arrayOfStrings.includes("pink") // => false
arrayOfStrings.includes("ellow") // => false
然而,大多數(shù)數(shù)據(jù)集并不是簡(jiǎn)單的數(shù)字或字符串列表,它們的每個(gè)數(shù)據(jù)點(diǎn)通常由多個(gè)屬性組成。讓我們想象一個(gè)虛構(gòu)機(jī)構(gòu)的員工數(shù)據(jù)庫(kù),如表 1.1 所示。該表包含四列:每個(gè)員工的 ID、姓名和職位,以及該員工是否在 D3 工作。
ID | 姓名 | 位置 | 與_d3一起工作 |
1 | 佐伊 | 數(shù)據(jù)分析師 | 錯(cuò)誤的 |
2 | 詹姆士 | 前端開(kāi)發(fā)人員 | 真的 |
3 | 愛(ài)麗絲 | 全棧開(kāi)發(fā)人員 | 真的 |
4 | 休伯特 | 設(shè)計(jì)師 | 錯(cuò)誤的 |
數(shù)據(jù)集中的每一行或數(shù)據(jù)點(diǎn)都可以由 JavaScript 對(duì)象表示,如下所示row1。
const row1 = {
id:"1",
name:"Zoe",
position:"Data analyst",
works_with_d3:false
};
我們可以使用點(diǎn)符號(hào)輕松訪問(wèn)對(duì)象中每個(gè)屬性的值。
row1.name // => "Zoe"
row1.works_with_d3 // => false
我們還可以使用括號(hào)表示法訪問(wèn)這些值。如果屬性名稱包含空格等特殊字符,或者如果我們之前將屬性名稱保存在常量或變量中,那么括號(hào)表示法會(huì)很方便。
row1["position"] // => "Data analyst"
const myProperty = "works_with_d3";
row1[myProperty] // => false
在現(xiàn)實(shí)生活中,數(shù)據(jù)集通常被格式化為對(duì)象數(shù)組。例如,如果我們使用 D3 加載表 1.2 中包含的數(shù)據(jù)集(正如我們將在第 3 章中學(xué)習(xí)的那樣),我們將獲得以下對(duì)象數(shù)組,可以將其保存在名為 的常量中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我們可以使用循環(huán)遍歷數(shù)組中的每個(gè)元素或數(shù)據(jù)點(diǎn)。更具體地說(shuō),JavaScript forEach循環(huán)非常方便且易于編寫和閱讀。迭代數(shù)據(jù)集的一個(gè)常見(jiàn)用例是數(shù)據(jù)整理。當(dāng)我們加載外部 CSV 文件時(shí),數(shù)字通常被格式化為字符串。讓我們以data數(shù)組為例,將屬性的值id從字符串轉(zhuǎn)換為數(shù)字。
在下面的示例中,數(shù)組迭代器使d我們能夠訪問(wèn)每個(gè)對(duì)象。使用點(diǎn)符號(hào),我們id使用運(yùn)算符將?每個(gè)值轉(zhuǎn)換為數(shù)字+。
data.forEach(d => {
d.id = +d.id;
});
JavaScript 提供了許多數(shù)組迭代器方法,可以幫助我們與數(shù)據(jù)交互,甚至在需要時(shí)重塑數(shù)據(jù)。假設(shè)我們想要將數(shù)據(jù)集中的每個(gè)員工定位到可視化上。創(chuàng)建一個(gè)僅包含員工姓名的簡(jiǎn)單數(shù)組可能會(huì)派上用場(chǎng),為此我們將使用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}
];
最后,我們可以通過(guò)該方法找到id為3的員工find()。請(qǐng)注意,該find()方法在找到它要查找的值后停止迭代。我們只能在搜索單個(gè)數(shù)據(jù)點(diǎn)時(shí)使用此方法。
data.find(d => d.id === 3);
// => {id:"3", name:"Alice", position:"Fullstack developer", works_with_d3:true}
本節(jié)討論的方法遠(yuǎn)未涵蓋 JavaScript 提供的所有數(shù)組和對(duì)象操作技術(shù)。但在處理數(shù)據(jù)時(shí),您可能會(huì)不斷回想起它們。每當(dāng)您需要找到另一種方法來(lái)訪問(wèn)或操作數(shù)據(jù)時(shí),MDN Web 文檔 ( https://developer.mozilla.org/ ) 始終是包含大量示例的可靠參考。
JavaScript 在過(guò)去十年中發(fā)生了重大變化。現(xiàn)代 JavaScript 的兩個(gè)最重要的趨勢(shì)是 Node.js 的興起和 JavaScript 框架作為大多數(shù)項(xiàng)目標(biāo)準(zhǔn)的建立。
對(duì)于 D3 項(xiàng)目,我們想了解的主要 Node 技術(shù)是 NPM,即 Node Package Manager。NPM 允許您安裝“模塊”或小型 JavaScript 代碼庫(kù)以在應(yīng)用程序中使用。您不必包含<script>對(duì)單個(gè)文件的一堆標(biāo)記引用,并且如果模塊已構(gòu)建為不是一個(gè)整體結(jié)構(gòu),則可以減少應(yīng)用程序中包含的代碼量。
D3.js 版本 7 于 2021 年中期發(fā)布,利用了模塊導(dǎo)入的優(yōu)勢(shì)。在本書(shū)中,您將看到以兩種方式之一使用 D3 的示例。我們要么加載整個(gè) D3 庫(kù),就像我們?cè)诘?2 章中所做的那樣,要么只包含我們需要的 D3 的各個(gè)部分,正如您將在后面的示例中看到的那樣。我們可以使用腳本標(biāo)簽來(lái)做到這一點(diǎn),但從第 2 部分開(kāi)始,我們將使用 NPM 導(dǎo)入 D3 模塊,因?yàn)檫@被認(rèn)為是當(dāng)今的標(biāo)準(zhǔn)做法。如果您交付專業(yè)的 D3 項(xiàng)目,您可能需要熟悉它。
如果您已經(jīng)參與了專業(yè)的 Web 項(xiàng)目,那么您也很有可能正在使用 JavaScript 框架,例如 React、Angular、Vue 或 Svelte。框架為開(kāi)發(fā)人員提供了使用模塊化、可重用且可測(cè)試的代碼構(gòu)建 Web 項(xiàng)目的基礎(chǔ)。這些框架負(fù)責(zé)構(gòu)建和更新 DOM,這也是 D3 庫(kù)所做的事情。在第 8 章中,我們將討論在 JavaScript 框架內(nèi)構(gòu)建 D3 可視化時(shí)避免沖突的策略。
最后,在專業(yè)的工作環(huán)境中,您可能會(huì)將 D3 與 TypeScript 結(jié)合使用。TypeScript 是 JavaScript 的語(yǔ)法超集,為代碼添加了類型安全性。盡管我們不會(huì)在本書(shū)中詳細(xì)討論它,但 D3 方法的類型可以使用 NPM 包 @type/d3 ( https://www.npmjs.com/package/@types/d3 ) 進(jìn)行安裝。在第 8 章中,我們將在 Angular 項(xiàng)目中使用此類類型。
如果您在網(wǎng)絡(luò)上搜索 D3 項(xiàng)目的示例,您無(wú)疑會(huì)遇到 Observable 筆記本 ( observablehq.com )。Observable 是數(shù)據(jù)科學(xué)和可視化的協(xié)作平臺(tái),類似于 Python 項(xiàng)目的 Jupyter 環(huán)境。Observable 平臺(tái)由 Mike Bostock 創(chuàng)建,取代了之前的在線 D3 沙箱bl.ocks.org 。所有官方的 D3 示例現(xiàn)在都位于 Observable 上,并且 D3 社區(qū)在那里非常活躍。
重要的是要知道 Observable 要求您學(xué)習(xí)一種處理特定于該平臺(tái)的 D3 項(xiàng)目的方法。此外,您不能直接將 Observable Notebook 復(fù)制粘貼到前端開(kāi)發(fā)環(huán)境中(但有多種方法可以導(dǎo)出和重用它們)。由于本書(shū)的重點(diǎn)是在類似于我們?nèi)绾谓桓?D3 項(xiàng)目進(jìn)行生產(chǎn)的環(huán)境中構(gòu)建 D3 可視化,因此我們不會(huì)討論 Observable 筆記本。如果您有興趣學(xué)習(xí) Observable,您可以在observablehq.com/tutorials找到一系列優(yōu)秀的教程。您將在本書(shū)中學(xué)到的大部分技術(shù)和概念都可以轉(zhuǎn)化為 Observable 筆記本。
數(shù)據(jù)可視化從未像今天這樣流行。豐富的地圖、圖表以及系統(tǒng)和數(shù)據(jù)集的復(fù)雜表示不僅存在于工作場(chǎng)所,而且還存在于我們的娛樂(lè)和日常生活中。隨著這種流行,使用視覺(jué)手段以及美學(xué)規(guī)則來(lái)表示數(shù)據(jù)和信息的類和子類庫(kù)不斷增長(zhǎng),以促進(jìn)易讀性和理解性。您的受眾,無(wú)論是公眾、學(xué)者還是決策者,已經(jīng)習(xí)慣了我們?cè)?jīng)認(rèn)為極其抽象和復(fù)雜的數(shù)據(jù)趨勢(shì)表示。這使得 D3 這樣的庫(kù)不僅受到數(shù)據(jù)科學(xué)家的歡迎,而且還受到記者、藝術(shù)家、學(xué)者、IT 專業(yè)人士,甚至數(shù)據(jù)可視化愛(ài)好者的歡迎。
如此豐富的選項(xiàng)似乎讓人不知所措,而且修改數(shù)據(jù)集以顯示在流圖、樹(shù)狀圖或直方圖中相對(duì)容易,這往往會(huì)促進(jìn)這樣一種觀念:信息可視化更多地是關(guān)于風(fēng)格而不是實(shí)質(zhì)內(nèi)容。幸運(yùn)的是,完善的規(guī)則規(guī)定了針對(duì)不同系統(tǒng)的不同數(shù)據(jù)類型使用哪些圖表和方法。本書(shū)并不旨在涵蓋數(shù)據(jù)可視化中的所有最佳實(shí)踐,但我們將介紹其中的一些。盡管開(kāi)發(fā)人員使用 D3 徹底改變了顏色和布局的使用,但大多數(shù)人希望創(chuàng)建支持實(shí)際問(wèn)題的數(shù)據(jù)的可視化表示。
當(dāng)您構(gòu)建第一個(gè)可視化項(xiàng)目時(shí),如果有疑問(wèn),請(qǐng)簡(jiǎn)化 - 通常,呈現(xiàn)直方圖比小提琴圖更好,或者分層網(wǎng)絡(luò)布局(如樹(shù)狀圖)比力導(dǎo)向的網(wǎng)絡(luò)布局更好。視覺(jué)上更復(fù)雜的數(shù)據(jù)顯示方法往往會(huì)激發(fā)更多的興奮,但也會(huì)導(dǎo)致觀眾關(guān)注圖形的美觀而不是數(shù)據(jù)。創(chuàng)建酷炫且令人瞠目結(jié)舌的可視化并沒(méi)有什么錯(cuò),但我們永遠(yuǎn)不應(yīng)該忘記任何數(shù)據(jù)可視化的主要目標(biāo)都是講述一個(gè)故事。詢問(wèn)周圍的人是否理解你的可視化以及他們?nèi)绾谓忉屗侵陵P(guān)重要的一步。他們需要解釋嗎?他們可以從與您的項(xiàng)目的互動(dòng)中得出哪些結(jié)論?故事被講述了嗎?
盡管如此,為了正確部署信息可視化,您應(yīng)該知道該做什么和不該做什么。您需要對(duì)您的數(shù)據(jù)和受眾有深入的了解。D3 賦予我們巨大的靈活性,但正如俗話所說(shuō),“能力越大,責(zé)任越大”。雖然知道某些圖表更適合表示特定類型的數(shù)據(jù)固然很好,但更重要的是要記住,如果不謹(jǐn)慎地從知情的角度構(gòu)建數(shù)據(jù)可視化,則可能會(huì)攜帶錯(cuò)誤信息。如果您打算設(shè)計(jì)自己的可視化,那么了解數(shù)據(jù)可視化最佳實(shí)踐是至關(guān)重要的。了解這一點(diǎn)的最佳方法是回顧知名設(shè)計(jì)師和信息可視化從業(yè)者的工作。盡管整個(gè)圖書(shū)館的作品都在處理這些問(wèn)題,以下是我們發(fā)現(xiàn)的一些有用的內(nèi)容,可以幫助您了解基礎(chǔ)知識(shí)。這些絕不是學(xué)習(xí)數(shù)據(jù)可視化的唯一文本,但它們是一個(gè)很好的起點(diǎn)。
在閱讀有關(guān)數(shù)據(jù)可視化的內(nèi)容時(shí)要記住的一件事是,文獻(xiàn)通常關(guān)注靜態(tài)圖表。使用 D3,您將進(jìn)行交互式動(dòng)態(tài)可視化。一些交互可以使可視化不僅更具可讀性,而且更有吸引力。感覺(jué)自己是在探索而不是閱讀的用戶,即使只是將鼠標(biāo)懸停在事件上幾次或簡(jiǎn)單地單擊進(jìn)行縮放,也可能會(huì)發(fā)現(xiàn)可視化的內(nèi)容比閱讀靜態(tài)等效內(nèi)容更引人注目和更令人難忘。但這種增加的復(fù)雜性需要了解用戶體驗(yàn)。我們將在第 7 章中更詳細(xì)地討論這一點(diǎn)。
我們的第一章到此結(jié)束!盡管我們還沒(méi)有使用過(guò) D3,但您現(xiàn)在已經(jīng)掌握了入門所需的所有知識(shí)。當(dāng)您不確定應(yīng)該在可視化中使用哪個(gè) SVG 元素或者需要提醒如何使用 JavaScript 操作數(shù)據(jù)時(shí),請(qǐng)繼續(xù)返回本章。從下一章開(kāi)始,我們將卷起袖子,創(chuàng)建 D3 可視化。
眾號(hào):
答案:CSS的盒模型是用于布局和定位元素的概念。它由內(nèi)容區(qū)域、內(nèi)邊距、邊框和外邊距組成,這些部分依次包裹在元素周圍。
答案:CSS選擇器用于選擇要應(yīng)用樣式的HTML元素。選擇器的優(yōu)先級(jí)規(guī)則是:內(nèi)聯(lián)樣式 > ID選擇器 > 類選擇器、屬性選擇器、偽類選擇器 > 元素選擇器 > 通用選擇器。同時(shí),使用!important可以提升樣式的優(yōu)先級(jí)。
答案:浮動(dòng)(float)是CSS中用于實(shí)現(xiàn)元素的左浮動(dòng)或右浮動(dòng),使其脫離文檔流并環(huán)繞在其周圍的元素。例如:
.float-example {
float: left;
width: 200px;
height: 200px;
}
答案:定位(position)屬性用于控制元素的定位方式。常見(jiàn)的取值有:static(默認(rèn),按照文檔流定位)、relative(相對(duì)定位)、absolute(絕對(duì)定位)、fixed(固定定位)和sticky(粘性定位)。
答案:層疊順序(z-index)用于控制元素在垂直方向上的堆疊順序。具有較高層疊順序值的元素將顯示在較低層疊順序值的元素之上。默認(rèn)情況下,層疊順序值為auto。
答案:偽類用于向選擇器添加特殊的狀態(tài),如:hover、:active等。偽元素用于向選擇器添加特殊的元素,如::before、::after等。例如:
/* 偽類示例 */
a:hover {
color: red;
}
/* 偽元素示例 */
p::before {
content: "前綴";
}
答案:標(biāo)準(zhǔn)模式是按照W3C標(biāo)準(zhǔn)解析渲染頁(yè)面的模式。怪異模式是兼容舊版本瀏覽器的解析渲染頁(yè)面的模式。可以通過(guò)聲明來(lái)指定使用哪種模式。
答案:BFC(塊級(jí)格式化上下文)是CSS中的一種渲染模式,它創(chuàng)建了一個(gè)獨(dú)立的渲染環(huán)境,其中的元素按照一定的規(guī)則進(jìn)行布局和定位。BFC的作用包括:清除浮動(dòng)、防止外邊距重疊等。
答案:flexbox布局是一種用于創(chuàng)建靈活的、響應(yīng)式的布局的CSS模塊。它通過(guò)flex容器和flex項(xiàng)目的組合來(lái)實(shí)現(xiàn)強(qiáng)大的布局能力。其優(yōu)勢(shì)包括簡(jiǎn)單易用、自適應(yīng)性強(qiáng)、對(duì)齊和分布控制靈活等。
答案:媒體查詢是CSS中的一種技術(shù),用于根據(jù)設(shè)備的特性和屬性來(lái)應(yīng)用不同的樣式。通過(guò)媒體查詢,可以根據(jù)屏幕尺寸、設(shè)備類型、分辨率等條件來(lái)優(yōu)化頁(yè)面的布局和樣式。
答案:JavaScript有七種數(shù)據(jù)類型:字符串(String)、數(shù)字(Number)、布爾值(Boolean)、對(duì)象(Object。Array/數(shù)組 和 function/函數(shù) 也屬于對(duì)象的一種)、空值(Null)、未定義(Undefined)、Symbol(獨(dú)一無(wú)二的值,ES6 新增)、BigInt (大整數(shù),能夠表示超過(guò) Number 類型大小限制的整數(shù),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;
答案:變量提升是指在JavaScript中,變量和函數(shù)聲明會(huì)在代碼執(zhí)行之前被提升到作用域的頂部。這意味著可以在聲明之前使用變量和函數(shù)。例如:
console.log(x); // 輸出 undefined
var x = 5;
答案:閉包是指函數(shù)可以訪問(wèn)并操作其詞法作用域之外的變量。它通過(guò)在函數(shù)內(nèi)部創(chuàng)建一個(gè)內(nèi)部函數(shù),并返回該內(nèi)部函數(shù)來(lái)實(shí)現(xiàn)。例如:
function outer() {
let x = 10;
function inner() {
console.log(x);
}
return inner;
}
let closure = outer();
closure(); // 輸出 10
答案:事件冒泡是指事件從最具體的元素開(kāi)始向父元素逐級(jí)觸發(fā),直到觸發(fā)到根元素。事件捕獲是指事件從根元素開(kāi)始,逐級(jí)向最具體的元素觸發(fā)。可以使用addEventListener方法的第三個(gè)參數(shù)來(lái)控制是使用事件冒泡還是事件捕獲。
答案:原型繼承是JavaScript中實(shí)現(xiàn)對(duì)象之間繼承關(guān)系的一種機(jī)制。每個(gè)對(duì)象都有一個(gè)原型對(duì)象,它包含了共享的屬性和方法。當(dāng)訪問(wèn)對(duì)象的屬性或方法時(shí),如果對(duì)象本身沒(méi)有,則會(huì)沿著原型鏈向上查找。可以使用Object.create()方法或設(shè)置對(duì)象的__proto__屬性來(lái)實(shí)現(xiàn)原型繼承。
答案:異步編程是指在代碼執(zhí)行過(guò)程中,不會(huì)阻塞后續(xù)代碼執(zhí)行的一種編程方式。常見(jiàn)的異步操作包括網(wǎng)絡(luò)請(qǐng)求、定時(shí)器等。例如:
console.log("開(kāi)始");
setTimeout(function() {
console.log("異步操作");
}, 1000);
console.log("結(jié)束");
答案:this關(guān)鍵字在JavaScript中表示當(dāng)前執(zhí)行上下文的對(duì)象。它的具體取值根據(jù)函數(shù)的調(diào)用方式而定。在全局作用域中,this指向全局對(duì)象(瀏覽器環(huán)境中為window對(duì)象)。在函數(shù)中,this的指向取決于函數(shù)的調(diào)用方式,可以通過(guò)call、apply、bind等方法來(lái)顯式地指定this的值。
答案:事件委托是指將事件處理程序綁定到父元素上,而不是直接綁定到每個(gè)子元素上。當(dāng)事件觸發(fā)時(shí),事件會(huì)冒泡到父元素,然后通過(guò)判斷事件的目標(biāo)來(lái)執(zhí)行相應(yīng)的處理邏輯。這樣可以減少事件處理程序的數(shù)量,提高性能。例如:
<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>
答案:模塊化編程是指將代碼劃分為獨(dú)立的模塊,每個(gè)模塊負(fù)責(zé)特定的功能,并通過(guò)導(dǎo)入和導(dǎo)出來(lái)實(shí)現(xiàn)模塊之間的依賴關(guān)系。ES6引入了模塊化的語(yǔ)法,可以使用import和export關(guān)鍵字來(lái)導(dǎo)入和導(dǎo)出模塊。例如:
// module.js
export function sayHello() {
console.log("Hello!");
}
// main.js
import { sayHello } from "./module.js";
sayHello(); // 輸出 "Hello!"
答案:嚴(yán)格模式是一種JavaScript的執(zhí)行模式,它提供了更嚴(yán)格的語(yǔ)法和錯(cuò)誤檢查。在嚴(yán)格模式下,一些不安全或不推薦的語(yǔ)法會(huì)被禁用,同時(shí)會(huì)引入一些新的特性,如變量必須先聲明才能使用、禁止使用this指向全局對(duì)象等。
答案:事件冒泡是指當(dāng)一個(gè)事件在DOM樹(shù)中觸發(fā)時(shí),它會(huì)從最內(nèi)層的元素開(kāi)始向外傳播至最外層的元素。事件捕獲是指當(dāng)一個(gè)事件在DOM樹(shù)中觸發(fā)時(shí),它會(huì)從最外層的元素開(kāi)始向內(nèi)傳播至最內(nèi)層的元素。
答案:原型鏈?zhǔn)荍avaScript中對(duì)象之間的連接關(guān)系,每個(gè)對(duì)象都有一個(gè)指向其原型(prototype)的引用。通過(guò)原型鏈,對(duì)象可以繼承其原型對(duì)象的屬性和方法。可以使用原型鏈實(shí)現(xiàn)繼承,通過(guò)將一個(gè)對(duì)象的原型指向另一個(gè)對(duì)象,從而使得該對(duì)象可以訪問(wèn)另一個(gè)對(duì)象的屬性和方法。
答案:防抖和節(jié)流都是用于控制函數(shù)執(zhí)行頻率的技術(shù)。防抖指的是在某個(gè)時(shí)間段內(nèi),只執(zhí)行最后一次觸發(fā)的函數(shù)調(diào)用。節(jié)流指的是在某個(gè)時(shí)間段內(nèi),按照固定的時(shí)間間隔執(zhí)行函數(shù)調(diào)用。
答案:事件循環(huán)是JavaScript中處理異步操作的機(jī)制。事件循環(huán)不斷地從任務(wù)隊(duì)列中取出任務(wù)并執(zhí)行,直到任務(wù)隊(duì)列為空。事件循環(huán)由主線程和任務(wù)隊(duì)列組成,主線程負(fù)責(zé)執(zhí)行同步任務(wù),異步任務(wù)會(huì)被放入任務(wù)隊(duì)列中,等待主線程空閑時(shí)被執(zhí)行。
答案:深拷貝是指創(chuàng)建一個(gè)新對(duì)象,將原始對(duì)象的所有屬性和嵌套對(duì)象的屬性都復(fù)制到新對(duì)象中。淺拷貝是指創(chuàng)建一個(gè)新對(duì)象,將原始對(duì)象的屬性復(fù)制到新對(duì)象中,但嵌套對(duì)象的引用仍然是共享的。
答案:異步編程是一種處理可能耗時(shí)的操作而不阻塞主線程的編程方式。常見(jiàn)的處理異步操作的方法有回調(diào)函數(shù)、Promise、async/await和事件監(jiān)聽(tīng)等。
答案:變量提升是指在JavaScript中,變量和函數(shù)的聲明會(huì)被提升到當(dāng)前作用域的頂部。這意味著可以在聲明之前使用變量和函數(shù),但它們的賦值或定義仍然在原來(lái)的位置。
答案:柯里化是一種將接受多個(gè)參數(shù)的函數(shù)轉(zhuǎn)換為接受一個(gè)參數(shù)并返回一個(gè)新函數(shù)的過(guò)程。示例:
function add(a) {
return function(b) {
return a + b;
}
}
var add5 = add(5);
console.log(add5(3)); // 輸出:8
答案:TypeScript是JavaScript的超集,它添加了靜態(tài)類型和其他一些特性。TypeScript代碼可以編譯成JavaScript代碼,因此可以在任何支持JavaScript的環(huán)境中運(yùn)行。
答案:類型注解是指在變量、函數(shù)參數(shù)、函數(shù)返回值等地方顯式地聲明類型信息。可以使用冒號(hào)(:)后跟類型來(lái)添加類型注解。例如:
let num: number = 10;
function add(a: number, b: number): number {
return a + b;
}
答案:接口是一種用于定義對(duì)象的結(jié)構(gòu)和類型的語(yǔ)法。可以使用interface關(guān)鍵字來(lái)定義接口。例如:
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!"
答案:類是一種用于創(chuàng)建對(duì)象的藍(lán)圖,它包含屬性和方法。可以使用class關(guān)鍵字來(lái)定義類。例如:
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!"
答案:泛型是一種用于創(chuàng)建可重用代碼的工具,它允許在定義函數(shù)、類或接口時(shí)使用占位符類型。可以使用尖括號(hào)(<>)來(lái)指定泛型類型。例如:
function identity<T>(value: T): T {
return value;
}
let result = identity<string>("Hello");
console.log(result); // 輸出 "Hello"
答案:枚舉是一種用于定義命名常量集合的語(yǔ)法。可以使用enum關(guān)鍵字來(lái)定義枚舉。例如:
enum Color {
Red,
Green,
Blue,
}
let color: Color = Color.Green;
console.log(color); // 輸出 1
答案:模塊是用于組織和封裝代碼的單元。可以使用export關(guān)鍵字將模塊中的變量、函數(shù)、類等導(dǎo)出,以便其他模塊可以使用。可以使用import關(guān)鍵字來(lái)導(dǎo)入其他模塊的導(dǎo)出。例如:
// module.ts
export function greet(name: string) {
console.log(`Hello, ${name}!`);
}
// main.ts
import { greet } from "./module";
greet("John"); // 輸出 "Hello, John!"
答案:類型推斷是指TypeScript根據(jù)上下文自動(dòng)推斷變量的類型,而無(wú)需顯式地添加類型注解。例如:
let num = 10; // 推斷為 number 類型
let str = "Hello"; // 推斷為 string 類型
答案:命名空間是一種用于組織和封裝代碼的機(jī)制,它避免了全局命名沖突。可以使用namespace關(guān)鍵字來(lái)定義命名空間。例如:
namespace MyNamespace {
export function greet(name: string) {
console.log(`Hello, ${name}!`);
}
}
MyNamespace.greet("John"); // 輸出 "Hello, John!"
答案:類型別名是給類型起一個(gè)別名,以便在代碼中更方便地引用。可以使用type關(guān)鍵字來(lái)定義類型別名。例如:
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)"
答案:Vue.js是一個(gè)用于構(gòu)建用戶界面的JavaScript框架。它具有以下特點(diǎn):
響應(yīng)式數(shù)據(jù)綁定:通過(guò)使用Vue的數(shù)據(jù)綁定語(yǔ)法,可以實(shí)現(xiàn)數(shù)據(jù)的自動(dòng)更新。 組件化開(kāi)發(fā):Vue允許將頁(yè)面劃分為獨(dú)立的組件,提高了代碼的可維護(hù)性和復(fù)用性。 虛擬DOM:Vue使用虛擬DOM來(lái)跟蹤頁(yè)面上的變化,并高效地更新實(shí)際的DOM。 指令系統(tǒng):Vue提供了豐富的內(nèi)置指令,用于處理常見(jiàn)的DOM操作和邏輯控制。 生態(tài)系統(tǒng):Vue擁有龐大的生態(tài)系統(tǒng),包括插件、工具和第三方庫(kù),可以滿足各種開(kāi)發(fā)需求。
答案:Vue中的雙向數(shù)據(jù)綁定是通過(guò)v-model指令實(shí)現(xiàn)的。v-model可以在表單元素(如、、)上創(chuàng)建雙向數(shù)據(jù)綁定。當(dāng)用戶輸入改變表單元素的值時(shí),數(shù)據(jù)模型會(huì)自動(dòng)更新;反之,當(dāng)數(shù)據(jù)模型的值改變時(shí),表單元素也會(huì)自動(dòng)更新。
答案:Vue中的生命周期鉤子包括beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy和destroyed。它們的執(zhí)行順序如下:
beforeCreate created beforeMount mounted beforeUpdate updated beforeDestroy destroyed
答案:計(jì)算屬性是基于依賴的屬性,它根據(jù)其依賴的數(shù)據(jù)動(dòng)態(tài)計(jì)算得出值。計(jì)算屬性具有緩存機(jī)制,只有在依賴的數(shù)據(jù)發(fā)生變化時(shí)才會(huì)重新計(jì)算。監(jiān)聽(tīng)器是用于監(jiān)聽(tīng)數(shù)據(jù)的變化并執(zhí)行相應(yīng)的操作。當(dāng)數(shù)據(jù)發(fā)生變化時(shí),監(jiān)聽(tīng)器會(huì)立即執(zhí)行指定的回調(diào)函數(shù)。
答案:Vue中的組件通信方式包括:
父子組件通信:通過(guò)props向子組件傳遞數(shù)據(jù),子組件通過(guò)事件向父組件發(fā)送消息。 子父組件通信:子組件通過(guò)$emit觸發(fā)事件,父組件通過(guò)監(jiān)聽(tīng)事件并響應(yīng)。 兄弟組件通信:通過(guò)共享的父組件來(lái)傳遞數(shù)據(jù)或通過(guò)事件總線(Event Bus)進(jìn)行通信。 跨級(jí)組件通信:通過(guò)provide和inject來(lái)在祖先組件中提供數(shù)據(jù),然后在后代組件中使用。
答案:Vue中的路由是通過(guò)Vue Router實(shí)現(xiàn)的。Vue Router是Vue.js官方提供的路由管理器,它允許開(kāi)發(fā)者在Vue應(yīng)用中實(shí)現(xiàn)單頁(yè)面應(yīng)用(SPA)。Vue Router通過(guò)配置路由映射關(guān)系,將URL路徑與組件進(jìn)行關(guān)聯(lián),并提供導(dǎo)航功能,使用戶可以在不刷新頁(yè)面的情況下切換視圖。
答案:Vue中常用的指令包括:
v-if:根據(jù)表達(dá)式的值條件性地渲染元素。 v-for:根據(jù)數(shù)組或?qū)ο蟮臄?shù)據(jù)進(jìn)行循環(huán)渲染。 v-bind:用于動(dòng)態(tài)綁定屬性或響應(yīng)式地更新屬性。 v-on:用于監(jiān)聽(tīng)DOM事件并執(zhí)行相應(yīng)的方法。 v-model:用于在表單元素上實(shí)現(xiàn)雙向數(shù)據(jù)綁定。 例如:
<div v-if="show">顯示內(nèi)容</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">點(diǎn)擊按鈕</button>
<input v-model="message">
答案:watch和computed都可以用于監(jiān)聽(tīng)數(shù)據(jù)的變化,但它們的用法和實(shí)現(xiàn)方式略有不同。watch用于監(jiān)聽(tīng)指定的數(shù)據(jù)變化,并在數(shù)據(jù)變化時(shí)執(zhí)行相應(yīng)的操作。computed用于根據(jù)依賴的數(shù)據(jù)動(dòng)態(tài)計(jì)算得出一個(gè)新的值,并將該值緩存起來(lái),只有在依賴的數(shù)據(jù)發(fā)生變化時(shí)才會(huì)重新計(jì)算。
答案:Mixin是一種用于在多個(gè)組件之間共享代碼的方式。Mixin可以包含組件選項(xiàng)(如數(shù)據(jù)、方法、生命周期鉤子等),并將其合并到使用Mixin的組件中。這樣可以實(shí)現(xiàn)代碼的復(fù)用和組件的擴(kuò)展,減少重復(fù)編寫相似代碼的工作。
答案:是Vue中的一個(gè)內(nèi)置組件,用于緩存動(dòng)態(tài)組件。當(dāng)組件包裹在中時(shí),組件的狀態(tài)將被保留,包括它的實(shí)例、狀態(tài)和DOM結(jié)構(gòu)。這樣可以避免在組件切換時(shí)重復(fù)創(chuàng)建和銷毀組件,提高性能和用戶體驗(yàn)。
答案:依賴注入是一種設(shè)計(jì)模式,用于將依賴關(guān)系從一個(gè)組件傳遞到另一個(gè)組件。在Vue中,依賴注入通過(guò)provide和inject選項(xiàng)實(shí)現(xiàn)。父組件通過(guò)provide提供數(shù)據(jù),然后子組件通過(guò)inject注入這些數(shù)據(jù)。它在跨多個(gè)層級(jí)的組件通信中非常有用。
答案:渲染函數(shù)是一種用JavaScript代碼編寫組件的方式,它可以動(dòng)態(tài)地生成虛擬DOM。與模板相比,渲染函數(shù)提供了更大的靈活性和控制力,可以處理更復(fù)雜的邏輯和動(dòng)態(tài)渲染需求。
答案:插槽是一種用于在組件中擴(kuò)展內(nèi)容的機(jī)制。命名插槽允許父組件向子組件插入具有特定名稱的內(nèi)容,而作用域插槽允許子組件將數(shù)據(jù)傳遞給父組件。示例:
<!-- 父組件 -->
<template>
<div>
<slot name="header"></slot>
<slot :data="data"></slot>
</div>
</template>
<!-- 子組件 -->
<template>
<div>
<slot name="header">默認(rèn)標(biāo)題</slot>
<slot :data="computedData">{{ computedData }}</slot>
</div>
</template>
答案:Vue.js的動(dòng)畫(huà)系統(tǒng)通過(guò)CSS過(guò)渡和動(dòng)畫(huà)類實(shí)現(xiàn)。通過(guò)在元素上添加過(guò)渡類或動(dòng)畫(huà)類,可以觸發(fā)相應(yīng)的過(guò)渡效果或動(dòng)畫(huà)效果。示例:
<transition name="fade">
<div v-if="show">顯示內(nèi)容</div>
</transition>
<!-- CSS樣式 -->
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
</style>
答案:Vue.js提供了全局的錯(cuò)誤處理機(jī)制和組件級(jí)別的錯(cuò)誤處理機(jī)制。全局錯(cuò)誤處理可以通過(guò)errorCaptured鉤子函數(shù)捕獲和處理錯(cuò)誤。組件級(jí)別的錯(cuò)誤處理可以通過(guò)errorCaptured鉤子函數(shù)或errorHandler選項(xiàng)捕獲和處理錯(cuò)誤。
答案:服務(wù)端渲染是指在服務(wù)器上生成HTML內(nèi)容并將其發(fā)送到瀏覽器進(jìn)行渲染的過(guò)程。Vue.js可以進(jìn)行服務(wù)端渲染,提供更好的首次加載性能和SEO優(yōu)化。然而,服務(wù)端渲染也帶來(lái)了一些限制,如增加了服務(wù)器負(fù)載和開(kāi)發(fā)復(fù)雜性。
答案:Vue.js的響應(yīng)式系統(tǒng)對(duì)于數(shù)組的變異方法(如push、pop、splice等)是無(wú)法追蹤的。為了解決這個(gè)限制,Vue提供了一些特殊的方法,如Vue.set、vm.$set和Array.prototype.splice。這些方法可以用于更新數(shù)組并保持響應(yīng)式。
答案:常見(jiàn)的Vue.js性能優(yōu)化技巧包括:
使用v-if和v-for時(shí)注意避免不必要的渲染。 合理使用computed屬性和watch監(jiān)聽(tīng)器。 使用keep-alive組件緩存組件狀態(tài)。 使用異步組件進(jìn)行按需加載。 避免在模板中使用復(fù)雜的表達(dá)式。 使用key屬性管理組件和元素的復(fù)用。 合理使用懶加載和分割代碼。
答案:Vue.js中的路由導(dǎo)航守衛(wèi)包括全局前置守衛(wèi)、全局解析守衛(wèi)、全局后置守衛(wèi)、路由獨(dú)享守衛(wèi)和組件內(nèi)守衛(wèi)。它們的執(zhí)行順序如下:
全局前置守衛(wèi)(beforeEach) 路由獨(dú)享守衛(wèi)(beforeEnter) 解析守衛(wèi)(beforeResolve) 組件內(nèi)守衛(wèi)(beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave) 全局后置守衛(wèi)(afterEach)
答案:Vue.js的單元測(cè)試可以使用工具如Jest或Mocha進(jìn)行。示例:
// 組件代碼
// 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>
// 單元測(cè)試代碼
// 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)
})
})
答案:Composition API是Vue.js 3中引入的一種新的組織組件邏輯的方式。它允許開(kāi)發(fā)者通過(guò)函數(shù)的方式組織和重用邏輯,而不是通過(guò)選項(xiàng)對(duì)象。相比之下,Options API是Vue.js 2中常用的組織組件邏輯的方式,通過(guò)選項(xiàng)對(duì)象中的屬性來(lái)定義組件的數(shù)據(jù)、方法等。
答案:Teleport是Vue.js 3中引入的一種機(jī)制,用于將組件的內(nèi)容渲染到DOM樹(shù)中的任意位置。示例:
<template>
<div>
<button @click="showModal = true">打開(kāi)模態(tài)框</button>
<teleport to="body">
<modal v-if="showModal" @close="showModal = false">模態(tài)框內(nèi)容</modal>
</teleport>
</div>
</template>
答案:Vue.js 3中的響應(yīng)式系統(tǒng)使用了Proxy對(duì)象來(lái)實(shí)現(xiàn)。與Vue.js 2中的響應(yīng)式系統(tǒng)相比,Vue.js 3的響應(yīng)式系統(tǒng)具有更好的性能和更細(xì)粒度的追蹤,能夠更準(zhǔn)確地檢測(cè)到數(shù)據(jù)的變化,并且支持嵌套的響應(yīng)式數(shù)據(jù)。
答案:Suspense是Vue.js 3中引入的一種機(jī)制,用于處理異步組件的加載狀態(tài)。它可以在異步組件加載完成之前顯示一個(gè)占位符,并在加載完成后渲染異步組件的內(nèi)容。這樣可以更好地處理異步組件的加載過(guò)程,提供更好的用戶體驗(yàn)。
答案:provide和inject用于實(shí)現(xiàn)組件之間的依賴注入。通過(guò)在父組件中使用provide提供數(shù)據(jù),然后在子組件中使用inject注入這些數(shù)據(jù)。示例:
// 父組件
const Parent = {
provide: {
message: 'Hello'
},
// ...
}
// 子組件
const Child = {
inject: ['message'],
created() {
console.log(this.message); // 輸出:Hello
},
// ...
}
答案:Vue.js 3中的動(dòng)畫(huà)系統(tǒng)相比Vue.js 2有以下改進(jìn)之處:
更好的性能:Vue.js 3的動(dòng)畫(huà)系統(tǒng)使用了更高效的動(dòng)畫(huà)引擎,提供了更好的性能。 更簡(jiǎn)潔的語(yǔ)法:Vue.js 3的動(dòng)畫(huà)系統(tǒng)使用了更簡(jiǎn)潔的語(yǔ)法,使得動(dòng)畫(huà)的定義和使用更加直觀和方便。 支持更多的動(dòng)畫(huà)特性:Vue.js 3的動(dòng)畫(huà)系統(tǒng)支持更多的動(dòng)畫(huà)特性,如交互式動(dòng)畫(huà)和更復(fù)雜的動(dòng)畫(huà)效果。 Vue.js 3中的靜態(tài)提升(Static Tree Hoisting)是什么?它有什么優(yōu)勢(shì)? 答案:靜態(tài)提升是Vue.js 3中的一項(xiàng)優(yōu)化技術(shù),通過(guò)在編譯階段將靜態(tài)節(jié)點(diǎn)提升為常量,從而減少了運(yùn)行時(shí)的開(kāi)銷。這項(xiàng)優(yōu)化技術(shù)可以提高組件的渲染性能,并減少生成的代碼體積。
答案:Fragment是Vue.js 3中引入的一種機(jī)制,用于在組件中返回多個(gè)根節(jié)點(diǎn)。在Vue.js 2中,組件的模板只能有一個(gè) Vue.js 3中的Composition API中的ref和reactive有什么區(qū)別?什么時(shí)候使用哪個(gè)? 答案:ref用于創(chuàng)建一個(gè)響應(yīng)式的基本數(shù)據(jù)類型,而reactive用于創(chuàng)建一個(gè)響應(yīng)式的對(duì)象。當(dāng)需要?jiǎng)?chuàng)建一個(gè)簡(jiǎn)單的響應(yīng)式數(shù)據(jù)時(shí),可以使用ref,當(dāng)需要?jiǎng)?chuàng)建一個(gè)包含多個(gè)屬性的響應(yīng)式對(duì)象時(shí),可以使用reactive。
答案:watchEffect用于監(jiān)聽(tīng)響應(yīng)式數(shù)據(jù)的變化,并在回調(diào)函數(shù)中執(zhí)行相應(yīng)的操作。它會(huì)自動(dòng)追蹤依賴,并在依賴變化時(shí)重新運(yùn)行回調(diào)函數(shù)。watch用于監(jiān)聽(tīng)指定的響應(yīng)式數(shù)據(jù),并在其變化時(shí)執(zhí)行相應(yīng)的操作。它可以精確地指定要監(jiān)聽(tīng)的數(shù)據(jù),并提供更多的配置選項(xiàng)。一般來(lái)說(shuō),如果只需要監(jiān)聽(tīng)一個(gè)響應(yīng)式數(shù)據(jù)的變化并執(zhí)行相應(yīng)操作,可以使用watchEffect;如果需要更細(xì)粒度的控制,可以使用watch。
答案:在使用v-model指令時(shí),有以下注意事項(xiàng):
v-model指令必須與一個(gè)表單元素一起使用,如、、等。 當(dāng)使用自定義組件時(shí),組件內(nèi)部必須實(shí)現(xiàn)modelValue屬性和update:modelValue事件,以支持v-model的雙向綁定。 可以使用.lazy修飾符實(shí)現(xiàn)在輸入框失去焦點(diǎn)時(shí)更新數(shù)據(jù)。 可以使用.trim修飾符自動(dòng)去除輸入框內(nèi)容的首尾空格。 可以使用.number修飾符將輸入框的值轉(zhuǎn)換為數(shù)字類型。
答案:默認(rèn)情況下,provide和inject不支持響應(yīng)式數(shù)據(jù)。如果需要在provide中提供一個(gè)響應(yīng)式數(shù)據(jù),可以使用ref或reactive將數(shù)據(jù)包裝起來(lái)。然后在inject中使用toRefs或toRef將數(shù)據(jù)解構(gòu)出來(lái),以獲取響應(yīng)式的引用。
答案:nextTick方法用于在下次DOM更新循環(huán)結(jié)束之后執(zhí)行回調(diào)函數(shù)。它可以用來(lái)確保在更新DOM后執(zhí)行某些操作,如操作更新后的DOM元素或獲取更新后的計(jì)算屬性的值。通常在需要等待DOM更新完成后進(jìn)行操作的情況下使用nextTick。
答案:組件用于將組件的內(nèi)容渲染到DOM樹(shù)中的任意位置,而組件用于在組件進(jìn)入或離開(kāi)DOM樹(shù)時(shí)應(yīng)用過(guò)渡效果。主要用于組件的位置移動(dòng),而主要用于組件的顯示和隱藏過(guò)渡。
答案:v-for指令中的key屬性用于給每個(gè)迭代項(xiàng)設(shè)置一個(gè)唯一的標(biāo)識(shí)符。它的作用是幫助Vue.js跟蹤每個(gè)節(jié)點(diǎn)的身份,以便在數(shù)據(jù)發(fā)生變化時(shí)高效地更新DOM。使用key屬性可以避免出現(xiàn)錯(cuò)誤的節(jié)點(diǎn)更新或重新排序的問(wèn)題。
答案:React是一個(gè)用于構(gòu)建用戶界面的JavaScript庫(kù)。它的核心概念是組件化和聲明式編程。React將用戶界面拆分為獨(dú)立的可重用組件,并使用聲明式語(yǔ)法描述組件的狀態(tài)和UI的關(guān)系,使得構(gòu)建復(fù)雜的UI變得簡(jiǎn)單和可維護(hù)。
答案:JSX是一種JavaScript的語(yǔ)法擴(kuò)展,用于在React中描述UI的結(jié)構(gòu)。它類似于HTML,但有一些區(qū)別:
答案:React組件是構(gòu)建用戶界面的獨(dú)立單元。React組件有兩種類型:
函數(shù)組件:使用函數(shù)來(lái)定義組件,接收props作為參數(shù),并返回一個(gè)React元素。 類組件:使用ES6類來(lái)定義組件,繼承自React.Component類,通過(guò)render方法返回一個(gè)React元素。
答案:狀態(tài)(state)是組件自身管理的數(shù)據(jù),可以通過(guò)setState方法來(lái)更新。屬性(props)是從父組件傳遞給子組件的數(shù)據(jù),子組件無(wú)法直接修改props,只能通過(guò)父組件的更新來(lái)改變props。
區(qū)別:
狀態(tài)(state)是組件內(nèi)部的數(shù)據(jù),可以在組件中自由修改和管理。 屬性(props)是從父組件傳遞給子組件的數(shù)據(jù),子組件無(wú)法直接修改,只能接收和使用。
答案:React生命周期方法是在組件不同階段執(zhí)行的特定方法。以下是一些常用的React生命周期方法:
componentDidMount:組件掛載后立即調(diào)用。 componentDidUpdate:組件更新后調(diào)用。 componentWillUnmount:組件卸載前調(diào)用。 shouldComponentUpdate:決定組件是否需要重新渲染。 getDerivedStateFromProps:根據(jù)props的變化來(lái)更新?tīng)顟B(tài)。
答案:React Hooks是React 16.8版本引入的一種特性,用于在函數(shù)組件中使用狀態(tài)和其他React特性。Hooks提供了一種無(wú)需編寫類組件的方式來(lái)管理狀態(tài)和處理副作用,使得函數(shù)組件具有類組件的能力。
答案:React Router是React中用于處理路由的庫(kù)。它提供了一種在單頁(yè)面應(yīng)用中實(shí)現(xiàn)導(dǎo)航和路由功能的方式。React Router可以幫助開(kāi)發(fā)者實(shí)現(xiàn)頁(yè)面之間的切換、URL參數(shù)的傳遞、嵌套路由等功能。
答案:React Context是一種用于在組件樹(shù)中共享數(shù)據(jù)的機(jī)制。它可以避免通過(guò)props一層層傳遞數(shù)據(jù),使得跨組件的數(shù)據(jù)共享變得更加簡(jiǎn)單和高效。React Context提供了一個(gè)Provider和Consumer組件,用于提供和消費(fèi)共享的數(shù)據(jù)。
答案:React的協(xié)調(diào)過(guò)程是指React在進(jìn)行組件更新時(shí),通過(guò)比較新舊虛擬DOM樹(shù)的差異,僅對(duì)需要更新的部分進(jìn)行實(shí)際的DOM操作。協(xié)調(diào)過(guò)程的工作方式如下:
React會(huì)逐層比較新舊虛擬DOM樹(shù)的節(jié)點(diǎn),并找出差異。 對(duì)于每個(gè)差異,React會(huì)生成相應(yīng)的DOM操作指令,如插入、更新或刪除節(jié)點(diǎn)。 React會(huì)將所有的DOM操作指令批量執(zhí)行,以減少對(duì)真實(shí)DOM的操作次數(shù)。
答案:React的事件合成是一種在React中處理事件的機(jī)制。它是React為了提高性能和跨瀏覽器兼容性而實(shí)現(xiàn)的一種事件系統(tǒng)。事件合成的作用包括:
提供了一種統(tǒng)一的方式來(lái)處理事件,無(wú)需考慮瀏覽器兼容性。 可以通過(guò)事件委托的方式將事件處理程序綁定到父組件,提高性能。 可以訪問(wèn)原生事件對(duì)象的屬性和方法。
答案:React的Fiber架構(gòu)是React 16版本引入的一種新的協(xié)調(diào)算法和架構(gòu)。它旨在解決長(zhǎng)時(shí)間渲染阻塞主線程的問(wèn)題,提高應(yīng)用的性能和用戶體驗(yàn)。Fiber架構(gòu)通過(guò)將渲染過(guò)程分解為多個(gè)小任務(wù),并使用優(yōu)先級(jí)調(diào)度算法來(lái)動(dòng)態(tài)分配時(shí)間片,使得React可以在每個(gè)幀中執(zhí)行一部分任務(wù),從而實(shí)現(xiàn)平滑的用戶界面和更好的響應(yīng)性能。
答案:React的錯(cuò)誤邊界是一種用于處理組件錯(cuò)誤的機(jī)制。它允許組件捕獲并處理其子組件中發(fā)生的JavaScript錯(cuò)誤,以避免整個(gè)應(yīng)用崩潰。錯(cuò)誤邊界的作用包括:
捕獲并處理組件樹(shù)中的錯(cuò)誤,防止錯(cuò)誤導(dǎo)致整個(gè)應(yīng)用崩潰。 提供一種優(yōu)雅的方式來(lái)顯示錯(cuò)誤信息或備用UI。 可以用于記錄錯(cuò)誤和發(fā)送錯(cuò)誤報(bào)告。
答案:HTTP(Hypertext Transfer Protocol)是一種用于在Web上傳輸數(shù)據(jù)的協(xié)議。它使用客戶端-服務(wù)器模型,客戶端發(fā)送HTTP請(qǐng)求到服務(wù)器,服務(wù)器返回HTTP響應(yīng)。HTTP的工作流程如下:
客戶端發(fā)送HTTP請(qǐng)求到指定的URL。 服務(wù)器接收請(qǐng)求并處理,然后返回HTTP響應(yīng)。 客戶端接收響應(yīng)并解析,從中獲取所需的數(shù)據(jù)。
答案:HTTPS(Hypertext Transfer Protocol Secure)是HTTP的安全版本,通過(guò)使用SSL(Secure Sockets Layer)或TLS(Transport Layer Security)協(xié)議對(duì)通信進(jìn)行加密和身份驗(yàn)證。與HTTP相比,HTTPS具有以下區(qū)別:
數(shù)據(jù)在傳輸過(guò)程中通過(guò)加密進(jìn)行保護(hù),提供更高的安全性。 使用數(shù)字證書(shū)對(duì)服務(wù)器進(jìn)行身份驗(yàn)證,防止中間人攻擊。 使用默認(rèn)端口443。
答案:跨域請(qǐng)求是指在瀏覽器中向不同域名、端口或協(xié)議發(fā)送的請(qǐng)求。由于瀏覽器的同源策略(Same-Origin Policy)限制,跨域請(qǐng)求會(huì)受到限制。為了解決跨域問(wèn)題,可以使用以下方法:
JSONP(JSON with Padding):通過(guò)動(dòng)態(tài)創(chuàng)建
答案:緩存是將數(shù)據(jù)或資源存儲(chǔ)在臨時(shí)存儲(chǔ)中,以便在后續(xù)請(qǐng)求中重復(fù)使用,從而提高性能和減少網(wǎng)絡(luò)流量。在前端中,可以使用以下方式來(lái)利用緩存:
HTTP緩存:通過(guò)設(shè)置適當(dāng)?shù)木彺骖^(如Cache-Control和Expires)來(lái)指示瀏覽器緩存響應(yīng)。 資源緩存:使用文件指紋或版本號(hào)來(lái)重命名靜態(tài)資源文件,以便在文件內(nèi)容變化時(shí)使瀏覽器重新下載。 數(shù)據(jù)緩存:使用內(nèi)存緩存、瀏覽器本地存儲(chǔ)(如localStorage)或服務(wù)端緩存(如Redis)來(lái)存儲(chǔ)數(shù)據(jù),避免重復(fù)請(qǐng)求。
答案:CDN(Content Delivery Network)是一種分布式網(wǎng)絡(luò)架構(gòu),用于在全球各地提供高性能、低延遲的內(nèi)容傳輸服務(wù)。CDN的作用包括:
將靜態(tài)資源(如圖片、樣式表、腳本等)緩存到離用戶更近的服務(wù)器上,提供更快的加載速度。 分發(fā)網(wǎng)絡(luò)流量,減輕源服務(wù)器的負(fù)載壓力。 提供內(nèi)容壓縮、數(shù)據(jù)壓縮和緩存等優(yōu)化技術(shù),提高用戶體驗(yàn)。
答案:網(wǎng)頁(yè)加載性能優(yōu)化是指通過(guò)各種技術(shù)手段來(lái)減少網(wǎng)頁(yè)加載時(shí)間并提高用戶體驗(yàn)。可以采取以下措施來(lái)改善網(wǎng)頁(yè)加載性能:
壓縮和合并資源文件(如CSS和JavaScript),減少文件大小和請(qǐng)求數(shù)量。 使用圖像壓縮和適當(dāng)?shù)母袷竭x擇來(lái)減小圖像文件大小。 使用瀏覽器緩存和HTTP緩存頭來(lái)緩存靜態(tài)資源。 使用懶加載延遲加載非關(guān)鍵資源,提高初始加載速度。 使用CDN(內(nèi)容分發(fā)網(wǎng)絡(luò))來(lái)分發(fā)靜態(tài)資源,減少網(wǎng)絡(luò)延遲。 優(yōu)化關(guān)鍵渲染路徑,盡早呈現(xiàn)頁(yè)面內(nèi)容。
答案:網(wǎng)頁(yè)性能監(jiān)測(cè)和分析是指通過(guò)測(cè)量和收集有關(guān)網(wǎng)頁(yè)加載和交互性能的數(shù)據(jù),以便識(shí)別性能瓶頸并進(jìn)行優(yōu)化。可以使用以下工具來(lái)監(jiān)測(cè)和分析網(wǎng)頁(yè)性能:
Web性能API:瀏覽器提供的JavaScript API,可通過(guò)performance對(duì)象來(lái)收集性能數(shù)據(jù)。 Lighthouse:一種開(kāi)源工具,可提供關(guān)于網(wǎng)頁(yè)性能、可訪問(wèn)性和最佳實(shí)踐的綜合報(bào)告。 WebPagetest:在線工具,可測(cè)量網(wǎng)頁(yè)加載時(shí)間并提供詳細(xì)的性能分析報(bào)告。 Chrome開(kāi)發(fā)者工具:瀏覽器內(nèi)置的開(kāi)發(fā)者工具,提供了性能分析、網(wǎng)絡(luò)監(jiān)控和頁(yè)面審查等功能。
答案:漸進(jìn)式圖像加載是一種技術(shù),通過(guò)逐步加載圖像的模糊或低分辨率版本,然后逐漸提高圖像的清晰度,以改善網(wǎng)頁(yè)加載性能和用戶體驗(yàn)。漸進(jìn)式圖像加載的好處包括:
用戶可以更快地看到頁(yè)面內(nèi)容,提高感知速度。 逐步加載圖像可以減少網(wǎng)頁(yè)整體的加載時(shí)間。 漸進(jìn)式圖像加載可以提供平滑的過(guò)渡效果,避免頁(yè)面內(nèi)容突然閃爍或變化。
答案:前端資源優(yōu)先級(jí)是指為不同類型的資源分配加載優(yōu)先級(jí),以優(yōu)化網(wǎng)頁(yè)加載性能。可以使用以下方法設(shè)置資源的優(yōu)先級(jí):
使用標(biāo)簽來(lái)指定資源的預(yù)加載,以確保關(guān)鍵資源盡早加載。 使用標(biāo)簽來(lái)指定可能在未來(lái)頁(yè)面中使用的資源,以提前加載。 使用標(biāo)簽來(lái)指定要預(yù)解析的域名,以減少DNS查找時(shí)間。 使用標(biāo)簽來(lái)指定要預(yù)連接的域名,以減少建立連接的時(shí)間。
答案:瀏覽器的工作原理包括以下幾個(gè)關(guān)鍵步驟:
解析:瀏覽器將接收到的HTML、CSS和JavaScript代碼解析成DOM樹(shù)、CSSOM樹(shù)和JavaScript引擎可執(zhí)行的代碼。 渲染:瀏覽器使用DOM樹(shù)和CSSOM樹(shù)構(gòu)建渲染樹(shù),然后根據(jù)渲染樹(shù)進(jìn)行布局(計(jì)算元素的位置和大小)和繪制(將元素繪制到屏幕上)。 布局和繪制:瀏覽器根據(jù)渲染樹(shù)的變化進(jìn)行布局和繪制,然后將最終的頁(yè)面呈現(xiàn)給用戶。 JavaScript引擎執(zhí)行:瀏覽器的JavaScript引擎解釋和執(zhí)行JavaScript代碼,并根據(jù)需要更新渲染樹(shù)和重新渲染頁(yè)面。
答案:重繪是指當(dāng)元素的外觀(如顏色、背景等)發(fā)生改變,但布局不受影響時(shí)的更新過(guò)程。重繪不會(huì)導(dǎo)致元素的位置或大小發(fā)生變化。
重排是指當(dāng)元素的布局屬性(如寬度、高度、位置等)發(fā)生改變時(shí)的更新過(guò)程。重排會(huì)導(dǎo)致瀏覽器重新計(jì)算渲染樹(shù)和重新繪制頁(yè)面的一部分或全部。
區(qū)別在于重繪只涉及外觀的更改,而重排涉及布局的更改。重排比重繪更消耗性能,因?yàn)樗枰匦掠?jì)算布局和繪制整個(gè)頁(yè)面。
答案:事件冒泡和事件捕獲是指瀏覽器處理事件時(shí)的兩種不同的傳播方式。
事件冒泡是指事件從最內(nèi)層的元素開(kāi)始觸發(fā),然后逐級(jí)向上傳播到父元素,直到傳播到最外層的元素。
事件捕獲是指事件從最外層的元素開(kāi)始觸發(fā),然后逐級(jí)向下傳播到最內(nèi)層的元素。
區(qū)別在于傳播方向的不同。事件冒泡是從內(nèi)向外傳播,而事件捕獲是從外向內(nèi)傳播。
答案:同步代碼是按照順序執(zhí)行的代碼,每個(gè)任務(wù)必須等待前一個(gè)任務(wù)完成后才能執(zhí)行。同步代碼會(huì)阻塞后續(xù)代碼的執(zhí)行,直到當(dāng)前任務(wù)完成。
異步代碼是不按照順序執(zhí)行的代碼,它會(huì)在后臺(tái)執(zhí)行,不會(huì)阻塞后續(xù)代碼的執(zhí)行。異步代碼通常使用回調(diào)函數(shù)、Promise、async/await等方式來(lái)處理異步操作的結(jié)果。
通過(guò)異步執(zhí)行,可以避免阻塞主線程,提高頁(yè)面的響應(yīng)性能。
答案:事件循環(huán)是JavaScript中處理異步代碼執(zhí)行的機(jī)制。它負(fù)責(zé)管理調(diào)度和執(zhí)行異步任務(wù),并將它們添加到執(zhí)行隊(duì)列中。
在JavaScript中,事件循環(huán)的作用是確保異步任務(wù)按照正確的順序執(zhí)行,并且不會(huì)阻塞主線程。它通過(guò)不斷地從執(zhí)行隊(duì)列中取出任務(wù)并執(zhí)行,以實(shí)現(xiàn)非阻塞的異步操作。
答案:瀏覽器的垃圾回收機(jī)制是一種自動(dòng)管理內(nèi)存的機(jī)制,用于檢測(cè)和回收不再使用的對(duì)象,以釋放內(nèi)存資源。
垃圾回收機(jī)制通過(guò)標(biāo)記-清除算法實(shí)現(xiàn)。它的工作原理如下:
標(biāo)記階段:垃圾回收器會(huì)從根對(duì)象(如全局對(duì)象)開(kāi)始,遞歸遍歷所有對(duì)象,并標(biāo)記仍然可訪問(wèn)的對(duì)象。 清除階段:垃圾回收器會(huì)掃描堆內(nèi)存,清除未被標(biāo)記的對(duì)象,并回收它們所占用的內(nèi)存空間。 垃圾回收機(jī)制的目標(biāo)是識(shí)別和回收不再使用的對(duì)象,以避免內(nèi)存泄漏和提高內(nèi)存利用率。
答案:同源策略是瀏覽器的一項(xiàng)安全機(jī)制,用于限制來(lái)自不同源的網(wǎng)頁(yè)之間的交互。同源是指協(xié)議、域名和端口號(hào)完全相同。
同源策略的限制包括:
腳本訪問(wèn)限制:不同源的腳本無(wú)法直接訪問(wèn)彼此的數(shù)據(jù)和操作。 DOM訪問(wèn)限制:不同源的網(wǎng)頁(yè)無(wú)法通過(guò)JavaScript訪問(wèn)彼此的DOM元素。 Cookie限制:不同源的網(wǎng)頁(yè)無(wú)法讀取或修改彼此的Cookie。 AJAX請(qǐng)求限制:不同源的網(wǎng)頁(yè)無(wú)法通過(guò)AJAX請(qǐng)求訪問(wèn)彼此的數(shù)據(jù)。 同源策略的存在可以防止惡意網(wǎng)站獲取用戶的敏感信息或進(jìn)行惡意操作。
答案:Web Workers是一種瀏覽器提供的JavaScript API,用于在后臺(tái)線程中執(zhí)行耗時(shí)的計(jì)算任務(wù),以避免阻塞主線程。
Web Workers的作用是提高瀏覽器的響應(yīng)性能,使得在執(zhí)行復(fù)雜計(jì)算或處理大量數(shù)據(jù)時(shí),不會(huì)影響用戶界面的流暢性。
Web Workers通過(guò)將任務(wù)委托給后臺(tái)線程來(lái)實(shí)現(xiàn)并行處理,從而充分利用多核處理器的能力。它們可以與主線程進(jìn)行通信,但不能直接訪問(wèn)DOM或執(zhí)行UI相關(guān)的操作。
答案:瀏覽器緩存是瀏覽器在本地存儲(chǔ)Web頁(yè)面和資源的副本,以便在后續(xù)訪問(wèn)時(shí)可以快速加載。它的作用是減少對(duì)服務(wù)器的請(qǐng)求次數(shù)和網(wǎng)絡(luò)傳輸量,提高頁(yè)面加載速度和用戶體驗(yàn)。
瀏覽器緩存通過(guò)在首次請(qǐng)求時(shí)將資源保存到本地,并在后續(xù)請(qǐng)求時(shí)檢查資源是否已經(jīng)存在并且沒(méi)有過(guò)期來(lái)工作。如果資源已經(jīng)存在且未過(guò)期,瀏覽器會(huì)直接從緩存中加載資源,而不是從服務(wù)器重新下載。
答案:重定向是指當(dāng)瀏覽器請(qǐng)求一個(gè)URL時(shí),服務(wù)器返回一個(gè)不同的URL,從而將瀏覽器的請(qǐng)求重定向到新的URL上。
重定向在瀏覽器中的作用是實(shí)現(xiàn)頁(yè)面的跳轉(zhuǎn)、URL的修改或資源的重定向。它可以用于多種情況,例如處理舊鏈接的跳轉(zhuǎn)、實(shí)現(xiàn)URL的規(guī)范化、處理用戶認(rèn)證等。
重定向通過(guò)在HTTP響應(yīng)中設(shè)置特定的狀態(tài)碼(如301永久重定向、302臨時(shí)重定向)和Location頭部字段來(lái)實(shí)現(xiàn)。
答案:瀏覽器存儲(chǔ)是瀏覽器提供的一種在客戶端存儲(chǔ)數(shù)據(jù)的機(jī)制,用于在不同的網(wǎng)頁(yè)間共享數(shù)據(jù)或持久保存數(shù)據(jù)。
瀏覽器存儲(chǔ)有以下不同的存儲(chǔ)機(jī)制:
Cookie:小型文本文件,可以存儲(chǔ)少量數(shù)據(jù),并在每次HTTP請(qǐng)求中自動(dòng)發(fā)送到服務(wù)器。 Web Storage(localStorage和sessionStorage):可以存儲(chǔ)較大量的數(shù)據(jù),以鍵值對(duì)的形式存儲(chǔ)在瀏覽器中。 IndexedDB:一種高級(jí)的客戶端數(shù)據(jù)庫(kù),可以存儲(chǔ)大量結(jié)構(gòu)化數(shù)據(jù),并支持索引和事務(wù)操作。 Cache API:用于緩存網(wǎng)絡(luò)請(qǐng)求的響應(yīng),以便離線訪問(wèn)或提高頁(yè)面加載速度。 不同的存儲(chǔ)機(jī)制適用于不同的需求,開(kāi)發(fā)者可以根據(jù)具體情況選擇合適的存儲(chǔ)方式。
原文鏈接:https://juejin.cn/post/7276407803618656295
2023金九銀十必看前端面試題! 金九銀十黃金期來(lái)了 想要跳槽的小伙伴快來(lái)看啊
答案:CSS的盒模型是用于布局和定位元素的概念。它由內(nèi)容區(qū)域、內(nèi)邊距、邊框和外邊距組成,這些部分依次包裹在元素周圍。
答案:CSS選擇器用于選擇要應(yīng)用樣式的HTML元素。選擇器的優(yōu)先級(jí)規(guī)則是:內(nèi)聯(lián)樣式 > ID選擇器 > 類選擇器、屬性選擇器、偽類選擇器 > 元素選擇器 > 通用選擇器。同時(shí),使用!important可以提升樣式的優(yōu)先級(jí)。
答案:浮動(dòng)(float)是CSS中用于實(shí)現(xiàn)元素的左浮動(dòng)或右浮動(dòng),使其脫離文檔流并環(huán)繞在其周圍的元素。例如:
.float-example {
float: left;
width: 200px;
height: 200px;
}
答案:定位(position)屬性用于控制元素的定位方式。常見(jiàn)的取值有:static(默認(rèn),按照文檔流定位)、relative(相對(duì)定位)、absolute(絕對(duì)定位)、fixed(固定定位)和sticky(粘性定位)。
答案:層疊順序(z-index)用于控制元素在垂直方向上的堆疊順序。具有較高層疊順序值的元素將顯示在較低層疊順序值的元素之上。默認(rèn)情況下,層疊順序值為auto。
答案:偽類用于向選擇器添加特殊的狀態(tài),如:hover、:active等。偽元素用于向選擇器添加特殊的元素,如::before、::after等。例如:
/* 偽類示例 */
a:hover {
color: red;
}
/* 偽元素示例 */
p::before {
content: "前綴";
}
答案:標(biāo)準(zhǔn)模式是按照W3C標(biāo)準(zhǔn)解析渲染頁(yè)面的模式。怪異模式是兼容舊版本瀏覽器的解析渲染頁(yè)面的模式。可以通過(guò)聲明來(lái)指定使用哪種模式。
答案:BFC(塊級(jí)格式化上下文)是CSS中的一種渲染模式,它創(chuàng)建了一個(gè)獨(dú)立的渲染環(huán)境,其中的元素按照一定的規(guī)則進(jìn)行布局和定位。BFC的作用包括:清除浮動(dòng)、防止外邊距重疊等。
答案:flexbox布局是一種用于創(chuàng)建靈活的、響應(yīng)式的布局的CSS模塊。它通過(guò)flex容器和flex項(xiàng)目的組合來(lái)實(shí)現(xiàn)強(qiáng)大的布局能力。其優(yōu)勢(shì)包括簡(jiǎn)單易用、自適應(yīng)性強(qiáng)、對(duì)齊和分布控制靈活等。
答案:媒體查詢是CSS中的一種技術(shù),用于根據(jù)設(shè)備的特性和屬性來(lái)應(yīng)用不同的樣式。通過(guò)媒體查詢,可以根據(jù)屏幕尺寸、設(shè)備類型、分辨率等條件來(lái)優(yōu)化頁(yè)面的布局和樣式。
答案:JavaScript有七種數(shù)據(jù)類型:字符串(String)、數(shù)字(Number)、布爾值(Boolean)、對(duì)象(Object)、數(shù)組(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;
答案:變量提升是指在JavaScript中,變量和函數(shù)聲明會(huì)在代碼執(zhí)行之前被提升到作用域的頂部。這意味著可以在聲明之前使用變量和函數(shù)。例如:
console.log(x); // 輸出 undefined
var x = 5;
答案:閉包是指函數(shù)可以訪問(wèn)并操作其詞法作用域之外的變量。它通過(guò)在函數(shù)內(nèi)部創(chuàng)建一個(gè)內(nèi)部函數(shù),并返回該內(nèi)部函數(shù)來(lái)實(shí)現(xiàn)。例如:
function outer() {
let x = 10;
function inner() {
console.log(x);
}
return inner;
}
let closure = outer();
closure(); // 輸出 10
答案:事件冒泡是指事件從最具體的元素開(kāi)始向父元素逐級(jí)觸發(fā),直到觸發(fā)到根元素。事件捕獲是指事件從根元素開(kāi)始,逐級(jí)向最具體的元素觸發(fā)。可以使用addEventListener方法的第三個(gè)參數(shù)來(lái)控制是使用事件冒泡還是事件捕獲。
答案:原型繼承是JavaScript中實(shí)現(xiàn)對(duì)象之間繼承關(guān)系的一種機(jī)制。每個(gè)對(duì)象都有一個(gè)原型對(duì)象,它包含了共享的屬性和方法。當(dāng)訪問(wèn)對(duì)象的屬性或方法時(shí),如果對(duì)象本身沒(méi)有,則會(huì)沿著原型鏈向上查找。可以使用Object.create()方法或設(shè)置對(duì)象的__proto__屬性來(lái)實(shí)現(xiàn)原型繼承。
答案:異步編程是指在代碼執(zhí)行過(guò)程中,不會(huì)阻塞后續(xù)代碼執(zhí)行的一種編程方式。常見(jiàn)的異步操作包括網(wǎng)絡(luò)請(qǐng)求、定時(shí)器等。例如:
console.log("開(kāi)始");
setTimeout(function() {
console.log("異步操作");
}, 1000);
console.log("結(jié)束");
答案:閉包是指函數(shù)可以訪問(wèn)并操作其詞法作用域之外的變量。它通過(guò)在函數(shù)內(nèi)部創(chuàng)建一個(gè)內(nèi)部函數(shù),并返回該內(nèi)部函數(shù)來(lái)實(shí)現(xiàn)。例如:
function outer() {
let x = 10;
function inner() {
console.log(x);
}
return inner;
}
let closure = outer();
closure(); // 輸出 10
答案:this關(guān)鍵字在JavaScript中表示當(dāng)前執(zhí)行上下文的對(duì)象。它的具體取值根據(jù)函數(shù)的調(diào)用方式而定。在全局作用域中,this指向全局對(duì)象(瀏覽器環(huán)境中為window對(duì)象)。在函數(shù)中,this的指向取決于函數(shù)的調(diào)用方式,可以通過(guò)call、apply、bind等方法來(lái)顯式地指定this的值。
答案:事件委托是指將事件處理程序綁定到父元素上,而不是直接綁定到每個(gè)子元素上。當(dāng)事件觸發(fā)時(shí),事件會(huì)冒泡到父元素,然后通過(guò)判斷事件的目標(biāo)來(lái)執(zhí)行相應(yīng)的處理邏輯。這樣可以減少事件處理程序的數(shù)量,提高性能。例如:
<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>
答案:模塊化編程是指將代碼劃分為獨(dú)立的模塊,每個(gè)模塊負(fù)責(zé)特定的功能,并通過(guò)導(dǎo)入和導(dǎo)出來(lái)實(shí)現(xiàn)模塊之間的依賴關(guān)系。ES6引入了模塊化的語(yǔ)法,可以使用import和export關(guān)鍵字來(lái)導(dǎo)入和導(dǎo)出模塊。例如:
// module.js
export function sayHello() {
console.log("Hello!");
}
// main.js
import { sayHello } from "./module.js";
sayHello(); // 輸出 "Hello!"
答案:事件冒泡是指當(dāng)一個(gè)事件在DOM樹(shù)中觸發(fā)時(shí),它會(huì)從最內(nèi)層的元素開(kāi)始向外傳播至最外層的元素。事件捕獲是指當(dāng)一個(gè)事件在DOM樹(shù)中觸發(fā)時(shí),它會(huì)從最外層的元素開(kāi)始向內(nèi)傳播至最內(nèi)層的元素。
答案:原型鏈?zhǔn)荍avaScript中對(duì)象之間的連接關(guān)系,每個(gè)對(duì)象都有一個(gè)指向其原型(prototype)的引用。通過(guò)原型鏈,對(duì)象可以繼承其原型對(duì)象的屬性和方法。可以使用原型鏈實(shí)現(xiàn)繼承,通過(guò)將一個(gè)對(duì)象的原型指向另一個(gè)對(duì)象,從而使得該對(duì)象可以訪問(wèn)另一個(gè)對(duì)象的屬性和方法。
答案:防抖和節(jié)流都是用于控制函數(shù)執(zhí)行頻率的技術(shù)。防抖指的是在某個(gè)時(shí)間段內(nèi),只執(zhí)行最后一次觸發(fā)的函數(shù)調(diào)用。節(jié)流指的是在某個(gè)時(shí)間段內(nèi),按照固定的時(shí)間間隔執(zhí)行函數(shù)調(diào)用。
答案:事件循環(huán)是JavaScript中處理異步操作的機(jī)制。事件循環(huán)不斷地從任務(wù)隊(duì)列中取出任務(wù)并執(zhí)行,直到任務(wù)隊(duì)列為空。事件循環(huán)由主線程和任務(wù)隊(duì)列組成,主線程負(fù)責(zé)執(zhí)行同步任務(wù),異步任務(wù)會(huì)被放入任務(wù)隊(duì)列中,等待主線程空閑時(shí)被執(zhí)行。
答案:深拷貝是指創(chuàng)建一個(gè)新對(duì)象,將原始對(duì)象的所有屬性和嵌套對(duì)象的屬性都復(fù)制到新對(duì)象中。淺拷貝是指創(chuàng)建一個(gè)新對(duì)象,將原始對(duì)象的屬性復(fù)制到新對(duì)象中,但嵌套對(duì)象的引用仍然是共享的。
答案:異步編程是一種處理可能耗時(shí)的操作而不阻塞主線程的編程方式。常見(jiàn)的處理異步操作的方法有回調(diào)函數(shù)、Promise、async/await和事件監(jiān)聽(tīng)等。
答案:變量提升是指在JavaScript中,變量和函數(shù)的聲明會(huì)被提升到當(dāng)前作用域的頂部。這意味著可以在聲明之前使用變量和函數(shù),但它們的賦值或定義仍然在原來(lái)的位置。
答案:柯里化是一種將接受多個(gè)參數(shù)的函數(shù)轉(zhuǎn)換為接受一個(gè)參數(shù)并返回一個(gè)新函數(shù)的過(guò)程。示例:
function add(a) {
return function(b) {
return a + b;
}
}
var add5 = add(5);
console.log(add5(3)); // 輸出:8
答案:嚴(yán)格模式是一種JavaScript的執(zhí)行模式,它提供了更嚴(yán)格的語(yǔ)法和錯(cuò)誤檢查。在嚴(yán)格模式下,一些不安全或不推薦的語(yǔ)法會(huì)被禁用,同時(shí)會(huì)引入一些新的特性,如變量必須先聲明才能使用、禁止使用this指向全局對(duì)象等。
答案:TypeScript是JavaScript的超集,它添加了靜態(tài)類型和其他一些特性。TypeScript代碼可以編譯成JavaScript代碼,因此可以在任何支持JavaScript的環(huán)境中運(yùn)行。
答案:類型注解是指在變量、函數(shù)參數(shù)、函數(shù)返回值等地方顯式地聲明類型信息。可以使用冒號(hào)(:)后跟類型來(lái)添加類型注解。例如:
let num: number = 10;
function add(a: number, b: number): number {
return a + b;
}
答案:接口是一種用于定義對(duì)象的結(jié)構(gòu)和類型的語(yǔ)法。可以使用interface關(guān)鍵字來(lái)定義接口。例如:
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!"
答案:類是一種用于創(chuàng)建對(duì)象的藍(lán)圖,它包含屬性和方法。可以使用class關(guān)鍵字來(lái)定義類。例如:
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!"
答案:泛型是一種用于創(chuàng)建可重用代碼的工具,它允許在定義函數(shù)、類或接口時(shí)使用占位符類型。可以使用尖括號(hào)(<>)來(lái)指定泛型類型。例如:
function identity<T>(value: T): T {
return value;
}
let result = identity<string>("Hello");
console.log(result); // 輸出 "Hello"
答案:枚舉是一種用于定義命名常量集合的語(yǔ)法。可以使用enum關(guān)鍵字來(lái)定義枚舉。例如:
enum Color {
Red,
Green,
Blue,
}
let color: Color = Color.Green;
console.log(color); // 輸出 1
答案:模塊是用于組織和封裝代碼的單元。可以使用export關(guān)鍵字將模塊中的變量、函數(shù)、類等導(dǎo)出,以便其他模塊可以使用。可以使用import關(guān)鍵字來(lái)導(dǎo)入其他模塊的導(dǎo)出。例如:
// module.ts
export function greet(name: string) {
console.log(`Hello, ${name}!`);
}
// main.ts
import { greet } from "./module";
greet("John"); // 輸出 "Hello, John!"
答案:類型推斷是指TypeScript根據(jù)上下文自動(dòng)推斷變量的類型,而無(wú)需顯式地添加類型注解。例如:
let num = 10; // 推斷為 number 類型
let str = "Hello"; // 推斷為 string 類型
答案:命名空間是一種用于組織和封裝代碼的機(jī)制,它避免了全局命名沖突。可以使用namespace關(guān)鍵字來(lái)定義命名空間。例如:
namespace MyNamespace {
export function greet(name: string) {
console.log(`Hello, ${name}!`);
}
}
MyNamespace.greet("John"); // 輸出 "Hello, John!"
答案:類型別名是給類型起一個(gè)別名,以便在代碼中更方便地引用。可以使用type關(guān)鍵字來(lái)定義類型別名。例如:
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)"
答案:Vue.js是一個(gè)用于構(gòu)建用戶界面的JavaScript框架。它具有以下特點(diǎn):
響應(yīng)式數(shù)據(jù)綁定:通過(guò)使用Vue的數(shù)據(jù)綁定語(yǔ)法,可以實(shí)現(xiàn)數(shù)據(jù)的自動(dòng)更新。 組件化開(kāi)發(fā):Vue允許將頁(yè)面劃分為獨(dú)立的組件,提高了代碼的可維護(hù)性和復(fù)用性。 虛擬DOM:Vue使用虛擬DOM來(lái)跟蹤頁(yè)面上的變化,并高效地更新實(shí)際的DOM。 指令系統(tǒng):Vue提供了豐富的內(nèi)置指令,用于處理常見(jiàn)的DOM操作和邏輯控制。 生態(tài)系統(tǒng):Vue擁有龐大的生態(tài)系統(tǒng),包括插件、工具和第三方庫(kù),可以滿足各種開(kāi)發(fā)需求。
答案:Vue中的雙向數(shù)據(jù)綁定是通過(guò)v-model指令實(shí)現(xiàn)的。v-model可以在表單元素(如、、)上創(chuàng)建雙向數(shù)據(jù)綁定。當(dāng)用戶輸入改變表單元素的值時(shí),數(shù)據(jù)模型會(huì)自動(dòng)更新;反之,當(dāng)數(shù)據(jù)模型的值改變時(shí),表單元素也會(huì)自動(dòng)更新。
答案:Vue中的生命周期鉤子包括beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy和destroyed。它們的執(zhí)行順序如下:
beforeCreate created beforeMount mounted beforeUpdate updated beforeDestroy destroyed
答案:計(jì)算屬性是基于依賴的屬性,它根據(jù)其依賴的數(shù)據(jù)動(dòng)態(tài)計(jì)算得出值。計(jì)算屬性具有緩存機(jī)制,只有在依賴的數(shù)據(jù)發(fā)生變化時(shí)才會(huì)重新計(jì)算。監(jiān)聽(tīng)器是用于監(jiān)聽(tīng)數(shù)據(jù)的變化并執(zhí)行相應(yīng)的操作。當(dāng)數(shù)據(jù)發(fā)生變化時(shí),監(jiān)聽(tīng)器會(huì)立即執(zhí)行指定的回調(diào)函數(shù)。
答案:Vue中的組件通信方式包括:
父子組件通信:通過(guò)props向子組件傳遞數(shù)據(jù),子組件通過(guò)事件向父組件發(fā)送消息。 子父組件通信:子組件通過(guò)$emit觸發(fā)事件,父組件通過(guò)監(jiān)聽(tīng)事件并響應(yīng)。 兄弟組件通信:通過(guò)共享的父組件來(lái)傳遞數(shù)據(jù)或通過(guò)事件總線(Event Bus)進(jìn)行通信。 跨級(jí)組件通信:通過(guò)provide和inject來(lái)在祖先組件中提供數(shù)據(jù),然后在后代組件中使用。
答案:Vue中的路由是通過(guò)Vue Router實(shí)現(xiàn)的。Vue Router是Vue.js官方提供的路由管理器,它允許開(kāi)發(fā)者在Vue應(yīng)用中實(shí)現(xiàn)單頁(yè)面應(yīng)用(SPA)。Vue Router通過(guò)配置路由映射關(guān)系,將URL路徑與組件進(jìn)行關(guān)聯(lián),并提供導(dǎo)航功能,使用戶可以在不刷新頁(yè)面的情況下切換視圖。
答案:Vue中常用的指令包括:
v-if:根據(jù)表達(dá)式的值條件性地渲染元素。 v-for:根據(jù)數(shù)組或?qū)ο蟮臄?shù)據(jù)進(jìn)行循環(huán)渲染。 v-bind:用于動(dòng)態(tài)綁定屬性或響應(yīng)式地更新屬性。 v-on:用于監(jiān)聽(tīng)DOM事件并執(zhí)行相應(yīng)的方法。 v-model:用于在表單元素上實(shí)現(xiàn)雙向數(shù)據(jù)綁定。 例如:
<div v-if="show">顯示內(nèi)容</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">點(diǎn)擊按鈕</button>
<input v-model="message">
答案:watch和computed都可以用于監(jiān)聽(tīng)數(shù)據(jù)的變化,但它們的用法和實(shí)現(xiàn)方式略有不同。watch用于監(jiān)聽(tīng)指定的數(shù)據(jù)變化,并在數(shù)據(jù)變化時(shí)執(zhí)行相應(yīng)的操作。computed用于根據(jù)依賴的數(shù)據(jù)動(dòng)態(tài)計(jì)算得出一個(gè)新的值,并將該值緩存起來(lái),只有在依賴的數(shù)據(jù)發(fā)生變化時(shí)才會(huì)重新計(jì)算。
答案:Mixin是一種用于在多個(gè)組件之間共享代碼的方式。Mixin可以包含組件選項(xiàng)(如數(shù)據(jù)、方法、生命周期鉤子等),并將其合并到使用Mixin的組件中。這樣可以實(shí)現(xiàn)代碼的復(fù)用和組件的擴(kuò)展,減少重復(fù)編寫相似代碼的工作。
答案:是Vue中的一個(gè)內(nèi)置組件,用于緩存動(dòng)態(tài)組件。當(dāng)組件包裹在中時(shí),組件的狀態(tài)將被保留,包括它的實(shí)例、狀態(tài)和DOM結(jié)構(gòu)。這樣可以避免在組件切換時(shí)重復(fù)創(chuàng)建和銷毀組件,提高性能和用戶體驗(yàn)。
答案:依賴注入是一種設(shè)計(jì)模式,用于將依賴關(guān)系從一個(gè)組件傳遞到另一個(gè)組件。在Vue中,依賴注入通過(guò)provide和inject選項(xiàng)實(shí)現(xiàn)。父組件通過(guò)provide提供數(shù)據(jù),然后子組件通過(guò)inject注入這些數(shù)據(jù)。它在跨多個(gè)層級(jí)的組件通信中非常有用。
答案:渲染函數(shù)是一種用JavaScript代碼編寫組件的方式,它可以動(dòng)態(tài)地生成虛擬DOM。與模板相比,渲染函數(shù)提供了更大的靈活性和控制力,可以處理更復(fù)雜的邏輯和動(dòng)態(tài)渲染需求。
答案:插槽是一種用于在組件中擴(kuò)展內(nèi)容的機(jī)制。命名插槽允許父組件向子組件插入具有特定名稱的內(nèi)容,而作用域插槽允許子組件將數(shù)據(jù)傳遞給父組件。示例:
<!-- 父組件 -->
<template>
<div>
<slot name="header"></slot>
<slot :data="data"></slot>
</div>
</template>
<!-- 子組件 -->
<template>
<div>
<slot name="header">默認(rèn)標(biāo)題</slot>
<slot :data="computedData">{{ computedData }}</slot>
</div>
</template>
答案:Vue.js的動(dòng)畫(huà)系統(tǒng)通過(guò)CSS過(guò)渡和動(dòng)畫(huà)類實(shí)現(xiàn)。通過(guò)在元素上添加過(guò)渡類或動(dòng)畫(huà)類,可以觸發(fā)相應(yīng)的過(guò)渡效果或動(dòng)畫(huà)效果。示例:
<transition name="fade">
<div v-if="show">顯示內(nèi)容</div>
</transition>
<!-- CSS樣式 -->
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
</style>
答案:Vue.js提供了全局的錯(cuò)誤處理機(jī)制和組件級(jí)別的錯(cuò)誤處理機(jī)制。全局錯(cuò)誤處理可以通過(guò)errorCaptured鉤子函數(shù)捕獲和處理錯(cuò)誤。組件級(jí)別的錯(cuò)誤處理可以通過(guò)errorCaptured鉤子函數(shù)或errorHandler選項(xiàng)捕獲和處理錯(cuò)誤。
答案:服務(wù)端渲染是指在服務(wù)器上生成HTML內(nèi)容并將其發(fā)送到瀏覽器進(jìn)行渲染的過(guò)程。Vue.js可以進(jìn)行服務(wù)端渲染,提供更好的首次加載性能和SEO優(yōu)化。然而,服務(wù)端渲染也帶來(lái)了一些限制,如增加了服務(wù)器負(fù)載和開(kāi)發(fā)復(fù)雜性。
答案:Vue.js的響應(yīng)式系統(tǒng)對(duì)于數(shù)組的變異方法(如push、pop、splice等)是無(wú)法追蹤的。為了解決這個(gè)限制,Vue提供了一些特殊的方法,如Vue.set、vm.$set和Array.prototype.splice。這些方法可以用于更新數(shù)組并保持響應(yīng)式。
答案:常見(jiàn)的Vue.js性能優(yōu)化技巧包括:
使用v-if和v-for時(shí)注意避免不必要的渲染。 合理使用computed屬性和watch監(jiān)聽(tīng)器。 使用keep-alive組件緩存組件狀態(tài)。 使用異步組件進(jìn)行按需加載。 避免在模板中使用復(fù)雜的表達(dá)式。 使用key屬性管理組件和元素的復(fù)用。 合理使用懶加載和分割代碼。
答案:Vue.js中的路由導(dǎo)航守衛(wèi)包括全局前置守衛(wèi)、全局解析守衛(wèi)、全局后置守衛(wèi)、路由獨(dú)享守衛(wèi)和組件內(nèi)守衛(wèi)。它們的執(zhí)行順序如下:
全局前置守衛(wèi)(beforeEach) 路由獨(dú)享守衛(wèi)(beforeEnter) 解析守衛(wèi)(beforeResolve) 組件內(nèi)守衛(wèi)(beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave) 全局后置守衛(wèi)(afterEach)
答案:Vue.js的單元測(cè)試可以使用工具如Jest或Mocha進(jìn)行。示例:
// 組件代碼
// 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>
// 單元測(cè)試代碼
// 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)
})
})
答案:Composition API是Vue.js 3中引入的一種新的組織組件邏輯的方式。它允許開(kāi)發(fā)者通過(guò)函數(shù)的方式組織和重用邏輯,而不是通過(guò)選項(xiàng)對(duì)象。相比之下,Options API是Vue.js 2中常用的組織組件邏輯的方式,通過(guò)選項(xiàng)對(duì)象中的屬性來(lái)定義組件的數(shù)據(jù)、方法等。
答案:Teleport是Vue.js 3中引入的一種機(jī)制,用于將組件的內(nèi)容渲染到DOM樹(shù)中的任意位置。示例:
<template>
<div>
<button @click="showModal = true">打開(kāi)模態(tài)框</button>
<teleport to="body">
<modal v-if="showModal" @close="showModal = false">模態(tài)框內(nèi)容</modal>
</teleport>
</div>
</template>
答案:Vue.js 3中的響應(yīng)式系統(tǒng)使用了Proxy對(duì)象來(lái)實(shí)現(xiàn)。與Vue.js 2中的響應(yīng)式系統(tǒng)相比,Vue.js 3的響應(yīng)式系統(tǒng)具有更好的性能和更細(xì)粒度的追蹤,能夠更準(zhǔn)確地檢測(cè)到數(shù)據(jù)的變化,并且支持嵌套的響應(yīng)式數(shù)據(jù)。
答案:Suspense是Vue.js 3中引入的一種機(jī)制,用于處理異步組件的加載狀態(tài)。它可以在異步組件加載完成之前顯示一個(gè)占位符,并在加載完成后渲染異步組件的內(nèi)容。這樣可以更好地處理異步組件的加載過(guò)程,提供更好的用戶體驗(yàn)。
答案:provide和inject用于實(shí)現(xiàn)組件之間的依賴注入。通過(guò)在父組件中使用provide提供數(shù)據(jù),然后在子組件中使用inject注入這些數(shù)據(jù)。示例:
// 父組件
const Parent = {
provide: {
message: 'Hello'
},
// ...
}
// 子組件
const Child = {
inject: ['message'],
created() {
console.log(this.message); // 輸出:Hello
},
// ...
}
答案:Vue.js 3中的動(dòng)畫(huà)系統(tǒng)相比Vue.js 2有以下改進(jìn)之處:
更好的性能:Vue.js 3的動(dòng)畫(huà)系統(tǒng)使用了更高效的動(dòng)畫(huà)引擎,提供了更好的性能。 更簡(jiǎn)潔的語(yǔ)法:Vue.js 3的動(dòng)畫(huà)系統(tǒng)使用了更簡(jiǎn)潔的語(yǔ)法,使得動(dòng)畫(huà)的定義和使用更加直觀和方便。 支持更多的動(dòng)畫(huà)特性:Vue.js 3的動(dòng)畫(huà)系統(tǒng)支持更多的動(dòng)畫(huà)特性,如交互式動(dòng)畫(huà)和更復(fù)雜的動(dòng)畫(huà)效果。 Vue.js 3中的靜態(tài)提升(Static Tree Hoisting)是什么?它有什么優(yōu)勢(shì)? 答案:靜態(tài)提升是Vue.js 3中的一項(xiàng)優(yōu)化技術(shù),通過(guò)在編譯階段將靜態(tài)節(jié)點(diǎn)提升為常量,從而減少了運(yùn)行時(shí)的開(kāi)銷。這項(xiàng)優(yōu)化技術(shù)可以提高組件的渲染性能,并減少生成的代碼體積。
答案:Fragment是Vue.js 3中引入的一種機(jī)制,用于在組件中返回多個(gè)根節(jié)點(diǎn)。在Vue.js 2中,組件的模板只能有一個(gè) Vue.js 3中的Composition API中的ref和reactive有什么區(qū)別?什么時(shí)候使用哪個(gè)? 答案:ref用于創(chuàng)建一個(gè)響應(yīng)式的基本數(shù)據(jù)類型,而reactive用于創(chuàng)建一個(gè)響應(yīng)式的對(duì)象。當(dāng)需要?jiǎng)?chuàng)建一個(gè)簡(jiǎn)單的響應(yīng)式數(shù)據(jù)時(shí),可以使用ref,當(dāng)需要?jiǎng)?chuàng)建一個(gè)包含多個(gè)屬性的響應(yīng)式對(duì)象時(shí),可以使用reactive。
答案:watchEffect用于監(jiān)聽(tīng)響應(yīng)式數(shù)據(jù)的變化,并在回調(diào)函數(shù)中執(zhí)行相應(yīng)的操作。它會(huì)自動(dòng)追蹤依賴,并在依賴變化時(shí)重新運(yùn)行回調(diào)函數(shù)。watch用于監(jiān)聽(tīng)指定的響應(yīng)式數(shù)據(jù),并在其變化時(shí)執(zhí)行相應(yīng)的操作。它可以精確地指定要監(jiān)聽(tīng)的數(shù)據(jù),并提供更多的配置選項(xiàng)。一般來(lái)說(shuō),如果只需要監(jiān)聽(tīng)一個(gè)響應(yīng)式數(shù)據(jù)的變化并執(zhí)行相應(yīng)操作,可以使用watchEffect;如果需要更細(xì)粒度的控制,可以使用watch。
答案:在使用v-model指令時(shí),有以下注意事項(xiàng):
v-model指令必須與一個(gè)表單元素一起使用,如、、等。 當(dāng)使用自定義組件時(shí),組件內(nèi)部必須實(shí)現(xiàn)modelValue屬性和update:modelValue事件,以支持v-model的雙向綁定。 可以使用.lazy修飾符實(shí)現(xiàn)在輸入框失去焦點(diǎn)時(shí)更新數(shù)據(jù)。 可以使用.trim修飾符自動(dòng)去除輸入框內(nèi)容的首尾空格。 可以使用.number修飾符將輸入框的值轉(zhuǎn)換為數(shù)字類型。
答案:默認(rèn)情況下,provide和inject不支持響應(yīng)式數(shù)據(jù)。如果需要在provide中提供一個(gè)響應(yīng)式數(shù)據(jù),可以使用ref或reactive將數(shù)據(jù)包裝起來(lái)。然后在inject中使用toRefs或toRef將數(shù)據(jù)解構(gòu)出來(lái),以獲取響應(yīng)式的引用。
答案:nextTick方法用于在下次DOM更新循環(huán)結(jié)束之后執(zhí)行回調(diào)函數(shù)。它可以用來(lái)確保在更新DOM后執(zhí)行某些操作,如操作更新后的DOM元素或獲取更新后的計(jì)算屬性的值。通常在需要等待DOM更新完成后進(jìn)行操作的情況下使用nextTick。
答案:組件用于將組件的內(nèi)容渲染到DOM樹(shù)中的任意位置,而組件用于在組件進(jìn)入或離開(kāi)DOM樹(shù)時(shí)應(yīng)用過(guò)渡效果。主要用于組件的位置移動(dòng),而主要用于組件的顯示和隱藏過(guò)渡。
答案:v-for指令中的key屬性用于給每個(gè)迭代項(xiàng)設(shè)置一個(gè)唯一的標(biāo)識(shí)符。它的作用是幫助Vue.js跟蹤每個(gè)節(jié)點(diǎn)的身份,以便在數(shù)據(jù)發(fā)生變化時(shí)高效地更新DOM。使用key屬性可以避免出現(xiàn)錯(cuò)誤的節(jié)點(diǎn)更新或重新排序的問(wèn)題。
答案:React是一個(gè)用于構(gòu)建用戶界面的JavaScript庫(kù)。它的核心概念是組件化和聲明式編程。React將用戶界面拆分為獨(dú)立的可重用組件,并使用聲明式語(yǔ)法描述組件的狀態(tài)和UI的關(guān)系,使得構(gòu)建復(fù)雜的UI變得簡(jiǎn)單和可維護(hù)。
答案:JSX是一種JavaScript的語(yǔ)法擴(kuò)展,用于在React中描述UI的結(jié)構(gòu)。它類似于HTML,但有一些區(qū)別:
答案:React組件是構(gòu)建用戶界面的獨(dú)立單元。React組件有兩種類型:
函數(shù)組件:使用函數(shù)來(lái)定義組件,接收props作為參數(shù),并返回一個(gè)React元素。 類組件:使用ES6類來(lái)定義組件,繼承自React.Component類,通過(guò)render方法返回一個(gè)React元素。
答案:狀態(tài)(state)是組件自身管理的數(shù)據(jù),可以通過(guò)setState方法來(lái)更新。屬性(props)是從父組件傳遞給子組件的數(shù)據(jù),子組件無(wú)法直接修改props,只能通過(guò)父組件的更新來(lái)改變props。
區(qū)別:
狀態(tài)(state)是組件內(nèi)部的數(shù)據(jù),可以在組件中自由修改和管理。 屬性(props)是從父組件傳遞給子組件的數(shù)據(jù),子組件無(wú)法直接修改,只能接收和使用。
答案:React生命周期方法是在組件不同階段執(zhí)行的特定方法。以下是一些常用的React生命周期方法:
componentDidMount:組件掛載后立即調(diào)用。 componentDidUpdate:組件更新后調(diào)用。 componentWillUnmount:組件卸載前調(diào)用。 shouldComponentUpdate:決定組件是否需要重新渲染。 getDerivedStateFromProps:根據(jù)props的變化來(lái)更新?tīng)顟B(tài)。
答案:React Hooks是React 16.8版本引入的一種特性,用于在函數(shù)組件中使用狀態(tài)和其他React特性。Hooks提供了一種無(wú)需編寫類組件的方式來(lái)管理狀態(tài)和處理副作用,使得函數(shù)組件具有類組件的能力。
答案:React Router是React中用于處理路由的庫(kù)。它提供了一種在單頁(yè)面應(yīng)用中實(shí)現(xiàn)導(dǎo)航和路由功能的方式。React Router可以幫助開(kāi)發(fā)者實(shí)現(xiàn)頁(yè)面之間的切換、URL參數(shù)的傳遞、嵌套路由等功能。
答案:React Context是一種用于在組件樹(shù)中共享數(shù)據(jù)的機(jī)制。它可以避免通過(guò)props一層層傳遞數(shù)據(jù),使得跨組件的數(shù)據(jù)共享變得更加簡(jiǎn)單和高效。React Context提供了一個(gè)Provider和Consumer組件,用于提供和消費(fèi)共享的數(shù)據(jù)。
答案:React的協(xié)調(diào)過(guò)程是指React在進(jìn)行組件更新時(shí),通過(guò)比較新舊虛擬DOM樹(shù)的差異,僅對(duì)需要更新的部分進(jìn)行實(shí)際的DOM操作。協(xié)調(diào)過(guò)程的工作方式如下:
React會(huì)逐層比較新舊虛擬DOM樹(shù)的節(jié)點(diǎn),并找出差異。 對(duì)于每個(gè)差異,React會(huì)生成相應(yīng)的DOM操作指令,如插入、更新或刪除節(jié)點(diǎn)。 React會(huì)將所有的DOM操作指令批量執(zhí)行,以減少對(duì)真實(shí)DOM的操作次數(shù)。
答案:React的事件合成是一種在React中處理事件的機(jī)制。它是React為了提高性能和跨瀏覽器兼容性而實(shí)現(xiàn)的一種事件系統(tǒng)。事件合成的作用包括:
提供了一種統(tǒng)一的方式來(lái)處理事件,無(wú)需考慮瀏覽器兼容性。 可以通過(guò)事件委托的方式將事件處理程序綁定到父組件,提高性能。 可以訪問(wèn)原生事件對(duì)象的屬性和方法。
答案:React的Fiber架構(gòu)是React 16版本引入的一種新的協(xié)調(diào)算法和架構(gòu)。它旨在解決長(zhǎng)時(shí)間渲染阻塞主線程的問(wèn)題,提高應(yīng)用的性能和用戶體驗(yàn)。Fiber架構(gòu)通過(guò)將渲染過(guò)程分解為多個(gè)小任務(wù),并使用優(yōu)先級(jí)調(diào)度算法來(lái)動(dòng)態(tài)分配時(shí)間片,使得React可以在每個(gè)幀中執(zhí)行一部分任務(wù),從而實(shí)現(xiàn)平滑的用戶界面和更好的響應(yīng)性能。
答案:React的錯(cuò)誤邊界是一種用于處理組件錯(cuò)誤的機(jī)制。它允許組件捕獲并處理其子組件中發(fā)生的JavaScript錯(cuò)誤,以避免整個(gè)應(yīng)用崩潰。錯(cuò)誤邊界的作用包括:
捕獲并處理組件樹(shù)中的錯(cuò)誤,防止錯(cuò)誤導(dǎo)致整個(gè)應(yīng)用崩潰。 提供一種優(yōu)雅的方式來(lái)顯示錯(cuò)誤信息或備用UI。 可以用于記錄錯(cuò)誤和發(fā)送錯(cuò)誤報(bào)告。
答案:HTTP(Hypertext Transfer Protocol)是一種用于在Web上傳輸數(shù)據(jù)的協(xié)議。它使用客戶端-服務(wù)器模型,客戶端發(fā)送HTTP請(qǐng)求到服務(wù)器,服務(wù)器返回HTTP響應(yīng)。HTTP的工作流程如下:
客戶端發(fā)送HTTP請(qǐng)求到指定的URL。 服務(wù)器接收請(qǐng)求并處理,然后返回HTTP響應(yīng)。 客戶端接收響應(yīng)并解析,從中獲取所需的數(shù)據(jù)。
答案:HTTPS(Hypertext Transfer Protocol Secure)是HTTP的安全版本,通過(guò)使用SSL(Secure Sockets Layer)或TLS(Transport Layer Security)協(xié)議對(duì)通信進(jìn)行加密和身份驗(yàn)證。與HTTP相比,HTTPS具有以下區(qū)別:
數(shù)據(jù)在傳輸過(guò)程中通過(guò)加密進(jìn)行保護(hù),提供更高的安全性。 使用數(shù)字證書(shū)對(duì)服務(wù)器進(jìn)行身份驗(yàn)證,防止中間人攻擊。 使用默認(rèn)端口443。
答案:跨域請(qǐng)求是指在瀏覽器中向不同域名、端口或協(xié)議發(fā)送的請(qǐng)求。由于瀏覽器的同源策略(Same-Origin Policy)限制,跨域請(qǐng)求會(huì)受到限制。為了解決跨域問(wèn)題,可以使用以下方法:
JSONP(JSON with Padding):通過(guò)動(dòng)態(tài)創(chuàng)建
答案:緩存是將數(shù)據(jù)或資源存儲(chǔ)在臨時(shí)存儲(chǔ)中,以便在后續(xù)請(qǐng)求中重復(fù)使用,從而提高性能和減少網(wǎng)絡(luò)流量。在前端中,可以使用以下方式來(lái)利用緩存:
HTTP緩存:通過(guò)設(shè)置適當(dāng)?shù)木彺骖^(如Cache-Control和Expires)來(lái)指示瀏覽器緩存響應(yīng)。 資源緩存:使用文件指紋或版本號(hào)來(lái)重命名靜態(tài)資源文件,以便在文件內(nèi)容變化時(shí)使瀏覽器重新下載。 數(shù)據(jù)緩存:使用內(nèi)存緩存、瀏覽器本地存儲(chǔ)(如localStorage)或服務(wù)端緩存(如Redis)來(lái)存儲(chǔ)數(shù)據(jù),避免重復(fù)請(qǐng)求。
答案:CDN(Content Delivery Network)是一種分布式網(wǎng)絡(luò)架構(gòu),用于在全球各地提供高性能、低延遲的內(nèi)容傳輸服務(wù)。CDN的作用包括:
將靜態(tài)資源(如圖片、樣式表、腳本等)緩存到離用戶更近的服務(wù)器上,提供更快的加載速度。 分發(fā)網(wǎng)絡(luò)流量,減輕源服務(wù)器的負(fù)載壓力。 提供內(nèi)容壓縮、數(shù)據(jù)壓縮和緩存等優(yōu)化技術(shù),提高用戶體驗(yàn)。
答案:網(wǎng)頁(yè)加載性能優(yōu)化是指通過(guò)各種技術(shù)手段來(lái)減少網(wǎng)頁(yè)加載時(shí)間并提高用戶體驗(yàn)。可以采取以下措施來(lái)改善網(wǎng)頁(yè)加載性能:
壓縮和合并資源文件(如CSS和JavaScript),減少文件大小和請(qǐng)求數(shù)量。 使用圖像壓縮和適當(dāng)?shù)母袷竭x擇來(lái)減小圖像文件大小。 使用瀏覽器緩存和HTTP緩存頭來(lái)緩存靜態(tài)資源。 使用懶加載延遲加載非關(guān)鍵資源,提高初始加載速度。 使用CDN(內(nèi)容分發(fā)網(wǎng)絡(luò))來(lái)分發(fā)靜態(tài)資源,減少網(wǎng)絡(luò)延遲。 優(yōu)化關(guān)鍵渲染路徑,盡早呈現(xiàn)頁(yè)面內(nèi)容。
答案:網(wǎng)頁(yè)性能監(jiān)測(cè)和分析是指通過(guò)測(cè)量和收集有關(guān)網(wǎng)頁(yè)加載和交互性能的數(shù)據(jù),以便識(shí)別性能瓶頸并進(jìn)行優(yōu)化。可以使用以下工具來(lái)監(jiān)測(cè)和分析網(wǎng)頁(yè)性能:
Web性能API:瀏覽器提供的JavaScript API,可通過(guò)performance對(duì)象來(lái)收集性能數(shù)據(jù)。 Lighthouse:一種開(kāi)源工具,可提供關(guān)于網(wǎng)頁(yè)性能、可訪問(wèn)性和最佳實(shí)踐的綜合報(bào)告。 WebPagetest:在線工具,可測(cè)量網(wǎng)頁(yè)加載時(shí)間并提供詳細(xì)的性能分析報(bào)告。 Chrome開(kāi)發(fā)者工具:瀏覽器內(nèi)置的開(kāi)發(fā)者工具,提供了性能分析、網(wǎng)絡(luò)監(jiān)控和頁(yè)面審查等功能。
答案:漸進(jìn)式圖像加載是一種技術(shù),通過(guò)逐步加載圖像的模糊或低分辨率版本,然后逐漸提高圖像的清晰度,以改善網(wǎng)頁(yè)加載性能和用戶體驗(yàn)。漸進(jìn)式圖像加載的好處包括:
用戶可以更快地看到頁(yè)面內(nèi)容,提高感知速度。 逐步加載圖像可以減少網(wǎng)頁(yè)整體的加載時(shí)間。 漸進(jìn)式圖像加載可以提供平滑的過(guò)渡效果,避免頁(yè)面內(nèi)容突然閃爍或變化。
答案:前端資源優(yōu)先級(jí)是指為不同類型的資源分配加載優(yōu)先級(jí),以優(yōu)化網(wǎng)頁(yè)加載性能。可以使用以下方法設(shè)置資源的優(yōu)先級(jí):
使用標(biāo)簽來(lái)指定資源的預(yù)加載,以確保關(guān)鍵資源盡早加載。 使用標(biāo)簽來(lái)指定可能在未來(lái)頁(yè)面中使用的資源,以提前加載。 使用標(biāo)簽來(lái)指定要預(yù)解析的域名,以減少DNS查找時(shí)間。 使用標(biāo)簽來(lái)指定要預(yù)連接的域名,以減少建立連接的時(shí)間。
答案:瀏覽器的工作原理包括以下幾個(gè)關(guān)鍵步驟:
解析:瀏覽器將接收到的HTML、CSS和JavaScript代碼解析成DOM樹(shù)、CSSOM樹(shù)和JavaScript引擎可執(zhí)行的代碼。 渲染:瀏覽器使用DOM樹(shù)和CSSOM樹(shù)構(gòu)建渲染樹(shù),然后根據(jù)渲染樹(shù)進(jìn)行布局(計(jì)算元素的位置和大小)和繪制(將元素繪制到屏幕上)。 布局和繪制:瀏覽器根據(jù)渲染樹(shù)的變化進(jìn)行布局和繪制,然后將最終的頁(yè)面呈現(xiàn)給用戶。 JavaScript引擎執(zhí)行:瀏覽器的JavaScript引擎解釋和執(zhí)行JavaScript代碼,并根據(jù)需要更新渲染樹(shù)和重新渲染頁(yè)面。
答案:重繪是指當(dāng)元素的外觀(如顏色、背景等)發(fā)生改變,但布局不受影響時(shí)的更新過(guò)程。重繪不會(huì)導(dǎo)致元素的位置或大小發(fā)生變化。
重排是指當(dāng)元素的布局屬性(如寬度、高度、位置等)發(fā)生改變時(shí)的更新過(guò)程。重排會(huì)導(dǎo)致瀏覽器重新計(jì)算渲染樹(shù)和重新繪制頁(yè)面的一部分或全部。
區(qū)別在于重繪只涉及外觀的更改,而重排涉及布局的更改。重排比重繪更消耗性能,因?yàn)樗枰匦掠?jì)算布局和繪制整個(gè)頁(yè)面。
答案:事件冒泡和事件捕獲是指瀏覽器處理事件時(shí)的兩種不同的傳播方式。
事件冒泡是指事件從最內(nèi)層的元素開(kāi)始觸發(fā),然后逐級(jí)向上傳播到父元素,直到傳播到最外層的元素。
事件捕獲是指事件從最外層的元素開(kāi)始觸發(fā),然后逐級(jí)向下傳播到最內(nèi)層的元素。
區(qū)別在于傳播方向的不同。事件冒泡是從內(nèi)向外傳播,而事件捕獲是從外向內(nèi)傳播。
答案:同步代碼是按照順序執(zhí)行的代碼,每個(gè)任務(wù)必須等待前一個(gè)任務(wù)完成后才能執(zhí)行。同步代碼會(huì)阻塞后續(xù)代碼的執(zhí)行,直到當(dāng)前任務(wù)完成。
異步代碼是不按照順序執(zhí)行的代碼,它會(huì)在后臺(tái)執(zhí)行,不會(huì)阻塞后續(xù)代碼的執(zhí)行。異步代碼通常使用回調(diào)函數(shù)、Promise、async/await等方式來(lái)處理異步操作的結(jié)果。
通過(guò)異步執(zhí)行,可以避免阻塞主線程,提高頁(yè)面的響應(yīng)性能。
答案:事件循環(huán)是JavaScript中處理異步代碼執(zhí)行的機(jī)制。它負(fù)責(zé)管理調(diào)度和執(zhí)行異步任務(wù),并將它們添加到執(zhí)行隊(duì)列中。
在JavaScript中,事件循環(huán)的作用是確保異步任務(wù)按照正確的順序執(zhí)行,并且不會(huì)阻塞主線程。它通過(guò)不斷地從執(zhí)行隊(duì)列中取出任務(wù)并執(zhí)行,以實(shí)現(xiàn)非阻塞的異步操作。
答案:瀏覽器的垃圾回收機(jī)制是一種自動(dòng)管理內(nèi)存的機(jī)制,用于檢測(cè)和回收不再使用的對(duì)象,以釋放內(nèi)存資源。
垃圾回收機(jī)制通過(guò)標(biāo)記-清除算法實(shí)現(xiàn)。它的工作原理如下:
標(biāo)記階段:垃圾回收器會(huì)從根對(duì)象(如全局對(duì)象)開(kāi)始,遞歸遍歷所有對(duì)象,并標(biāo)記仍然可訪問(wèn)的對(duì)象。 清除階段:垃圾回收器會(huì)掃描堆內(nèi)存,清除未被標(biāo)記的對(duì)象,并回收它們所占用的內(nèi)存空間。 垃圾回收機(jī)制的目標(biāo)是識(shí)別和回收不再使用的對(duì)象,以避免內(nèi)存泄漏和提高內(nèi)存利用率。
答案:同源策略是瀏覽器的一項(xiàng)安全機(jī)制,用于限制來(lái)自不同源的網(wǎng)頁(yè)之間的交互。同源是指協(xié)議、域名和端口號(hào)完全相同。
同源策略的限制包括:
腳本訪問(wèn)限制:不同源的腳本無(wú)法直接訪問(wèn)彼此的數(shù)據(jù)和操作。 DOM訪問(wèn)限制:不同源的網(wǎng)頁(yè)無(wú)法通過(guò)JavaScript訪問(wèn)彼此的DOM元素。 Cookie限制:不同源的網(wǎng)頁(yè)無(wú)法讀取或修改彼此的Cookie。 AJAX請(qǐng)求限制:不同源的網(wǎng)頁(yè)無(wú)法通過(guò)AJAX請(qǐng)求訪問(wèn)彼此的數(shù)據(jù)。 同源策略的存在可以防止惡意網(wǎng)站獲取用戶的敏感信息或進(jìn)行惡意操作。
答案:Web Workers是一種瀏覽器提供的JavaScript API,用于在后臺(tái)線程中執(zhí)行耗時(shí)的計(jì)算任務(wù),以避免阻塞主線程。
Web Workers的作用是提高瀏覽器的響應(yīng)性能,使得在執(zhí)行復(fù)雜計(jì)算或處理大量數(shù)據(jù)時(shí),不會(huì)影響用戶界面的流暢性。
Web Workers通過(guò)將任務(wù)委托給后臺(tái)線程來(lái)實(shí)現(xiàn)并行處理,從而充分利用多核處理器的能力。它們可以與主線程進(jìn)行通信,但不能直接訪問(wèn)DOM或執(zhí)行UI相關(guān)的操作。
答案:瀏覽器緩存是瀏覽器在本地存儲(chǔ)Web頁(yè)面和資源的副本,以便在后續(xù)訪問(wèn)時(shí)可以快速加載。它的作用是減少對(duì)服務(wù)器的請(qǐng)求次數(shù)和網(wǎng)絡(luò)傳輸量,提高頁(yè)面加載速度和用戶體驗(yàn)。
瀏覽器緩存通過(guò)在首次請(qǐng)求時(shí)將資源保存到本地,并在后續(xù)請(qǐng)求時(shí)檢查資源是否已經(jīng)存在并且沒(méi)有過(guò)期來(lái)工作。如果資源已經(jīng)存在且未過(guò)期,瀏覽器會(huì)直接從緩存中加載資源,而不是從服務(wù)器重新下載。
答案:重定向是指當(dāng)瀏覽器請(qǐng)求一個(gè)URL時(shí),服務(wù)器返回一個(gè)不同的URL,從而將瀏覽器的請(qǐng)求重定向到新的URL上。
重定向在瀏覽器中的作用是實(shí)現(xiàn)頁(yè)面的跳轉(zhuǎn)、URL的修改或資源的重定向。它可以用于多種情況,例如處理舊鏈接的跳轉(zhuǎn)、實(shí)現(xiàn)URL的規(guī)范化、處理用戶認(rèn)證等。
重定向通過(guò)在HTTP響應(yīng)中設(shè)置特定的狀態(tài)碼(如301永久重定向、302臨時(shí)重定向)和Location頭部字段來(lái)實(shí)現(xiàn)。
答案:瀏覽器存儲(chǔ)是瀏覽器提供的一種在客戶端存儲(chǔ)數(shù)據(jù)的機(jī)制,用于在不同的網(wǎng)頁(yè)間共享數(shù)據(jù)或持久保存數(shù)據(jù)。
瀏覽器存儲(chǔ)有以下不同的存儲(chǔ)機(jī)制:
Cookie:小型文本文件,可以存儲(chǔ)少量數(shù)據(jù),并在每次HTTP請(qǐng)求中自動(dòng)發(fā)送到服務(wù)器。 Web Storage(localStorage和sessionStorage):可以存儲(chǔ)較大量的數(shù)據(jù),以鍵值對(duì)的形式存儲(chǔ)在瀏覽器中。 IndexedDB:一種高級(jí)的客戶端數(shù)據(jù)庫(kù),可以存儲(chǔ)大量結(jié)構(gòu)化數(shù)據(jù),并支持索引和事務(wù)操作。 Cache API:用于緩存網(wǎng)絡(luò)請(qǐng)求的響應(yīng),以便離線訪問(wèn)或提高頁(yè)面加載速度。 不同的存儲(chǔ)機(jī)制適用于不同的需求,開(kāi)發(fā)者可以根據(jù)具體情況選擇合適的存儲(chǔ)方式。
祝想跳槽的小伙伴們都能成功!需要更多完整面試題可以私信我!
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。