首頁 > web前端 > js教程 > 使用 AI/ML API、Deepgram Aura 和 IndexedDB 整合從頭開始建立 Chrome 擴充功能

使用 AI/ML API、Deepgram Aura 和 IndexedDB 整合從頭開始建立 Chrome 擴充功能

Linda Hamilton
發布: 2024-10-26 19:52:03
原創
945 人瀏覽過

介紹

建立利用 AI 技術的 Chrome 擴充功能可以透過直接向瀏覽器添加強大的功能來顯著增強用戶體驗。

在本教程中,我們將介紹使用 AI/ML API、Deepgram Aura 和 IndexDB 從頭開始建立 Chrome 擴充功能的整個過程,從設定到部署。我們將從設定開發環境開始,包括安裝必要的工具和配置我們的專案。然後,我們將深入研究 Chrome 擴充功能的核心元件:manifest.json 包含有關擴充功能的基本元數據,scripts.js 負責我們的擴充功能的行為方式,styles.css 用於添加一些樣式。我們將探索如何透過 AI/ML API 將這些技術與 Deepgram Aura 集成,並使用 IndexDB 作為生成的音訊檔案的臨時儲存。在此過程中,我們將討論建立 Chrome 擴充功能、處理使用者查詢以及在資料庫中保存資料的最佳實踐。學完本教學後,您將在建立 Chrome 擴充功能方面打下堅實的基礎,並具備建立任何人工智慧驅動的 Chrome 擴充功能的能力。

讓我們簡單概述一下我們將要使用的技術。

人工智慧/機器學習API

AI/ML API 是一個改變遊戲規則的平台,適合希望將尖端 AI 功能整合到其產品中的開發人員和 SaaS 企業家。 AI/ML API 提供對 200 多個最先進的 AI 模型的單點訪問,涵蓋從 NLP 到電腦視覺的所有內容。

開發者的主要功能:

  • 豐富的模型庫:200 個預訓練模型,用於快速原型設計和部署
  • 自訂選項:微調模型以適合您的特定用例
  • 開發人員友善的整合:RESTful API 和 SDK,可無縫合併到您的堆疊中
  • 無伺服器架構:專注於編碼,而不是基礎設施管理

深入研究 AI/ML API 文件; https://docs.aimlapi.com/

Chrome 擴充功能

Chrome 擴充功能是一個小型軟體程序,可修改或增強 Google Chrome 網路瀏覽器的功能。這些擴充功能是使用 HTML、CSS 和 JavaScript 等 Web 技術建構的,旨在服務於單一目的,使它們易於理解和使用。

瀏覽 Chrome 線上應用程式商店; https://chromewebstore.google.com/

深層光環

Deepgram Aura 是第一個專為即時對話式 AI 代理和應用程式設計的文字轉語音 (TTS) AI 模型。它以無與倫比的速度和效率提供類人語音質量,使其成為構建響應靈敏、高吞吐量的語音 AI 體驗的遊戲規則改變者。

了解更多技術細節; https://aimlapi.com/models/aura

索引資料庫

IndexedDB 是一個低階 API,用於客戶端儲存大量結構化資料(包括檔案/blob)。 IndexedDB 是一個基於 JavaScript 的物件導向資料庫。

了解更多關鍵概念和用法; https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API

Chrome 擴充功能入門

建立 Chrome 擴充功能需要了解其結構、權限以及它如何與網頁互動。我們將首先設定開發環境並建立擴充所需的基礎檔案。

設定您的開發環境

在我們開始編碼之前,請確保您具備以下條件:

  • Chrome 瀏覽器:我們將在其中載入和測試擴充功能的瀏覽器。
  • 文字編輯器或 IDE:Visual Studio Code、Sublime Text 或 Atom 等工具適合編輯程式碼。我們將在本教學中使用 Visual Studio Code。
  • HTML、CSS 和 JavaScript 的基本知識:熟悉這些技術對於建立 Chrome 擴充功能至關重要。

建立專案結構

最小的 Chrome 擴充功能至少需要三個檔案:

  • manifest.json:包含擴充的元資料和配置。
  • scripts.js:保存定義擴充行為的 JavaScript 程式碼。
  • styles.css:包括擴充的 UI 元素的任何樣式。

讓我們為我們的專案建立一個目錄並設定這些檔案。
第 1 步:建立新目錄
打開終端機並執行以下命令為您的擴充功能建立一個新資料夾:

mkdir my-first-chrome-extension
cd my-first-chrome-extension
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

第 2 步:建立基本檔案
在新目錄中,建立必要的檔案:

touch manifest.json
touch scripts.js
touch styles.css
登入後複製
登入後複製
登入後複製
登入後複製

了解manifest.json

manifest.json 檔案是 Chrome 擴充功能的核心。它告訴瀏覽器您的擴充功能、它的用途以及它需要什麼權限。讓我們深入研究如何正確配置此文件。

{
  "manifest_version": 3,
  "name": "Read Aloud",
  "version": "1.0",
  "description": "Read Aloud anything in any tab",
  "host_permissions": [
    "*://*.aimlapi.com/*"
],
  "permissions": [
      "activeTab"
  ],
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["scripts.js"],
      "css": ["styles.css"]
    }
  ],
  "icons": {
    "16": "icons/icon.png",
    "48": "icons/icon.png",
    "128": "icons/icon.png"
  }
}
登入後複製
登入後複製
登入後複製
登入後複製

manifest.json 中的基本字段

manifest.json 必須至少包含:

  • manifest_version:指定清單檔案格式的版本。 Chrome 目前使用版本 3。
  • 名稱:您的擴充功能的名稱,它將顯示給使用者。
  • 版本:擴充程式的版本號,遵循語意版本控制。

新增元資料和權限

除了必要欄位之外,我們將新增:

  • 描述:您的擴充功能的簡短摘要。
  • host_permissions:指定擴充功能可以存取哪些網域。為了與 AI/ML API 集成,我們需要訪問 *.aimlapi.com。
  • 權限:聲明所需的特殊權限,例如存取活動標籤。
  • content_scripts:定義要注入網頁的腳本和樣式。
  • 圖示:提供各種尺寸的擴充圖示。

關鍵字段的解釋

  • manifest_version:設定為 3 以使用最新的 Chrome 擴充功能。
  • 名稱:我們將我們的擴充命名為“Read Aloud”,以反映其功能。
  • 版本:以「1.0」開頭表示初始版本。
  • 描述:「大聲朗讀任何標籤中的任何內容」告知使用者該擴充功能的用途。
  • host_permissions:通配符 *://*.aimlapi.com/* 允許擴充功能與 aimlapi.com 的任何子網域進行通信,這是 API 呼叫所必需的。
  • 權限:「activeTab」允許擴充功能與目前選項卡的內容進行互動。
  • content_scripts:指定將scripts.js 和styles.css 注入到所有網頁(「」)。
  • 圖示:擴充功能的引用圖示檔案(確保圖示目錄中有適當的圖示檔案)。

生成圖示

開啟瀏覽器並造訪 chatgpt.com。現在讓我們為 Chrome 擴充功能產生圖示。我們將使用一個圖示來實現不同的尺寸(完全沒問題)。

輸入以下提示:

為我的「Read Aloud」Chrome 擴充功能產生黑白圖示。此擴充功能允許用戶突出顯示網站中的特定文字並收聽它。它是由人工智慧驅動的 Chrome 擴充功能。背景應為白色且純色。

等待幾秒鐘,直到 ChatGPT 產生圖示(圖像)。點擊下載並將其重命名為 icon.png。然後放入icons資料夾內。

完成manifest.json

正確定義所有欄位後,您的manifest.json將使瀏覽器能夠理解並正確載入您的擴充功能。


開發 script.js

scripts.js 檔案包含控制擴充行為方式的邏輯。我們將概述您的腳本需要實現的關鍵功能。

變數和初始化

先設定必要的變數:

  • API 金鑰:您需要來自 AI/ML API 平台的 API 金鑰來驗證您的請求。
  • 疊加元素:為疊加層和「朗讀」按鈕建立 DOM 元素。
  • 選擇變數:儲存有關使用者選擇的文字及其位置的資訊。
mkdir my-first-chrome-extension
cd my-first-chrome-extension
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

處理文字選擇

您的擴充功能應該偵測使用者何時選擇網頁上的文字:

  • 事件監聽器:將 mouseup 事件監聽器附加到文件以偵測使用者何時完成選擇文字。
mkdir my-first-chrome-extension
cd my-first-chrome-extension
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
  • 選擇偵測:檢查所選文字是否不為空並儲存。
touch manifest.json
touch scripts.js
touch styles.css
登入後複製
登入後複製
登入後複製
登入後複製
  • 疊加層定位:計算疊加層的放置位置,使其靠近所選文字。
{
  "manifest_version": 3,
  "name": "Read Aloud",
  "version": "1.0",
  "description": "Read Aloud anything in any tab",
  "host_permissions": [
    "*://*.aimlapi.com/*"
],
  "permissions": [
      "activeTab"
  ],
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["scripts.js"],
      "css": ["styles.css"]
    }
  ],
  "icons": {
    "16": "icons/icon.png",
    "48": "icons/icon.png",
    "128": "icons/icon.png"
  }
}
登入後複製
登入後複製
登入後複製
登入後複製
  • 疊加管理:確保在新增疊加之前刪除任何現有疊加。
// Set your AIML_API_KEY key
const AIML_API_KEY = ''; // Replace with your AIML_API_KEY key

// Create the overlay
const overlay = document.createElement('div');
overlay.id = 'read-aloud-overlay';

// Create the "Read Aloud" button
const askButton = document.createElement('button');
askButton.id = 'read-aloud-button';
askButton.innerText = 'Read Aloud';

// Append the button to the overlay
overlay.appendChild(askButton);

// Variables to store selected text and range
let selectedText = '';
let selectedRange = null;
登入後複製
登入後複製

完整程式碼:

document.addEventListener('mouseup', (event) => {
  console.log('mouseup event: ', event);
  //...code
}
登入後複製
登入後複製

與 AI/ML API 交互

當使用者點選「朗讀」按鈕:

  • 輸入驗證:檢查所選文字是否符合長度要求。
const selection = window.getSelection();
const text = selection.toString().trim();
if (text !== '') {
  const range = selection.getRangeAt(0);
  const rect = range.getBoundingClientRect();
登入後複製
登入後複製
  • 停用按鈕:透過在處理過程中停用按鈕來防止多次點擊。
// Set the position of the overlay
overlay.style.top = `${window.scrollY + rect.top - 50}px`; // Adjust as needed
overlay.style.left = `${window.scrollX + rect.left + rect.width / 2 - 70}px`; // Adjust to center the overlay

selectedText = text;
selectedRange = range;
登入後複製
登入後複製
  • API 請求:使用選定的文字向 AI/ML API 發送 POST 請求,以進行文字轉語音轉換。
// Remove existing overlay if any
const existingOverlay = document.getElementById('read-aloud-overlay');
if (existingOverlay) {
  existingOverlay.remove();
}

// Append the overlay to the document body
document.body.appendChild(overlay);
} else {
  // Remove overlay if no text is selected
  const existingOverlay = document.getElementById('read-aloud-overlay');
  if (existingOverlay) {
    existingOverlay.remove();
  }
}
登入後複製
登入後複製
  • 錯誤處理:優雅地處理API請求期間發生的任何錯誤。
// Function to handle text selection
document.addEventListener('mouseup', (event) => {
  console.log('mouseup event: ', event);
  const selection = window.getSelection();
  const text = selection.toString().trim();
  if (text !== '') {
    const range = selection.getRangeAt(0);
    const rect = range.getBoundingClientRect();

    // Set the position of the overlay
    overlay.style.top = `${window.scrollY + rect.top - 50}px`; // Adjust as needed
    overlay.style.left = `${window.scrollX + rect.left + rect.width / 2 - 70}px`; // Adjust to center the overlay

    selectedText = text;
    selectedRange = range;

    // Remove existing overlay if any
    const existingOverlay = document.getElementById('read-aloud-overlay');
    if (existingOverlay) {
      existingOverlay.remove();
    }

    // Append the overlay to the document body
    document.body.appendChild(overlay);
  } else {
    // Remove overlay if no text is selected
    const existingOverlay = document.getElementById('read-aloud-overlay');
    if (existingOverlay) {
      existingOverlay.remove();
    }
  }
});
登入後複製
  • 音訊播放:收到音訊後,將其播放給使用者。
if (selectedText.length > 200) {
// ...code
}
登入後複製

使用 IndexedDB 進行存儲

要有效管理音訊檔案:

  • 開啟資料庫:建立或開啟 IndexedDB 資料庫來儲存音訊 blob。
// Disable the button
askButton.disabled = true;
askButton.innerText = 'Loading...';
登入後複製
  • 儲存音訊:從 API 接收音訊 blob 後將其儲存在 IndexedDB 中。
// Send the selected text to your AI/ML API for TTS
const response = await fetch('https://api.aimlapi.com/tts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${AIML_API_KEY}`, // Replace with your actual API key
  },
  body: JSON.stringify({
    model: '#g1_aura-asteria-en',  // Replace with your specific model if needed
    text: selectedText
  })
});
登入後複製
  • 檢索音訊:從 IndexedDB 取得音訊 blob 進行播放。
try {

  // ...code

  if (!response.ok) {
    throw new Error('API request failed');
  }

  // ...code

} catch (error) {
  console.error('Error:', error);
  askButton.disabled = false;
  askButton.innerText = 'Read Aloud';
  alert('An error occurred while fetching the audio.');
}
登入後複製
  • 刪除音訊:播放後從資料庫中刪除音訊 blob 以釋放空間。
// Play the audio
audio.play();
登入後複製

清理和使用者體驗

  • 覆蓋刪除:如果使用者點擊其他地方或取消選擇文本,則刪除覆蓋。
// Open IndexedDB
const db = await openDatabase();
const audioId = 'audio_' + Date.now(); // Generate a unique ID for the audio
登入後複製
  • 重新啟用按鈕:確保處理完成後重新啟用「朗讀」按鈕。
  • 使用者回饋:提供視覺提示,例如將按鈕文字變更為“正在載入...”,以通知使用者處理正在進行中。

完整程式碼:

// Save audio blob to IndexedDB
await saveAudioToIndexedDB(db, audioId, audioBlob);
登入後複製

實現 IndexedDB 函數

IndexedDB 是一個強大的客戶端儲存系統,它允許我們儲存大量數據,包括檔案和 blob。

要實現的功能

您需要建立四個主要函數來與 IndexedDB 互動:

  • openDatabase():開啟與資料庫的連線並建立物件儲存(如果不存在)。
mkdir my-first-chrome-extension
cd my-first-chrome-extension
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
  • saveAudioToIndexedDB():使用唯一 ID 儲存音訊 blob。
touch manifest.json
touch scripts.js
touch styles.css
登入後複製
登入後複製
登入後複製
登入後複製
  • getAudioFromIndexedDB():使用其 ID 檢索音訊 blob。
{
  "manifest_version": 3,
  "name": "Read Aloud",
  "version": "1.0",
  "description": "Read Aloud anything in any tab",
  "host_permissions": [
    "*://*.aimlapi.com/*"
],
  "permissions": [
      "activeTab"
  ],
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["scripts.js"],
      "css": ["styles.css"]
    }
  ],
  "icons": {
    "16": "icons/icon.png",
    "48": "icons/icon.png",
    "128": "icons/icon.png"
  }
}
登入後複製
登入後複製
登入後複製
登入後複製
  • deleteAudioFromIndexedDB():播放後刪除音訊 blob。
// Set your AIML_API_KEY key
const AIML_API_KEY = ''; // Replace with your AIML_API_KEY key

// Create the overlay
const overlay = document.createElement('div');
overlay.id = 'read-aloud-overlay';

// Create the "Read Aloud" button
const askButton = document.createElement('button');
askButton.id = 'read-aloud-button';
askButton.innerText = 'Read Aloud';

// Append the button to the overlay
overlay.appendChild(askButton);

// Variables to store selected text and range
let selectedText = '';
let selectedRange = null;
登入後複製
登入後複製

關鍵概念

  • 交易:與 I​​ndexedDB 的所有互動都發生在交易中。確保指定正確的事務模式(唯讀或讀寫)。
  • 物件儲存:與 SQL 資料庫中的表格類似,物件儲存保存資料。我們將使用名為“audios”的物件儲存。
  • 錯誤處理:始終處理資料庫操作的錯誤,以防止意外行為。

使用 styles.css 進行樣式設定

為了提供無縫的使用者體驗,您的擴充功能應該有一個乾淨直覺的介面。

設定疊加層和按鈕的樣式

定義樣式:

  • 疊加定位:絕對定位將疊加放置在所選文字附近。
document.addEventListener('mouseup', (event) => {
  console.log('mouseup event: ', event);
  //...code
}
登入後複製
登入後複製
  • 按鈕外觀:設計「朗讀」按鈕的樣式以匹配疊加層並易於點擊。
const selection = window.getSelection();
const text = selection.toString().trim();
if (text !== '') {
  const range = selection.getRangeAt(0);
  const rect = range.getBoundingClientRect();
登入後複製
登入後複製
  • 懸停效果:透過按鈕上的懸停效果增強使用者互動。
// Set the position of the overlay
overlay.style.top = `${window.scrollY + rect.top - 50}px`; // Adjust as needed
overlay.style.left = `${window.scrollX + rect.left + rect.width / 2 - 70}px`; // Adjust to center the overlay

selectedText = text;
selectedRange = range;
登入後複製
登入後複製
  • 停用狀態:直觀地指示按鈕何時被停用。
// Remove existing overlay if any
const existingOverlay = document.getElementById('read-aloud-overlay');
if (existingOverlay) {
  existingOverlay.remove();
}

// Append the overlay to the document body
document.body.appendChild(overlay);
} else {
  // Remove overlay if no text is selected
  const existingOverlay = document.getElementById('read-aloud-overlay');
  if (existingOverlay) {
    existingOverlay.remove();
  }
}
登入後複製
登入後複製

取得並設定您的 API 金鑰

要與 AI/ML API 和 Deepgram Aura 模型交互,您需要一個 API 金鑰。

取得 API 金鑰的步驟

  • 存取 AI/ML API 平台:導覽至 aimlapi.com

Building a Chrome Extension from Scratch with AI/ML API, Deepgram Aura, and IndexedDB Integration

  • 登入:點擊「取得 API 金鑰」並使用您的 Google 帳戶登入。

Building a Chrome Extension from Scratch with AI/ML API, Deepgram Aura, and IndexedDB Integration

  • 存取儀表板:登入後,您將被重新導向到儀表板。

Building a Chrome Extension from Scratch with AI/ML API, Deepgram Aura, and IndexedDB Integration

  • 建立 API 金鑰:進入「金鑰管理」選項卡,然後按一下「建立 API 金鑰」。

Building a Chrome Extension from Scratch with AI/ML API, Deepgram Aura, and IndexedDB Integration

  • 複製 API 金鑰:產生後,複製您的 API 金鑰。

Building a Chrome Extension from Scratch with AI/ML API, Deepgram Aura, and IndexedDB Integration

在您的擴充功能中設定 API 金鑰

  • 安全注意事項:如果您打算分發擴充程序,切勿將 API 金鑰硬編碼到腳本中。考慮使用環境變數或提示使用者輸入 API 金鑰。
mkdir my-first-chrome-extension
cd my-first-chrome-extension
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

現在輸入您的 API 金鑰:

touch manifest.json
touch scripts.js
touch styles.css
登入後複製
登入後複製
登入後複製
登入後複製

但它不會立即起作用。在 Chrome 擴充功能中使用 .env 需要其他額外的配置。我們將在接下來的教程中討論這個問題。

  • 用於測試:在 script.js 中,將 API 金鑰指派給處理 API 請求驗證的變數。
{
  "manifest_version": 3,
  "name": "Read Aloud",
  "version": "1.0",
  "description": "Read Aloud anything in any tab",
  "host_permissions": [
    "*://*.aimlapi.com/*"
],
  "permissions": [
      "activeTab"
  ],
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["scripts.js"],
      "css": ["styles.css"]
    }
  ],
  "icons": {
    "16": "icons/icon.png",
    "48": "icons/icon.png",
    "128": "icons/icon.png"
  }
}
登入後複製
登入後複製
登入後複製
登入後複製

運行和測試擴展

所有元件就位後,就可以將擴充功能載入到 Chrome 瀏覽器中並查看其運行情況了。

載入擴充

  • 開啟擴充頁面:在 Chrome 中,導覽至 chrome://extensions/。

Building a Chrome Extension from Scratch with AI/ML API, Deepgram Aura, and IndexedDB Integration

啟用開發者模式:切換右上角的「開發者模式」開關。

Building a Chrome Extension from Scratch with AI/ML API, Deepgram Aura, and IndexedDB Integration

  • 載入解壓縮的擴充功能:點擊「載入解壓縮的」並選擇您的 my-first-chrome-extension 資料夾。 (附:在我的例子中它是 aimlapi-tutorial-one)。

Building a Chrome Extension from Scratch with AI/ML API, Deepgram Aura, and IndexedDB Integration

  • 驗證安裝:擴充功能現在應該顯示在清單中及其名稱和圖示。

測試功能

  • 導覽至網頁:開啟包含文字內容的網頁,例如文章或部落格文章。
  • 選擇文字:突出顯示段落或句子。
  • 與疊加層互動:「正在載入...」疊加層應出現在所選文字上方。啟動文字轉語音過程時請等待幾秒鐘。
  • 聽:經過短暫的處理時間,您應該聽到人工智慧語音朗讀文字。

故障排除技巧

  • 覆蓋不出現:檢查在manifest.json中是否正確指定了content_scripts。
  • 無音訊播放:驗證您的 API 金鑰設定正確且 API 請求是否成功。
  • 控制台錯誤:使用瀏覽器的開發者工具檢查任何 JavaScript 錯誤或網頁問題。

項目概要

在本教學中,我們:

  • 設定開發環境:為 Chrome 擴充功能建立必要的專案結構和檔案。
  • 配置的manifest.json:定義必要的元資料和權限,了解每個欄位的重要性。
  • 開發了scripts.js:概述了處理文字選擇、與AI/ML API互動以及管理音訊播放的邏輯。
  • 實現了 IndexedDB 整合:學習如何使用 IndexedDB 在本機儲存和檢索音訊檔案。
  • 使用 styles.css 設計擴充功能:應用 CSS 來增強使用者介面並改善使用者體驗。
  • 取得並設定 API 金鑰:從 AI/ML API 平台取得 API 金鑰並將其安全地整合到我們的擴充功能中。
  • 載入並測試擴充功能:在 Chrome 中部署擴充功能並在即時網頁上驗證其功能。
  • 討論最佳實踐:強調擴展開發中安全性、使用者體驗和錯誤處理的重要性。

下一步

有了紮實的基礎,你就可以進一步增強你的延伸:

  • 新增自訂選項:允許使用者選擇不同的聲音或語言。
  • 改善錯誤處理:在 API 不可用時提供使用者友善的訊息和後備選項。
  • 最佳化效能:實作快取策略或最佳化 API 請求以減少延遲。
  • 發布您的擴充功能:透過在 Chrome 應用程式商店中發布您的作品來與他人分享。

Building a Chrome Extension from Scratch with AI/ML API, Deepgram Aura, and IndexedDB Integration


結論

恭喜您建立了一個整合了高級 AI 功能的 Chrome 擴充功能!該專案展示瞭如何將網路技術與強大的 API 相結合來創建引人入勝且易於訪問的用戶體驗。您現在已經具備了開發和擴充此擴充功能或建立利用 AI/ML API 的全新擴充功能的知識。

Github 上提供了完整的實現; https://github.com/TechWithAbee/Building-a-Chrome-Extension-from-Scratch-with-AI-ML-API-Deepgram-Aura-and-IndexDB-Integration


如果您有任何疑問或需要進一步協助,請隨時透過電子郵件聯絡 abdibrokhim@gmail.com。

以上是使用 AI/ML API、Deepgram Aura 和 IndexedDB 整合從頭開始建立 Chrome 擴充功能的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板