天天看點

前端面試題:基礎題目

作者:科技樹亂點
前端面試題:基礎題目

#挑戰30天在頭條寫日記#

請實作防抖和節流函數并解釋他們的差別

throttle 函數和 debounce 函數都用于限制函數的執行頻率,但它們的實作和行為有所不同。

Throttle(節流)函數: Throttle 函數確定在一段時間内,函數最多隻能被調用一次。如果在這段時間内多次調用函數,隻有第一次調用會執行,後續調用将被忽略。Throttle 适用于那些在短時間内頻繁觸發的事件,比如滾動事件或按鈕點選事件。

以下是一個簡單的實作 throttle 函數的例子:

function throttle(func, delay) {
  let lastTime = 0;
  return function (...args) {
    const currentTime = Date.now();
    if (currentTime - lastTime >= delay) {
      func.apply(this, args);
      lastTime = currentTime;
    }
  };
}           

Debounce(防抖)函數: Debounce 函數確定函數在一定時間間隔後執行,如果在這個時間間隔内又被調用,則重新計時。換句話說,隻有在停止觸發一段時間後,函數才會執行。Debounce 适用于那些在短時間内可能被連續觸發,但我們隻關心最後一次觸發的情況,比如搜尋框輸入事件或視窗大小調整事件。

以下是一個簡單的實作 debounce 函數的例子:

function debounce(func, delay) {
  let timeoutId;
  return function (...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(this, args), delay);
  };
}           

差別:

  • throttle 確定在一段時間内函數最多被執行一次,而 debounce 確定函數在停止觸發一段時間後執行一次。
  • throttle 是在固定時間間隔内執行函數,而 debounce 是在最後一次觸發後等待一定時間後執行函數。
  • throttle 可能會在開始時立即執行函數,而 debounce 一定會在最後一次觸發後執行一次函數。

選擇使用 throttle 還是 debounce 取決于具體場景和需求。如果你需要在一段時間内頻繁觸發函數,并確定在固定時間間隔内執行,那麼使用 throttle 更為合适。如果你需要避免頻繁觸發函數,僅在使用者停止操作一段時間後才執行函數,那麼使用 debounce 更為合适。

實作一個函數,将數組轉換成樹結構

要将數組轉換成樹結構,我們需要定義一個遞歸函數來實作這個過程。假設數組中的每個元素表示樹中的一個節點,節點具有 `id` 和 `parentId` 屬性。其中 `id` 是節點的唯一辨別符,`parentId` 表示節點的父節點的 `id`。根節點的 `parentId` 為 `null` 或 `undefined`。

下面是一個實作将數組轉換為樹結構的 JavaScript 函數:

function arrayToTree(arr) {
  const map = {};
  const roots = [];

  arr.forEach((item) => {
    map[item.id] = { ...item, children: [] };
  });

  arr.forEach((item) => {
    const parent = map[item.parentId];
    if (parent) {
      parent.children.push(map[item.id]);
    } else {
      roots.push(map[item.id]);
    }
  });

  return roots;
}           

假設我們有以下數組表示樹結構:

const arr = [
  { id: 1, parentId: null, name: 'Root' },
  { id: 2, parentId: 1, name: 'Node 1' },
  { id: 3, parentId: 1, name: 'Node 2' },
  { id: 4, parentId: 2, name: 'Node 1.1' },
  { id: 5, parentId: 2, name: 'Node 1.2' },
  { id: 6, parentId: 4, name: 'Node 1.1.1' },
];           

我們可以使用 `arrayToTree` 函數将它轉換成樹結構:

const tree = arrayToTree(arr);
console.log(tree);           

輸出結果為:

[
  {
    id: 1,
    parentId: null,
    name: 'Root',
    children: [
      {
        id: 2,
        parentId: 1,
        name: 'Node 1',
        children: [
          {
            id: 4,
            parentId: 2,
            name: 'Node 1.1',
            children: [
              {
                id: 6,
                parentId: 4,
                name: 'Node 1.1.1',
                children: []
              }
            ]
          },
          {
            id: 5,
            parentId: 2,
            name: 'Node 1.2',
            children: []
          }
        ]
      },
      {
        id: 3,
        parentId: 1,
        name: 'Node 2',
        children: []
      }
    ]
  }
]           

通過這個函數,我們成功地将數組轉換成了樹結構,并且保留了節點之間的父子關系。

什麼是Promise?Promise的常用方法?

`Promise` 是 JavaScript 中用于處理異步操作的對象,它表示一個可能會在未來完成或失敗的操作,并傳回一個代表該操作結果的值。使用 `Promise` 可以更優雅地處理異步代碼,避免了回調地獄(Callback Hell),使代碼更加清晰和可維護。

`Promise` 有三種狀态:`pending`(進行中)、`fulfilled`(已完成)和`rejected`(已拒絕)。一旦 `Promise` 進入 `fulfilled` 或 `rejected` 狀态,就稱為 settled(已定型)。

常用的 `Promise` 方法有:

1. `Promise.resolve(value)`: 建立一個已經被解決(fulfilled)的 Promise,其結果為給定的值 `value`。

2. `Promise.reject(reason)`: 建立一個已經被拒絕(rejected)的 Promise,其原因為給定的 `reason`。

3. `Promise.all(iterable)`: 接受一個可疊代的對象(如數組或字元串),傳回一個新的 Promise,隻有當所有的 Promise 都成功解決時,該 Promise 才會被解決,傳回一個包含所有解決值的數組。如果其中一個 Promise 被拒絕,整個 `Promise.all` 就會被拒絕,并傳回第一個被拒絕的 Promise 的原因。

4. `Promise.race(iterable)`: 接受一個可疊代的對象(如數組或字元串),傳回一個新的 Promise,隻要其中任何一個 Promise 解決或拒絕,該 Promise 就會跟着解決或拒絕,并傳回相應 Promise 的值或原因。

5. `Promise.then(onFulfilled, onRejected)`: 用于指定在 Promise 解決或拒絕後要執行的回調函數。`onFulfilled` 是 Promise 解決時調用的函數,接受解決值作為參數;`onRejected` 是 Promise 被拒絕時調用的函數,接受拒絕原因作為參數。這兩個參數都是可選的。

6. `Promise.catch(onRejected)`: 是 `Promise.then(null, onRejected)` 的簡寫形式,用于指定 Promise 被拒絕時要執行的回調函數。

7. `Promise.finally(onFinally)`: 無論 Promise 是否被解決或拒絕,最終都會執行 `onFinally` 回調函數。

8. `Promise.allSettled(iterable)`: 傳回一個新的 Promise,隻有當所有的 Promise 都被解決或拒絕後,該 Promise 才會被解決。傳回的 Promise 會包含一個數組,數組中的每個元素代表傳入的 Promise 結果,包括解決值或拒絕原因。

這些方法允許我們更好地管理和處理異步代碼,使得在異步操作中更容易地處理結果或錯誤,并以更清晰和結構化的方式組織代碼。

HTTP和HTTPS的差別是什麼?HTTP 2優化了哪些問題?

HTTP(Hypertext Transfer Protocol)和HTTPS(HTTP Secure)是用于在用戶端和伺服器之間傳輸資料的協定。它們的主要差別在于安全性和加密。

**HTTP**:

- HTTP是不安全的協定,資料以明文形式傳輸,容易被竊聽和篡改。

- HTTP使用80端口進行通信。

- 在HTTP中,不需要進行證書驗證,伺服器身份無法得到保障。

- 因為不需要加密和證書驗證,HTTP的通信速度相對較快。

**HTTPS**:

- HTTPS是安全的協定,資料通過TLS/SSL加密,保障了資料的機密性和完整性。

- HTTPS使用443端口進行通信。

- 在HTTPS中,需要使用數字證書來驗證伺服器的身份,確定通信的安全性。

- 由于加密和證書驗證的開銷,HTTPS的通信速度相對較慢。

**HTTP/2**:

HTTP/2是HTTP/1.1的進化版本,旨在提高性能和效率,它優化了以下問題:

1. **多路複用**:HTTP/2引入了多路複用,允許在一個連接配接上同時發送多個請求和響應。這消除了HTTP/1中的隊頭阻塞問題,提高了并行請求的效率,減少了延遲,提高了頁面加載速度。

2. **二進制傳輸**:HTTP/2采用二進制格式傳輸資料,取代了HTTP/1中的文本格式。二進制傳輸更加高效,減少了解析資料的時間,提高了傳輸速度。

3. **Header 壓縮**:HTTP/2使用HPACK算法對Header進行壓縮,減少了Header的大小,節省了帶寬,提高了性能。

4. **伺服器推送**:HTTP/2允許伺服器主動推送資料到用戶端緩存,提前發送用戶端需要的資源,減少了請求次數,提高了加載速度。

5. **優先級設定**:HTTP/2允許發送請求時設定優先級,優化了請求的處理順序,提高了頁面加載速度。

總的來說,HTTP/2通過多路複用、二進制傳輸、Header壓縮、伺服器推送和優先級設定等優化,提高了性能和效率,使得網絡通信更加高效和快速。

HTTPS可以用除了443的其他端口嗎?

HTTPS在技術上可以使用除了預設端口443以外的其他端口,但并不推薦這樣做。HTTPS的标準端口是443,這是被廣泛認可并期望的端口。如果使用非标準端口進行HTTPS通信,可能會導緻相容性問題,同時可能會出現安全警告或錯誤。

當您使用預設的HTTPS端口(443)時,Web浏覽器和其他HTTP用戶端會預設認為連接配接是安全的和加密的,是以不會顯示任何安全警告。然而,如果使用非标準端口進行HTTPS通信,用戶端可能無法自動識别安全連接配接,使用者可能會遇到警告或錯誤,這可能會影響使用者體驗和安全性。

在大多數情況下,最好堅持使用标準端口(443)進行HTTPS通信,以確定相容性和良好的使用者體驗。如果有特殊原因需要使用不同的端口,請確定了解潛在的影響,并準備處理可能出現的相容性問題。

Node.js裡的process.nextTick是什麼?和setImmediate的差別

process.nextTick 和 setImmediate 都是 Node.js 中用于處理異步操作的函數,它們在執行時機和調用順序上有所不同。

process.nextTick:

process.nextTick 是一個在事件循環結束之前調用的函數。它的執行優先級高于其他異步函數,包括 setTimeout 和 setImmediate。當使用 process.nextTick 注冊的回調函數時,它會在目前執行階段結束後、事件循環的下一個階段(即檢查 I/O 操作和定時器)開始之前被調用。這意味着 process.nextTick 的回調函數會在事件循環的開始階段被調用,而不是在 I/O 或定時器階段。

console.log('start');

setTimeout(function() {
  console.log('setTimeout')
}, 0);

process.nextTick(() => {
  console.log('process.nextTick callback');
});

console.log('end');           

輸出結果:

start
end
process.nextTick callback
setTimeout           

setImmediate:

setImmediate 是一個在事件循環的檢查階段結束後調用的函數。它的執行優先級低于 process.nextTick,但比 setTimeout 更高。當使用 setImmediate 注冊的回調函數時,它會在目前執行階段結束後,事件循環的下一個階段開始時被調用。這意味着 setImmediate 的回調函數會在 I/O 和定時器階段之後被調用。

HTTP的keep-alive機制

HTTP的Keep-Alive機制是一種持久連接配接(Persistent Connection)技術,旨在減少用戶端和伺服器之間建立和關閉連接配接的開銷,提高網絡通信的性能和效率。

在HTTP/1.0中,每次用戶端發送請求後,伺服器會立即關閉連接配接,而每次響應也需要重建立立連接配接,這樣頻繁的建立和關閉連接配接會帶來較大的開銷。

HTTP的Keep-Alive機制通過在響應頭中增加一個Connection: keep-alive的字段來實作持久連接配接。當伺服器收到帶有Connection: keep-alive字段的請求時,它會在響應頭中也包含Connection: keep-alive字段,告訴用戶端繼續使用同一個連接配接來發送後續的請求。這樣用戶端可以繼續使用已經建立的連接配接來發送多個請求,而無需每次都重建立立連接配接。

具體來說,Keep-Alive機制的工作流程如下:

  1. 用戶端發送帶有Connection: keep-alive字段的HTTP請求到伺服器。
  2. 伺服器接收到請求後,如果支援Keep-Alive,就在響應頭中包含Connection: keep-alive字段。
  3. 用戶端接收到帶有Connection: keep-alive字段的響應後,将繼續使用同一個連接配接發送後續的請求。
  4. 如果用戶端在一定時間内沒有再次發送請求,伺服器會關閉該連接配接,釋放資源。

Keep-Alive機制可以大幅減少連接配接建立和關閉的開銷,提高網絡性能,特别适用于頻繁的短連接配接場景,如網頁中加載多個資源的情況。在HTTP/1.1中,Keep-Alive已經成為預設的行為,除非在請求頭中明确指定Connection: close來關閉連接配接。

webpack中loader和plugin的差別?

在Webpack中,`Loader` 和 `Plugin` 是兩個核心概念,它們都用于對子產品進行處理,但在功能和應用場景上有所不同。

**Loader**:

`Loader` 用于對源代碼檔案進行轉換,将其從一種格式轉換為另一種格式。Webpack在處理源代碼子產品時,會通過 `Loader` 來處理各種類型的檔案,如将ES6代碼轉換成ES5、将SCSS檔案轉換成CSS、将圖檔檔案轉換成Base64編碼等。每個 `Loader` 是獨立的,處理不同類型的檔案,而且 `Loader` 鍊是有序的,檔案在經過 `Loader` 鍊處理後,最終輸出為Webpack可處理的子產品。

例如,在Webpack的配置中,可以定義一個用于處理CSS的 `Loader`,如下所示:

module: {
  rules: [
    {
      test: /\.css$/,
      use: [
        'style-loader', // 将CSS以<style>标簽插入到頁面中
        'css-loader'    // 處理CSS檔案,使其可以在JavaScript中被導入
      ]
    }
  ]
}           

**Plugin**:

`Plugin` 用于執行更廣泛範圍的任務,它可以對整個建構過程進行自定義的優化和功能擴充。Webpack在建構的不同階段會觸發不同的事件,`Plugin` 可以通過監聽這些事件,并在特定的階段執行一些操作,如打包優化、資源管理、自動生成HTML檔案、清理輸出目錄等。每個 `Plugin` 是獨立的,可以有自己的配置選項。

例如,`HtmlWebpackPlugin` 是一個常用的 `Plugin`,用于自動生成HTML檔案并自動引入打包後的JS和CSS檔案:

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  // ...其他配置...
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html' // 使用的HTML模闆檔案
    })
  ]
};           

**差別**:

  • `Loader` 用于對子產品的源代碼進行轉換,處理各種類型的檔案,最終輸出為Webpack可處理的子產品。
  • `Plugin` 用于執行更廣泛範圍的任務,對建構過程進行自定義的優化和功能擴充,通過監聽Webpack的事件,在特定階段執行操作。

在Webpack配置中,`Loader` 通過 `module.rules` 定義,`Plugin` 通過 `plugins` 定義。`Loader` 主要用于處理子產品的轉換,而 `Plugin` 主要用于執行建構過程的定制化操作。

Node.js中的事件循環機制

Node.js 的事件循環機制是其異步非阻塞特性的核心。它允許 Node.js 在單線程中處理大量并發的 I/O 操作,而無需建立多個線程,進而實作高效的并發處理。

事件循環的主要步驟如下:

1. **執行同步代碼**:Node.js 首先執行目前調用棧中的同步代碼,包括子產品加載、函數調用等。

2. **執行事件循環**:Node.js 進入事件循環,監聽事件隊列中是否有事件要處理。事件可以是 I/O 操作的完成、計時器到期或調用 `process.nextTick`、`setImmediate` 等注冊的回調函數。

3. **檢查事件隊列**:事件循環會檢查事件隊列,如果有事件待處理,則會執行相應的回調函數。

4. **執行 I/O 操作**:如果有 I/O 操作完成,比如讀取檔案或從網絡擷取資料,它們的回調函數會被添加到事件隊列中等待執行。

5. **執行定時器**:如果有定時器到期,比如 `setTimeout` 或 `setInterval`,它們的回調函數也會被添加到事件隊列中等待執行。

6. **執行 `process.nextTick` 和 `setImmediate`**:這兩個特殊的異步方法的回調函數會在不同的事件循環階段執行。`process.nextTick` 的回調函數會在目前執行階段結束後立即執行,而 `setImmediate` 的回調函數會在目前事件循環的檢查階段結束後執行。

7. **重複執行**:一旦事件循環的所有階段都執行完畢,Node.js 會再次檢查是否有新的事件要處理。如果有,它會繼續執行這些事件的回調函數。如果沒有,事件循環将進入休眠狀态,等待新的事件被觸發。

通過事件循環機制,Node.js 可以高效地處理大量的并發請求,同時保持單線程的簡潔性和高效性。所有的 I/O 操作都是非阻塞的,通過事件驅動的方式進行處理,進而使得 Node.js 在處理高并發和大規模應用時表現出色。

┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘           

JavaScript中除了setTimeout,還有哪些定時執行函數?

除了 `setTimeout`,JavaScript 中還有以下定時執行函數:

1. **setInterval**:

`setInterval` 函數用于周期性地重複執行一個回調函數。它接受兩個參數,第一個參數是要執行的回調函數,第二個參數是時間間隔(以毫秒為機關)。`setInterval` 會在每個時間間隔後執行一次回調函數。

setInterval(() => {

console.log('This will be executed every 1000 milliseconds.');

}, 1000);           

2. **requestAnimationFrame**:

`requestAnimationFrame` 是一種用于執行動畫效果的定時器函數。它會在浏覽器每次重繪頁面前執行一次指定的回調函數,通常用于實作平滑的動畫效果。與 `setTimeout` 或 `setInterval` 不同,`requestAnimationFrame` 的執行時間是由浏覽器的重新整理率來決定的,通常是每秒 60 次。

function animate() {

// Perform animation logic here

requestAnimationFrame(animate);

}

animate(); // Start the animation           

3. **setImmediate**:

`setImmediate` 用于在事件循環的檢查階段結束後立即執行回調函數。與 `setTimeout(fn, 0)` 不同,`setImmediate` 在 I/O 階段後執行,而 `setTimeout` 在定時器階段後執行。

setImmediate(() => {

console.log('This will be executed immediately after the I/O phase.');

});           

4. **process.nextTick**:

`process.nextTick` 是 Node.js 特有的函數,用于在目前執行階段結束後立即執行回調函數,優先級高于其他異步函數,包括 `setImmediate` 和 `setTimeout`。

process.nextTick(() => {

console.log('This will be executed immediately after the current execution phase.');

});           

這些定時執行函數允許在指定的時間間隔或特定的事件循環階段執行回調函數,使得 JavaScript 可以處理定時任務、動畫和異步操作,提高了代碼的靈活性和互動性。

CSS transition和transform的差別

CSS transition 和 CSS transform 是兩個在 CSS 中用于實作動畫效果的屬性,它們的作用和實作方式有所不同。

CSS Transition:

CSS transition 用于控制元素在一種狀态到另一種狀态之間的過渡效果。通過設定元素的屬性在一段時間内發生變化,實作平滑的過渡效果。transition 屬性需要指定過渡的屬性、過渡的持續時間、過渡的延遲時間、過渡的動畫速度曲線等。

/* 基本文法 */
transition: property duration timing-function delay;

/* 示例 */
div {
  width: 100px;
  height: 100px;
  background-color: red;
  transition: width 2s ease;
}

div:hover {
  width: 200px; /* 當滑鼠懸停時,寬度從 100px 過渡到 200px,持續時間為 2 秒,速度曲線為 ease。 */
}           

CSS Transform:

CSS transform 用于在元素的2D或3D空間中進行變換,如平移、旋轉、縮放、傾斜等。transform 屬性不會觸發過渡效果,而是瞬間将元素變換到指定的狀态。通過 transform 屬性,我們可以在不改變文檔流的情況下,改變元素的外觀和形狀。

/* 基本文法 */
transform: none | transform-functions;

/* 示例 */
div {
  width: 100px;
  height: 100px;
  background-color: red;
}

div:hover {
  transform: scale(1.2); /* 當滑鼠懸停時,元素放大1.2倍,沒有過渡效果,瞬間變換。 */
}           

差別:

  • CSS transition 用于實作屬性值的過渡效果,可以平滑地改變元素的外觀。
  • CSS transform 用于實作元素的變形效果,如平移、旋轉、縮放等,并沒有過渡效果,是瞬時變換。

雖然它們都可以用于建立動畫效果,但使用 CSS transition 可以在狀态之間實作平滑的過渡,而 CSS transform 則是用來立即變換元素的形狀或位置,不會産生過渡動畫。通常,在需要過渡效果的情況下,可以結合使用這兩個屬性來實作更複雜的動畫效果。

浏覽器緩存機制,HTTP緩存機制

浏覽器緩存機制和HTTP緩存機制是兩種不同的緩存機制,它們分别用于在浏覽器和Web伺服器之間緩存資源,以提高網頁加載速度和減少網絡請求。

**浏覽器緩存機制**:

浏覽器緩存機制是指浏覽器在第一次請求資源後,會将資源存儲在本地,下次請求相同的資源時,會直接從本地緩存中擷取,而不需要再向伺服器發起請求。這樣可以減少網絡請求,提高頁面加載速度。

浏覽器緩存機制可以通過設定HTTP頭來實作,主要有以下幾種方式:

- **強緩存**:浏覽器在緩存有效期内直接從本地緩存擷取資源,不發送請求到伺服器。通過設定 `Cache-Control` 和 `Expires` 來控制資源的緩存時間。

- **協商緩存**:在緩存過期後,浏覽器會向伺服器發送請求,伺服器通過比較資源的最後修改時間或ETag來判斷資源是否已經改變,如果資源沒有改變,則傳回狀态碼304 Not Modified,并在響應頭中設定 `Cache-Control` 或 `Expires` 來延長緩存時間。

**HTTP緩存機制**:

HTTP緩存機制是指Web伺服器在響應資源請求時,通過設定響應頭來控制資源的緩存行為。HTTP緩存機制主要通過設定以下響應頭來實作:

- **Cache-Control**:用于控制資源的緩存行為,如`public`表示資源可以被緩存且可以被代理伺服器緩存,`private`表示資源隻能被用戶端緩存而不能被代理伺服器緩存,`max-age`表示緩存的最大有效時間等。

- **Expires**:用于設定資源的過期時間,是一個GMT格式的時間字元串,告訴浏覽器資源的緩存時間。

- **ETag**:用于辨別資源的唯一性,伺服器會根據資源的内容生成一個唯一的辨別,當資源内容發生變化時,ETag的值也會改變。

HTTP緩存機制主要是由Web伺服器設定,通過設定響應頭的相關字段來告訴浏覽器資源的緩存行為和有效期,使得浏覽器可以根據這些資訊來進行資源的緩存和更新。

重繪和回流是什麼?

重繪(Repaint)和回流(Reflow)是浏覽器中兩個關鍵的渲染過程,它們是網頁性能優化中需要重點關注的問題。

**重繪(Repaint)**:

重繪是指當元素的樣式改變,但不影響其在文檔流中的位置和大小時,浏覽器會重新繪制元素的外觀,但不會觸釋出局的改變。重繪是相對較快的操作,隻需要更新元素的樣式,并重新繪制頁面的某個區域。

**回流(Reflow)**:

回流是指當元素的布局屬性發生改變,比如寬度、高度、位置等,浏覽器會重新計算元素的大小和位置,并重新建構頁面的布局樹和渲染樹。回流是一種比較耗時的操作,它會導緻整個頁面的重新渲染,影響了性能。

在頁面渲染的過程中,重繪和回流是緊密相關的,當元素的樣式改變時,可能會引發回流,而回流又會導緻重繪。是以,過多的重繪和回流會影響頁面的性能,導緻頁面渲染速度變慢,産生卡頓和閃爍的現象。

為了優化頁面的性能,我們需要盡量減少重繪和回流的次數。一些常見的優化方法包括:

- 使用`transform` 替代 `top`、`left` 等定位屬性,因為 `transform` 不會觸發回流。

- 避免頻繁操作樣式,可以使用 CSS 類進行一次性的樣式修改。

- 将多次操作合并為一次操作,比如使用文檔片段(DocumentFragment)進行多次節點插入。

- 使用`display: none`來隐藏元素,而不是`visibility: hidden`,因為前者不會觸發回流。

- 避免在循環中頻繁操作樣式或布局屬性。

通過優化避免不必要的重繪和回流,可以顯著提升頁面的性能和使用者體驗。