天天看點

2023.05.31 更新前端面試問題總結(9道題)

作者:成都程式員晴小篆

2023.05.29 - 2023.05.31 更新前端面試問題總結(9道題)

擷取更多面試相關問題可以通路

github 位址: https://github.com/pro-collection/interview-question/issues

gitee 位址: https://gitee.com/yanleweb/interview-question/issues

目錄:

  • 中級開發者相關問題【共計 4 道題】
    • 387.Generator 是如何做到中斷和恢複的【熱度: 1,558】【JavaScript】【出題公司: 百度】
    • 388.哪些原因會導緻js裡this指向混亂?【熱度: 1,282】【JavaScript】【出題公司: 小米】
    • 389.JS 作用域鍊連結多少?【熱度: 882】【JavaScript】【出題公司: 美團】
    • 393.實作一個JS 函數, 解析 url 參數, 傳回一個對象【JavaScript】【出題公司: Shopee】
  • 進階開發者相關問題【共計 5 道題】
    • 385.使用 Promise 實作一個異步流量控制的函數【熱度: 517】【JavaScript】【出題公司: 騰訊】
    • 386.不使用任何中間件, koa 如何解析 post 請求參數【熱度: 1,359】【Nodejs】【出題公司: 阿裡巴巴】
    • 390.webpack5 Module Federation 了解多少【工程化】【出題公司: 京東】
    • 391.小程式為什麼會有兩個線程【web應用場景】【出題公司: Shopee】
    • 392.[React] react-router 頁面跳轉時,是如何傳遞下一個頁面參數的?【web架構】【出題公司: 騰訊】

中級開發者相關問題【共計 4 道題】

387.Generator 是如何做到中斷和恢複的【熱度: 1,558】【JavaScript】【出題公司: 百度】

關鍵詞:Generator 中斷、Generator 回複

Generator 是 JavaScript 中一種特殊的函數,它能夠通過疊代器協定(Iterator Protocol)實作中斷和恢複的功能。

Generator 函數使用 function* 聲明,内部可以使用 yield 關鍵字來定義中斷點。當調用 Generator 函數時,它不會立即執行,而是傳回一個疊代器對象。通過調用疊代器的 next() 方法,可以逐漸執行

Generator 函數,并在每個 yield 關鍵字處暫停執行并傳回一個包含目前值的對象。

當調用 next() 方法時,Generator 函數會從上次暫停的地方繼續執行,直到遇到下一個 yield 關鍵字或函數結束。通過不斷調用 next() 方法,可以逐漸執行 Generator

函數的代碼,并擷取每個中斷點處的值。

由于 Generator 函數具有中斷和恢複的特性,可以用于異步程式設計,實作一種更直覺的方式來處理異步操作。通過 yield 關鍵字,可以将異步操作分割成多個步驟,每個步驟都可以通過 yield 暫停,等待異步操作完成後再恢複執行。

以下是一個簡單的示例,展示了 Generator 函數的中斷和恢複特性:

function* generatorFunction() {
  console.log('Step 1');
  yield;
  console.log('Step 2');
  yield;
  console.log('Step 3');
}

const generator = generatorFunction();

generator.next(); // Step 1
generator.next(); // Step 2
generator.next(); // Step 3
           

在上述示例中,我們定義了一個名為 generatorFunction 的 Generator 函數。在函數體内,使用 console.log 列印了三個不同的步驟,并在每個步驟後使用 yield

關鍵字暫停執行。然後,我們通過調用 generator.next() 方法逐漸執行 Generator 函數。每次調用 next() 方法時,函數會從上次暫停的地方恢複執行,列印相應的步驟。

通過使用 Generator 函數,可以實作更靈活、可控的異步程式設計模式,提供更好的代碼可讀性和維護性。

388.哪些原因會導緻js裡this指向混亂?【熱度: 1,282】【JavaScript】【出題公司: 小米】

關鍵詞:js 指向

JavaScript 中 this 指向混亂的原因主要有以下幾個:

  1. 函數調用方式不同:JavaScript 中函數的調用方式決定了 this 的指向。常見的函數調用方式有函數調用、方法調用、構造函數調用和箭頭函數調用。不同的調用方式會導緻 this 指向不同的對象,容易引發混亂。
  2. 丢失綁定:當函數作為一個獨立的變量傳遞時,或者作為回調函數傳遞給其他函數時,函數内部的 this 可能會丢失綁定。這意味着函數中的 this 不再指向原來的對象,而是指向全局對象(在浏覽器環境中通常是 window 對象)或

    undefined(在嚴格模式下)。

  3. 嵌套函數:當函數嵌套在其他函數内部時,嵌套函數中的 this 通常會與外部函數的 this 不同。這可能導緻 this 的指向出現混亂,特别是在多層嵌套的情況下。
  4. 使用 apply、call 或 bind 方法:apply、call 和 bind 是 JavaScript 中用于顯式指定函數的 this 的方法。如果不正确使用這些方法,比如傳遞了錯誤的上下文對象,就會導緻 this 指向錯誤。
  5. 箭頭函數:箭頭函數具有詞法作用域的 this 綁定,它會捕獲其所在上下文的 this 值,而不是動态綁定 this。是以,在箭頭函數中使用 this 時,它指向的是箭頭函數聲明時的上下文,而不是調用時的上下文。

為了避免 this 指向混亂的問題,可以采取以下措施:

  • 使用箭頭函數,確定 this 始終指向期望的上下文。
  • 在函數調用時,確定正确設定了函數的上下文對象,可以使用 bind、call 或 apply 方法。
  • 使用嚴格模式,避免函數内部的 this 預設綁定到全局對象。
  • 在嵌套函數中,使用箭頭函數或者顯式儲存外部函數的 this 值,以避免内部函數的 this 指向錯誤。

了解和正确處理 this 的指向是 JavaScript 開發中重要的一環,它能幫助我們避免許多常見的錯誤和混亂。

389.JS 作用域鍊連結多少?【熱度: 882】【JavaScript】【出題公司: 美團】

關鍵詞:JS 作用域鍊鍊、JS 作用域鍊鍊應用

概念

JavaScript 作用域鍊(Scope Chain)是指變量和函數的可通路性和查找規則。它是由多個執行上下文(Execution Context)的變量對象(Variable Object)按照它們被建立的順序組成的鍊式結構。

在 JavaScript 中,每個函數都會建立一個新的執行上下文,并将其添加到作用域鍊的最前端。當通路一個變量時,JavaScript

引擎會先從目前執行上下文的變量對象開始查找,如果找不到,則沿着作用域鍊依次向上查找,直到全局執行上下文的變量對象。

作用域鍊的建立過程如下:

  1. 在函數定義時,會建立一個變量對象(VO)來存儲函數的變量和函數聲明。這個變量對象包含了目前函數的作用域中的變量和函數。
  2. 在函數執行時,會建立一個執行上下文(Execution Context),并将其添加到作用域鍊的最前端。執行上下文中的變量對象稱為活動對象(Active Object)。
  3. 當通路一個變量時,JavaScript 引擎首先會在活動對象中查找,如果找不到,則沿着作用域鍊依次向上查找,直到全局執行上下文的變量對象。
  4. 如果在作用域鍊的任何一個環節找到了變量,則停止查找并傳回變量的值;如果未找到,則抛出引用錯誤(ReferenceError)。

作用域鍊的特點:

  1. 作用域鍊是一個靜态的概念,它在函數定義時就确定了,不會随着函數的調用而改變。
  2. 作用域鍊是由多個執行上下文的變量對象按照它們被建立的順序組成的。
  3. 作用域鍊的最後一個變量對象是全局執行上下文的變量對象,它是作用域鍊的終點。
  4. 内部函數可以通路外部函數的變量,因為内部函數的作用域鍊包含了外部函數的變量對象。

有哪些應用場景

作用域鍊在 JavaScript 中具有廣泛的應用場景。下面列舉了一些常見的應用場景:

  1. 變量查找:作用域鍊決定了變量的通路順序,當通路一個變量時,會按照作用域鍊的順序依次查找變量,直到找到比對的變量或到達全局作用域。
  2. 閉包:閉包是指函數能夠通路和操作它的外部函數中定義的變量。通過作用域鍊,内部函數可以通路外部函數的變量,實作了閉包的特性。閉包在許多場景中用于建立私有變量和實作函數封裝。
  3. 垃圾回收:JavaScript 的垃圾回收機制通過作用域鍊來判斷變量的生命周期。當變量不再被引用時,垃圾回收器可以回收它所占用的記憶體空間。
  4. 函數作為參數傳遞:在 JavaScript 中,可以将函數作為參數傳遞給其他函數。在傳遞過程中,作用域鍊決定了内部函數對外部函數變量的通路權限,實作了回調函數和高階函數的功能。
  5. 面向對象程式設計:JavaScript 中的對象和原型鍊是基于作用域鍊實作的。通過原型鍊,對象可以通路和繼承其原型對象的屬性和方法。
  6. 子產品化開發:作用域鍊可以用于實作子產品化開發,通過定義私有變量和公共接口,控制子產品内部變量的可通路性,避免變量沖突和全局污染。
  7. 作用域鍊的動态改變:在 JavaScript 中,可以通過閉包和動态作用域的特性來改變作用域鍊。例如,使用 eval() 函數或 with 語句可以改變目前的作用域鍊。

總之,作用域鍊在 JavaScript 中扮演了重要的角色,涵蓋了變量的通路、閉包、垃圾回收、子產品化開發等多個方面。深入了解作用域鍊對于編寫高品質的 JavaScript 代碼和了解其底層工作原理非常重要。

393.實作一個JS 函數, 解析 url 參數, 傳回一個對象【JavaScript】【出題公司: Shopee】

基礎版本

下面是一個簡單的 JavaScript 函數,用于解析 URL 參數并傳回一個包含參數鍵值對的對象:

function parseUrlParams(url) {
  const params = {};
  const queryString = url.split('?')[1];

  if (queryString) {
    const pairs = queryString.split('&');
    pairs.forEach(pair => {
      const [key, value] = pair.split('=');
      params[key] = decodeURIComponent(value);
    });
  }

  return params;
}
           

這個函數接受一個 URL 字元串作為參數,并傳回解析後的參數對象。例如:

const url = 'https://example.com?name=John&age=30&city=New%20York';
const params = parseUrlParams(url);

console.log(params);
// Output: { name: "John", age: "30", city: "New York" }
           

這個函數的實作思路是先從 URL 字元串中提取查詢字元串部分,然後将查詢字元串按照 & 分割成鍵值對數組。接着周遊鍵值對數組,将每個鍵值對按照 = 分割,然後将鍵和值存儲到結果對象 params 中,注意要對值進行 URL

解碼以處理特殊字元。最後傳回解析後的參數對象。

進階 - 支援json字元串參數

如果要支援複雜的 JSON 字元串作為查詢參數,可以使用 JSON.parse() 方法解析 JSON 字元串,并在解析後的對象中處理參數。

下面是一個修改後的函數,支援解析複雜的 JSON 字元串作為查詢參數:

function parseUrlParams(url) {
  const params = {};
  const queryString = url.split('?')[1];

  if (queryString) {
    const pairs = queryString.split('&');
    pairs.forEach(pair => {
      const [key, value] = pair.split('=');
      const decodedValue = decodeURIComponent(value);

      try {
        params[key] = JSON.parse(decodedValue);
      } catch (error) {
        // 如果解析 JSON 失敗,則将原始字元串存儲到參數對象中
        params[key] = decodedValue;
      }
    });
  }

  return params;
}
           

現在,如果查詢參數是一個 JSON 字元串,它将被解析為相應的 JavaScript 對象,并作為參數對象的值。如果解析失敗(例如,不是有效的 JSON 字元串),則将保留原始字元串作為值存儲在參數對象中。

以下是一個示例:

const url = 'https://example.com?name=John&age=30&address={"city":"New York","zipcode":10001}';
const params = parseUrlParams(url);

console.log(params);
// Output: { name: "John", age: "30", address: { city: "New York", zipcode: 10001 } }
           

再次進階-支援更複雜的場景, 比如嵌套對象, 數組

下面是修改後的函數,支援解析複雜的查詢參數,包括嵌套對象和數組:

function parseUrlParams(url) {
  const params = {};
  const queryString = url.split('?')[1];

  if (queryString) {
    const pairs = queryString.split('&');
    pairs.forEach(pair => {
      const [key, value] = pair.split('=');
      const decodedValue = decodeURIComponent(value);

      const keys = key.split('.');
      let current = params;

      for (let i = 0; i < keys.length; i++) {
        const nestedKey = keys[i];
        const isArray = /\[\]$/.test(nestedKey);

        if (isArray) {
          const arrayKey = nestedKey.slice(0, -2);

          if (!current[arrayKey]) {
            current[arrayKey] = [];
          }

          if (i === keys.length - 1) {
            current[arrayKey].push(parseValue(decodedValue));
          } else {
            const newIndex = current[arrayKey].length;
            if (!current[arrayKey][newIndex]) {
              current[arrayKey][newIndex] = {};
            }
            current = current[arrayKey][newIndex];
          }
        } else {
          if (i === keys.length - 1) {
            current[nestedKey] = parseValue(decodedValue);
          } else {
            if (!current[nestedKey]) {
              current[nestedKey] = {};
            }
            current = current[nestedKey];
          }
        }
      }
    });
  }

  return params;
}

function parseValue(value) {
  try {
    return JSON.parse(value);
  } catch (error) {
    // 解析失敗,則傳回原始值
    return value;
  }
}
           

現在,該函數可以正确解析包含嵌套對象和數組的查詢參數。

以下是一個示例:

const url = 'https://example.com?name=John&age=30&address.city=New%20York&address.zipcode=10001&tags[]=tag1&tags[]=tag2';
const params = parseUrlParams(url);

console.log(params);
// Output: { name: "John", age: "30", address: { city: "New York", zipcode: 10001 }, tags: ["tag1", "tag2"] }
           

在這個修改後的函數中,當遇到嵌套對象時,它會遞歸建立相應的對象屬性。當遇到數組時,它會建立一個數組,并将值添加到數組中。

進階開發者相關問題【共計 5 道題】

385.使用 Promise 實作一個異步流量控制的函數【熱度: 517】【JavaScript】【出題公司: 騰訊】

關鍵詞:異步流量控制的函數

下面是使用 Promise 實作異步流量控制的函數的示例:

function asyncFlowControl(tasks, limit) {
  let runningCount = 0; // 目前正在運作的任務數
  let index = 0; // 目前執行的任務索引
  const results = []; // 存儲任務的結果

  return new Promise((resolve, reject) => {
    function runTask() {
      if (runningCount >= limit || index >= tasks.length) {
        // 達到并發限制或所有任務已執行完畢,傳回結果
        if (results.length === tasks.length) {
          resolve(results);
        }
        return;
      }

      const task = tasks[index];
      const currentIndex = index; // 儲存目前任務索引

      index++;
      runningCount++;

      task().then((result) => {
        results[currentIndex] = result; // 存儲任務結果
        runningCount--;
        runTask(); // 遞歸執行下一個任務
      }).catch((error) => {
        reject(error);
      });

      runTask(); // 遞歸執行下一個任務
    }

    runTask(); // 開始執行任務
  });
}

// 示例用法
function asyncTask(value) {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(value);
      resolve(value);
    }, Math.random() * 1000);
  });
}

const tasks = [
  () => asyncTask(1),
  () => asyncTask(2),
  () => asyncTask(3),
  () => asyncTask(4),
  () => asyncTask(5),
];

asyncFlowControl(tasks, 2).then((results) => {
  console.log('All tasks completed:', results);
}).catch((error) => {
  console.error('Error occurred:', error);
});
           

以上示例中的 asyncFlowControl 函數接受一個任務數組 tasks 和一個并發限制 limit,它會按照并發限制逐個執行任務,并傳回一個 Promise 對象。在示例中,任務數組中的每個任務都是一個傳回

Promise 的函數,通過 setTimeout 模拟異步操作。

在執行過程中,asyncFlowControl 函數會維護一個 runningCount 變量來跟蹤目前正在運作的任務數,并使用遞歸的方式執行任務。當達到并發限制或所有任務都已執行完畢時,函數會傳回結果。

通過控制并發任務的數量,我們可以限制同時執行的異步操作,實作異步流量控制。在上述示例中,設定并發限制為 2,可以確定最多同時執行 2 個任務,并在任務執行完畢後再執行下一個任務。

386.不使用任何中間件, koa 如何解析 post 請求參數【熱度: 1,359】【Nodejs】【出題公司: 阿裡巴巴】

關鍵詞:koa 請求、koa 解析、koa body-parser

如果你不想使用任何中間件來解析 POST 請求參數,你可以手動解析請求體資料。在 Koa 中,你可以通過以下步驟來解析 POST 請求的參數:

  1. 使用 ctx.req 擷取原始的 Node.js 請求對象。
  2. 将請求對象的資料流通過 ctx.req.on('data', ...) 事件監聽進行讀取。
  3. 将讀取到的資料流進行處理,根據請求頭的 Content-Type 判斷資料格式,可以是 application/json 或 application/x-www-form-urlencoded。
  4. 将處理後的資料轉換為 JavaScript 對象或其他格式進行進一步處理。

以下是一個示例:

const Koa = require('koa');
const app = new Koa();

app.use(async (ctx) => {
  if (ctx.method === 'POST') {
    // 手動解析 POST 請求的參數
    const requestData = await parseRequestBody(ctx.req);
    // 處理請求參數
    // ...
    ctx.body = 'POST request received';
  } else {
    ctx.body = 'Hello, Koa!';
  }
});

function parseRequestBody(req) {
  return new Promise((resolve, reject) => {
    let data = '';
    req.on('data', (chunk) => {
      data += chunk;
    });
    req.on('end', () => {
      // 根據請求頭的 Content-Type 判斷資料格式
      if (req.headers['content-type'] === 'application/json') {
        // 解析 JSON 格式資料
        try {
          resolve(JSON.parse(data));
        } catch (error) {
          reject(error);
        }
      } else if (req.headers['content-type'] === 'application/x-www-form-urlencoded') {
        // 解析 URL 編碼格式資料
        const parsedData = {};
        const keyValuePairs = data.split('&');
        for (const pair of keyValuePairs) {
          const [key, value] = pair.split('=');
          parsedData[key] = decodeURIComponent(value);
        }
        resolve(parsedData);
      } else {
        reject(new Error('Unsupported content type'));
      }
    });
    req.on('error', (error) => {
      reject(error);
    });
  });
}

app.listen(3000, () => {
  console.log('Server started on port 3000');
});
           

在上述示例中,我們在中間件函數中手動解析 POST 請求的參數。parseRequestBody 函數使用 ctx.req 擷取原始的 Node.js 請求對象,并通過監聽 data

事件将請求體資料流進行讀取。然後,根據請求頭的 Content-Type 判斷資料格式,如果是 application/json,則使用 JSON.parse 解析為 JavaScript

對象;如果是 application/x-www-form-urlencoded,則将資料轉換為鍵值對對象。最後,将解析後的資料傳遞給處理函數進行進一步處理。

請注意,手動解析請求參數可能更複雜且容易出錯,而使用中間件能夠更友善地處理和解析請求體資料。是以,在實際開發中,推薦使用合适的中間件來解析請求參數。

390.webpack5 Module Federation 了解多少【工程化】【出題公司: 京東】

概念

Webpack 5 的 Module Federation 是一項功能強大的功能,它允許将 JavaScript 應用程式拆分成獨立的子產品,并在不同的 Webpack

建構中共享這些子產品。它解決了多個獨立應用程式之間共享代碼的問題,使得實作微前端架構變得更加容易。

Module Federation

可以将一個應用程式拆分成多個獨立的子應用,每個子應用都可以被獨立開發、部署和運作。每個子應用都可以通過配置指定需要共享的子產品,然後将這些共享子產品以動态方式加載到其他子應用中使用,而無需将這些子產品打包進每個子應用的建構檔案中。

Module Federation 的核心概念是 “容器”(Container)和 “遠端”(Remote)。容器是一個主應用程式,它可以加載和渲染其他子應用程式,而遠端是一個獨立的子應用程式,它提供了一些子產品供其他子應用程式使用。

Module Federation

提供了一種簡單的方式來定義遠端子產品,并在容器中引用這些遠端子產品。容器可以從遠端加載子產品,并通過配置将這些子產品暴露給其他子應用程式。這樣,子應用程式可以通過遠端加載和使用容器中的子產品,實作了子產品的共享和動态加載。

Module Federation

在實作微前端架構時非常有用,可以将多個獨立開發的子應用程式組合成一個整體,并實作共享子產品和資源的靈活管理。它提供了一種解決方案,讓多個團隊可以獨立開發和部署自己的子應用程式,同時又能夠共享代碼和資源,提高開發效率和整體性能。

Webpack 5 的 Module Federation 是一項用于實作微前端架構的功能,它可以将 JavaScript 應用程式拆分成獨立的子應用程式,并通過動态加載和共享子產品的方式實作子應用程式之間的互動和共享。

使用示範

下面是一個簡單的示例,示範如何在 Webpack 5 中使用 Module Federation。

假設我們有兩個獨立的應用程式:App1 和 App2。我們将使用 Module Federation 将 App2 的子產品共享給 App1。

首先,我們需要在 App2 的 Webpack 配置中啟用 Module Federation:

// webpack.config.js (App2)

const { ModuleFederationPlugin } = require('webpack');

module.exports = {
  // ...其他配置
  plugins: [
    new ModuleFederationPlugin({
      name: 'app2',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/Button', // 暴露 App2 的 Button 子產品
      },
    }),
  ],
};
           

接下來,我們需要在 App1 的 Webpack 配置中配置遠端加載 App2 的子產品:

// webpack.config.js (App1)

const { ModuleFederationPlugin } = require('webpack');

module.exports = {
  // ...其他配置
  plugins: [
    new ModuleFederationPlugin({
      name: 'app1',
      remotes: {
        app2: 'app2@http://localhost:3002/remoteEntry.js', // 遠端加載 App2 的子產品
      },
    }),
  ],
};
           

在 App1 中,我們可以像使用本地子產品一樣使用 App2 的子產品:

// App1

import React from 'react';
import ReactDOM from 'react-dom';
import App2Button from 'app2/Button'; // 遠端加載 App2 的 Button 子產品

ReactDOM.render(<App2Button/>, document.getElementById('root'));
           

在上面的示例中,我們通過 Module Federation 将 App2 的 Button 子產品暴露給了 App1,然後在 App1 中可以直接通過 import 語句引入并使用。

需要注意的是,App1 需要在 remotes 配置中指定遠端加載的子產品,其中 app2 是一個遠端子產品的名稱,而 http://localhost:3002/remoteEntry.js 是 App2 建構輸出的遠端入口檔案。

這隻是一個簡單的示例,實際使用中可能涉及更複雜的配置和場景。但通過以上配置,我們可以實作在不同應用程式之間共享子產品,并通過動态加載的方式使用遠端子產品。

391.小程式為什麼會有兩個線程【web應用場景】【出題公司: Shopee】

小程式之是以有兩個線程,是為了實作小程式的高效運作和良好的使用者體驗。

  1. 渲染線程(UI 線程): 渲染線程負責小程式界面的渲染和響應使用者的互動。它使用 WebView 進行頁面渲染,包括解析和繪制 DOM、布局、樣式計算和渲染等操作。渲染線程是單線程的,所有的界面操作都在這個線程中進行。
  2. 邏輯線程(JS 線程): 邏輯線程負責小程式的邏輯運算和資料處理。它是基于 JavaScript

    運作的,負責處理使用者互動、業務邏輯、資料請求、事件處理等操作。邏輯線程是獨立于渲染線程的,可以并行處理多個任務,避免阻塞界面的渲染和響應。

将界面渲染和邏輯運算分離成兩個線程的設計有以下好處:

  • 響應速度:邏輯線程和渲染線程分開,可以并行執行,提高了小程式的響應速度和使用者體驗。
  • 防止阻塞:邏輯線程的運作不會阻塞渲染線程,避免了長時間的計算或資料處理導緻界面卡頓或無響應的情況。
  • 資源隔離:渲染線程和邏輯線程是獨立的,它們有各自的資源和運作環境,可以避免互相幹擾和影響。

需要注意的是,小程式的渲染線程和邏輯線程之間通過微信用戶端進行通信和互動。邏輯線程可以發送請求給微信用戶端,然後用戶端将渲染指令發送給渲染線程進行界面渲染,同時渲染線程可以将使用者的互動事件發送給邏輯線程進行處理。這種通信方式保證了渲染和邏輯的協同工作,實作了小程式的正常運作。

小程式之是以有兩個線程,是為了提高渲染速度、避免阻塞和資源隔離。渲染線程負責界面渲染,邏輯線程負責業務邏輯和資料處理,兩者通過微信用戶端進行通信和互動,共同實作小程式的功能和性能。

392.[React] react-router 頁面跳轉時,是如何傳遞下一個頁面參數的?【web架構】【出題公司: 騰訊】

路由資料

React Router 是一個用于管理前端路由的庫,它與 React 應用程式內建在一起,提供了一種在單頁面應用中處理路由的方式。React Router 并沒有直接提供資料存儲的功能,它主要負責路由的比對和導航。

在 React Router 中,路由相關的資料主要存儲在元件的 props 群組件的狀态中。以下是一些常見的資料存儲方式:

  1. 路由參數(Route Parameters): React Router 允許通過路徑參數(如 /users/:id)傳遞參數給路由元件。這些參數可以通過 props.match.params

    對象在路由元件中擷取。路由參數通常用于辨別唯一資源的ID或其他需要動态變化的資料。

  2. 查詢參數(Query Parameters): 查詢參數是通過 URL 查詢字元串傳遞的鍵值對資料,如 /users?id=123&name=John。React Router

    可以通過 props.location.search 屬性擷取查詢字元串,并通過解析庫(如 query-string)将其轉換為 JavaScript 對象。查詢參數通常用于篩選、分頁或其他需要傳遞額外資料的場景。

  3. 路由狀态(Route State): 在某些情況下,可能需要将一些狀态資訊傳遞給路由元件,例如從一個頁面跳轉到另一個頁面時需要攜帶一些額外的狀态。React Router 提供了 props.location.state

    屬性,可以用于存儲和傳遞路由狀态。

  4. 上下文(Context): React Router 提供了一個 Router 元件,可以使用 React 的上下文功能共享路由相關的資料。通過在 Router 元件的上下文中提供資料,可以在路由元件中通路該資料,而無需通過

    props 層層傳遞。這在需要在多個嵌套層級中通路路由資料時非常友善。

總的來說,React Router 并沒有專門的資料存儲機制,它主要利用 React 元件的 props

和狀态來傳遞和存儲路由相關的資料。這些資料可以通過路由參數、查詢參數、路由狀态以及上下文等方式來傳遞和擷取。根據具體的需求和場景,可以選擇适合的方式來存儲和管理路由相關的資料。

路由狀态是如何存儲的

在 React Router 中,路由狀态可以通過 props.location.state 屬性來存儲和擷取。

當使用 React Router 進行頁面導航時,可以通過 history.push 或 history.replace 方法傳遞一個包含狀态資料的對象作為第二個參數。例如:

history.push('/dashboard', { isLoggedIn: true, username: 'John' });
           

這個對象會被存儲在新頁面的 props.location.state 中,可以在目标頁面的元件中通過 props.location.state 來通路它。例如:

import { useLocation } from 'react-router-dom';

function Dashboard() {
  const location = useLocation();
  const { isLoggedIn, username } = location.state;

  // 使用路由狀态資料
  // ...
}
           

需要注意的是,路由狀态僅在通過 history.push 或 history.replace 導航到新頁面時才可用。如果使用者通過浏覽器的前進/後退按鈕進行導航,或者直接輸入 URL 位址通路頁面,路由狀态将不會被保留。

另外,路由狀态也可以在類元件中通過 this.props.location.state 進行通路,或者在函數元件中使用 props.location.state。

props.location.state 資料是如何存儲的

在 React Router 中,路由狀态資料實際上是存儲在用戶端的記憶體中。

當使用 history.push 或 history.replace 方法導航到一個新頁面時,React Router

将路由狀态資料作為對象附加到浏覽器曆史記錄中的對應路由條目。這個對象會存儲在浏覽器的會話曆史中,并在新頁面加載時被 React Router 讀取并提供給元件。

具體地說,React Router 使用 HTML5 的 History API(pushState 或 replaceState

方法)來實作路由導航,并将路由狀态資料作為一個特殊的字段存儲在曆史記錄中。這個字段通常被稱為 state 字段,用于存儲路由狀态資料。

在浏覽器中,曆史記錄和相應的狀态資料會被儲存在記憶體中。當使用者進行前進、後退或直接通路某個 URL 時,浏覽器會根據曆史記錄加載對應的頁面,并将相關的狀态資料提供給 React

Router。這樣,元件就能夠通過 props.location.state 來通路之前存儲的路由狀态資料。

需要注意的是,路由狀态資料僅在用戶端記憶體中存在,每個使用者的路由狀态是獨立的。如果使用者重新整理頁面或關閉浏覽器,路由狀态資料将丢失,并需要重新通過導航操作來設定。是以,路由狀态适合存儲短期或臨時的資料,而對于長期或持久化的資料,應該考慮其他的資料存儲方式,如伺服器端存儲或狀态管理庫。

繼續閱讀