<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    一個實(shí)戰(zhàn)Demo,讓你理解"前端+Node后端+AI"的全棧項(xiàng)目如何實(shí)現(xiàn)

    共 15441字,需瀏覽 31分鐘

     ·

    2024-12-03 22:39

    前言

    在數(shù)字時代的浪潮下,前端、后端和人工智能(AI)的融合發(fā)展已經(jīng)成為技術(shù)創(chuàng)新和應(yīng)用的關(guān)鍵驅(qū)動力。它們各自扮演著不同的角色,卻又緊密相連,共同構(gòu)建起了現(xiàn)代軟件系統(tǒng)的核心架構(gòu)。前端負(fù)責(zé)與用戶進(jìn)行交互,提供直觀、友好的界面體驗(yàn);后端則負(fù)責(zé)處理業(yè)務(wù)邏輯、數(shù)據(jù)存儲和與前端的數(shù)據(jù)交換;而AI則通過智能算法和模型,為系統(tǒng)賦予了學(xué)習(xí)和決策的能力。

    為此,小編將通過一個實(shí)戰(zhàn)demo,向大家展示如何實(shí)現(xiàn)一個融合了前端、后端和AI技術(shù)的全棧項(xiàng)目。

    讓我們一同踏上這場全棧之旅,探索從前端走向后端和AI的無限可能!

    項(xiàng)目文件結(jié)構(gòu)

    首先我們來了解一下這個項(xiàng)目的文件結(jié)構(gòu),因?yàn)橹挥幸粋€頁面,所以前端代碼沒有細(xì)分css,html,js文件!當(dāng)然,不要小看這一個頁面,觸類旁通,學(xué)會這一個頁面,你就可以走向AI賦能的全棧工程師了!

    image.png

    前端界面

    界面長這樣,比較簡陋,小伙伴不要介意,咱們看看過程!

    image.png

    html代碼

    <div class="container">
        <div class="information">
          <h1>AI能力驅(qū)動的userData</h1>
          <table class="table table-striped" id="user_table">
            <thead>
              <tr>
                <th>ID</th>
                <th>姓名</th>
                <th>家鄉(xiāng)</th>
              </tr>
            </thead>
            <tbody>
              <!-- <tr>
                <td>1</td>
                <td>代童</td>
                <td>贛州</td>
              </tr>
              <tr>
                <td>2</td>
                <td>羅昭發(fā)</td>
                <td>贛州</td>
              </tr> -->

            </tbody>
          </table>

          <div class="send">
            <form name="aiForm">
              <div class="form-group">
                <label for="questionInput">向AI助理提問</label>
                <input type="text" name="question" class="form-control" id="questionInput" placeholder="請輸入你想問的users相關(guān)問題">
              </div>
              <button type="submit" class="btn btn-default">提交</button>
            </form>
          </div>
          
          <div class="answer" id="message"></div>
        </div>
      </div>

    這是一個非常簡單的html,但是小小的html也有大大的考點(diǎn)(細(xì)節(jié)):

    label(for) 和 input(id) 同名

      <label for="questionInput">向AI助理提問</label>
      <input type="text" id="questionInput" name="question" class="form-control" placeholder="請輸入你想問的users相關(guān)問題">

    在HTML表單中,使用 <label> 元素與對應(yīng)的 <input> 元素關(guān)聯(lián)起來有幾個重要的功能和目的,特別是為了增強(qiáng)用戶體驗(yàn)感,以便更好地服務(wù)各種用戶群體,包括有視力障礙的用戶。

    1. 可點(diǎn)擊的標(biāo)簽:

      label 元素的 for 屬性值是 "questionInput",這與 input 元素的 id 屬性值相同。這樣,當(dāng)用戶點(diǎn)擊“向AI助理提問”標(biāo)簽時,光標(biāo)會自動跳轉(zhuǎn)到輸入框中。這對于那些使用鼠標(biāo)或觸摸屏的用戶非常方便。

    2. 輔助技術(shù)支持:

      這種關(guān)聯(lián)對于使用屏幕閱讀器的用戶尤其重要。屏幕閱讀器會讀取 <label> 元素的內(nèi)容,并告知用戶該輸入框的用途。例如,盲人用戶在使用屏幕閱讀器時會聽到“向AI助理提問”,并知道他們應(yīng)該在此輸入框中輸入問題。

    3. 提升表單的可用性:

      通過這種關(guān)聯(lián),用戶可以更加直觀地理解每個輸入框的用途,減少混淆和錯誤輸入。例如,當(dāng)用戶看到“向AI助理提問”時,他們會清楚地知道應(yīng)該在下面的輸入框中輸入問題。

    HTML5 中的 placeholder 屬性

    1. 提示性文本:

      placeholder 屬性提供了一個灰色的提示文本(通常為淺灰色),在輸入框?yàn)榭諘r顯示。這個文本可以幫助用戶理解該輸入框的預(yù)期輸入內(nèi)容。例如,"請輸入你想問的相關(guān)問題" 或 "Email 地址" 等。

    2. 自動消失:

      當(dāng)用戶開始在輸入框中輸入內(nèi)容時,placeholder 提示文本會自動消失,讓用戶專注于輸入自己的內(nèi)容。這使得用戶體驗(yàn)更加流暢,不需要手動清除提示文本。

    當(dāng)前端小伙伴完成了前端頁面的搭建和交互邏輯后,通常會開始等待后端接口來獲取動態(tài)數(shù)據(jù),并將這些數(shù)據(jù)寫入數(shù)據(jù)庫中的user_table。然而,為了不讓開發(fā)進(jìn)程受到等待的限制,我們就可以自己編寫一個后端服務(wù)。通過自己編寫后端服務(wù),你可以更好地掌控?cái)?shù)據(jù)的處理過程和數(shù)據(jù)結(jié)構(gòu),更靈活地滿足前端的需求,并且加深對整個項(xiàng)目的理解和把握。

    后端數(shù)據(jù)

    前置知識

    json-server

    對于前端開發(fā)人員來說,我們可以使用json-server,它 是一個非常有用的開發(fā)工具,可以讓你使用一個 JSON 文件快速創(chuàng)建一個完整的 RESTful API 服務(wù)器,幫助你在沒有后端服務(wù)器的情況下進(jìn)行前端開發(fā)和測試。

    JSON文件

    JSON是一種用于數(shù)據(jù)交換的輕量級格式。它使用鍵值對來表示對象,并使用有序列表來表示數(shù)組。鍵是字符串,必須用雙引號包圍,值可以是字符串、數(shù)字、布爾值、數(shù)組、對象或 null。

    JSON 格式易于人類閱讀和編寫,也易于機(jī)器解析和生成,廣泛應(yīng)用于Web開發(fā)中的數(shù)據(jù)傳輸、配置文件和數(shù)據(jù)存儲。需要注意的是,JSON 中的鍵和值必須遵循嚴(yán)格的語法規(guī)則,如鍵名必須是字符串,不能包含多余的逗號等。

    backend創(chuàng)建過程

    Step 1: 初始化項(xiàng)目

    首先,在終端中導(dǎo)航到backend文件打開終端輸入命令(初始化為后端項(xiàng)目)。這將生成一個 package.json 文件,默認(rèn)配置會使用 -y 標(biāo)志。

    npm init -y

    這個命令會創(chuàng)建一個默認(rèn)的 package.json 文件,內(nèi)容如下:

    {
      "name""ai_server",
      "version""1.0.0",
      "description""",
      "main""index.js",
      "scripts": {
        "test""echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author""",
      "license""ISC",
      "dependencies": {
        "dotenv""^16.4.5",
        "openai""^4.47.1"
      }
    }

    Step 2: 安裝 json-server

    backend后端中安裝 json-server庫。

    npm i json-server

    Step 3: 創(chuàng)建 JSON 數(shù)據(jù)文件

    backend目錄下創(chuàng)建一個名為 users.json 的文件。這個文件里的數(shù)據(jù)將作為我們的模擬數(shù)據(jù)庫。內(nèi)容如下:

    {
      "users": [
        {
          "id"1,
          "name""蒲熠星",
          "hometown""綿陽"
        },
        {
          "id"2,
          "name""郭文韜",
          "hometown""青海"
        },
        {
          "id"3,
          "name""積米者",
          "hometown""潮汕"
        },
        {
          "id"4,
          "name""小旺車",
          "hometown""豐城"
        }
      ]
    }

    Step 4: 修改配置文件

    package.json 文件中添加一個新的腳本命令,在 scripts 部分添加以下內(nèi)容:

    {
      "scripts": {
        "dev""json-server users.json"
      }
    }

    該配置的主要目的是可以使用 json-server 啟動一個本地服務(wù)器,并利用 users.json 文件作為數(shù)據(jù)源。

    Step 5: 運(yùn)行開發(fā)服務(wù)器

    在終端中運(yùn)行以下命令來啟動 json-server

    npm run dev

    這將會啟動一個在 http://localhost:3000 上運(yùn)行的開發(fā)服務(wù)器,并使用 users.json 作為數(shù)據(jù)源。

    啟動成功后終端顯示如下:

    image.png

    在利用 json-server搭建了后端之后,我們可以通過發(fā)送 Fetch 請求到 http://localhost:3000/users[1] 接口來獲取用戶數(shù)據(jù),然后將這些數(shù)據(jù)渲染到用戶表格中。接下來,我們需要將用戶輸入的問題傳遞給 AI 模型進(jìn)行處理,實(shí)現(xiàn)向 AI 提問的功能。

    AI賦能

    前置知識

    OpenAI調(diào)用AI模型

    具體如何帶調(diào)用OpenAI 提供的 API 來調(diào)用 AI 模型可以先看看這篇文章

    如何將OpenAI集成到項(xiàng)目中 - 掘金 (juejin.cn)[2]

    HTTP模塊

    http 模塊是Node.js 一個內(nèi)置模塊,用于創(chuàng)建 HTTP 服務(wù)器和客戶端。它提供了一組 API,使得在 Node.js 環(huán)境中能夠方便地處理 HTTP 請求和響應(yīng)。

    以下是關(guān)于項(xiàng)目中使用 http 模塊的介紹:

    創(chuàng)建 HTTP 服務(wù)器

    使用 http.createServer() 方法可以創(chuàng)建一個 HTTP 服務(wù)器。這個方法接受一個回調(diào)函數(shù)作為參數(shù),回調(diào)函數(shù)會在每次收到 HTTP 請求時被調(diào)用?;卣{(diào)函數(shù)的參數(shù)包括代表請求的 request 對象和代表響應(yīng)的 response 對象。

    const http = require('http');

    const server = http.createServer((req, res) => {
      // 處理請求邏輯
    });

    server.listen(8888function ({
      console.log('server is running')
    })

    處理 HTTP 請求和響應(yīng)

    在 HTTP 請求的回調(diào)函數(shù)中,可以通過 req 對象獲取請求的信息,通過 res 對象發(fā)送響應(yīng)。

    • req.url: 請求的 URL。
    • req.method: 請求的方法,如 GET、POST 等。
    • req.headers: 請求頭部信息。
    • req.on('data', callback): 用于處理 POST 請求的請求體數(shù)據(jù)。

    URL模塊

    url 模塊是Node.js 一個內(nèi)置模塊,用于處理 URL 字符串的解析、格式化和操作。它提供了一組 API,使得在 Node.js 環(huán)境中能夠方便地解析和操作 URL 字符串。

    以下是關(guān)于項(xiàng)目中使用 url介紹:

    URL 查詢參數(shù)解析

    URL 查詢參數(shù)可以通過兩種方式進(jìn)行解析:使用 url.parse() 方法解析后的 URL 對象的 query 屬性,或者使用 url.parse() 方法的第二個參數(shù)設(shè)置為 true,將查詢參數(shù)解析成一個對象。

    // 使用 url 模塊的 parse 方法解析 HTTP 請求的 URL,將其轉(zhuǎn)換為一個 URL 對象
    const urlParams = url.parse(req.url, true);

    // 解析后的 URL 對象中的 query 屬性包含了 URL 中的查詢參數(shù),其中 true 參數(shù)表示將查詢字符串解析成對象
    // 對象解構(gòu)語法從查詢參數(shù)對象中提取出 question 和 users 參數(shù)的值
    const { question, users } = urlParams.query;

    Dotenv模塊

    dotenv 是一個 Node.js 的第三方模塊,用于加載環(huán)境變量。它的作用是從一個名為 .env 的文件中加載環(huán)境變量,并將這些變量添加到 Node.js 的 process.env 對象中,使得在應(yīng)用程序中可以輕松地訪問這些環(huán)境變量。

    以下是關(guān)于項(xiàng)目中使用 dotenv的介紹:

    // 導(dǎo)入 OpenAI 模塊
    const OpenAI = require('openai');

    // 導(dǎo)入 dotenv 模塊,并加載環(huán)境變量
    require('dotenv').config();

    // 創(chuàng)建 OpenAI 客戶端實(shí)例
    const client = new OpenAI({
      // 從環(huán)境變量中獲取 OpenAI API 密鑰
      apiKey: process.env.OPENAI_API_KEY,
      // 設(shè)置 OpenAI API 的基礎(chǔ) URL
      baseURL'https://api.chatanywhere.tech/v1'
    });

    ai_server創(chuàng)建過程

    Step 1: 初始化項(xiàng)目

    首先,在終端中導(dǎo)航到ai_server文件打開終端輸入命令(初始化為后端項(xiàng)目)。實(shí)現(xiàn)AI提問功能本質(zhì)上也是后端功能

    npm init -y

    Step 2: 創(chuàng)建 main.js 入口文件

    ai_server目錄下創(chuàng)建一個名為 main.js 的文件。

    內(nèi)容如下:

    //引入其他模塊
    const http = require('http');
    const url = require('url');
    const OpenAI = require('openai');
    require('dotenv').config();

    // 創(chuàng)建 OpenAI 客戶端實(shí)例
    const client = new OpenAI({
      apiKey: process.env.OPENAI_API_KEY,
      baseURL'https://api.chatanywhere.tech/v1'
    })

    const server = http.createServer(async function (req, res{
      // 解析請求的 URL,獲取查詢參數(shù)
      const urlParams = url.parse(req.url, true);
      const { question, users } = urlParams.query;

      // 構(gòu)建對話 prompt,模擬用戶發(fā)送問題
      const prompt = `${users}請根據(jù)以上JSON數(shù)據(jù),回答${question}這個問題,如果回答不了就回答不清楚!`;

      // 使用 OpenAI 客戶端發(fā)送對話請求,獲取回復(fù)
      const response = await client.chat.completions.create({
        model'gpt-3.5-turbo',
        messages: [{ role"user"content: prompt }],
        temperature0// 控制輸出的隨機(jī)性,0表示更確定的輸出
      });

      // 從回復(fù)中提取內(nèi)容
      const result = response.choices[0].message.content || '';

      // 構(gòu)造返回給客戶端的信息
      let info = {
        message: result
      };

      // 設(shè)置 CORS 頭部信息,允許跨域請求
      res.setHeader('Access-Control-Allow-Origin''*');
      res.setHeader('Access-Control-Allow-Methods''GET, POST, OPTIONS');
      res.setHeader('Access-Control-Allow-Headers''Content-Type, Authorization');

      // 設(shè)置 HTTP 響應(yīng)頭部信息和狀態(tài)碼,并發(fā)送 JSON 格式的響應(yīng)
      res.statusCode = 200;
      res.setHeader('Content-Type''text/json');
      res.end(JSON.stringify(info));
    });

    // 啟動 HTTP 服務(wù)器,監(jiān)聽指定端口
    server.listen(8888function ({
      console.log('server is running');
    });

    這段代碼實(shí)現(xiàn)了一個基于 Node.js 的 HTTP 服務(wù)器,通過與 OpenAI 的對話生成服務(wù)交互,提供了向 AI 助理提問的功能。當(dāng)客戶端發(fā)送 HTTP 請求到 http://localhost:8888 端口時,可以通過在請求中傳入問題和用戶數(shù)據(jù)參數(shù),服務(wù)器會解析這些參數(shù)并將其作為對話的內(nèi)容發(fā)送給 OpenAI。OpenAI 使用 GPT-3.5 模型生成對應(yīng)的回答,并將回答返回給服務(wù)器,最終服務(wù)器將回答作為 JSON 格式的數(shù)據(jù)發(fā)送回客戶端。

    這樣,通過訪問服務(wù)器提供的端點(diǎn),用戶可以與 AI 助理進(jìn)行實(shí)時的對話交互,向其提出問題并獲取回答,從而在項(xiàng)目中實(shí)現(xiàn)了一個簡單的 AI 對話功能。

    前端JS代碼

    通過上面實(shí)現(xiàn)了后端和AI服務(wù)功能后,我們獲取到了后端數(shù)據(jù)和AI功能請求接口,當(dāng)前端通過向后端數(shù)據(jù)接口發(fā)送請求獲取數(shù)據(jù),再通過向AI功能接口發(fā)送請求實(shí)現(xiàn)智能功能,就實(shí)現(xiàn)了前端+后端+AI的全棧項(xiàng)目流程。

    接下我們再來寫一下前端JS邏輯!

    向后端發(fā)送請求獲取用戶數(shù)據(jù)

    // 選擇表格中 tbody 元素,用于后續(xù)操作
    const oBody = document.querySelector("#user_table tbody");

    // 初始化一個空數(shù)組,用于存儲從服務(wù)器獲取的用戶數(shù)據(jù)
    let usersData = [];

    // 使用 fetch API 從后端獲取用戶數(shù)據(jù)
    fetch('http://localhost:3000/users')
      // 解析響應(yīng)的數(shù)據(jù)為 JSON 格式
      .then(data => data.json())
      // 處理解析后的用戶數(shù)據(jù)
      .then(users => {
        usersData = users;
        // 將用戶數(shù)據(jù)生成 HTML 表格行,并插入到 tbody 中
        oBody.innerHTML = users.map(user => `
          <tr>
            <td>${user.id}</td>
            <td>${user.name}</td>
            <td>${user.hometown}</td>
          </tr>
        `
    ).join("");
      });

    細(xì)節(jié): 面試官問你可不可以不使用join("")

    如果不使用 join(""),在 oBody.innerHTML 中會發(fā)生隱式轉(zhuǎn)換。隱式轉(zhuǎn)換會將數(shù)組中的每個元素和字符串相加,然后將結(jié)果拼接成一個字符串,形式是每個數(shù)組元素之間以逗號分隔。因此,如果不使用 join(""),則 oBody.innerHTML 的值將是一個以逗號分隔的字符串,每個元素都是一個包含 <tr> 元素的字符串。

    <tr>
      <td>1</td>
      <td>蒲熠星</td>
      <td>綿陽</td>
    </tr>,
    <tr>
      <td>2</td>
      <td>郭文韜</td>
      <td>青海</td>
    </tr>,
    <tr>
      <td>3</td>
      <td>積米者</td>
      <td>潮汕</td>
    </tr>

    這將會導(dǎo)致渲染錯誤,因?yàn)槎禾柌皇?HTML 表格結(jié)構(gòu)的一部分,而且會導(dǎo)致 DOM 解析器解析錯誤。

    向AI接口發(fā)送請求獲取AI回復(fù)功能

    const oMessage = document.querySelector("#message");
    const oForm = document.forms['aiForm'];

    // 監(jiān)聽表單提交事件
    oForm.addEventListener('submit'function (event{
      // 阻止表單默認(rèn)提交行為
      event.preventDefault();

      // 獲取表單中 name 屬性為 "question" 的輸入框的值,并去除首尾空格
      const question = this["question"].value.trim();

      // 如果問題不為空
      if (question) {
        // 發(fā)起 HTTP 請求,向AI服務(wù)發(fā)送問題和用戶數(shù)據(jù)
        fetch(`http://localhost:8888/users?question=${question}&users=${JSON.stringify(usersData)}`)
          .then(data => data.json()) // 將響應(yīng)數(shù)據(jù)解析為 JSON 格式
          .then(res => {
            console.log(res); // 打印響應(yīng)數(shù)據(jù)到控制臺
            // 將響應(yīng)中的消息顯示在頁面中 id 為 "message" 的元素中
            document.querySelector("#message").innerHTML = res.message;
          })
      }
    })

    細(xì)節(jié): 為什么要用this

     const question = this["question"].value.trim();

    在事件處理函數(shù)中,this 關(guān)鍵字指向觸發(fā)事件的元素。在這個特定的例子中,事件處理函數(shù)是通過 addEventListener 方法添加到表單元素上的,因此事件處理函數(shù)內(nèi)部的 this 指向的就是這個表單元素。

    通過 this["question"],我們可以方便地獲取到表單中名為 "question" 的輸入框元素,并獲取其值。這種寫法在代碼可讀性和維護(hù)性上比直接使用 document.getElementById 更好,因?yàn)樗@式地表明了我們想要操作當(dāng)前表單元素的某個屬性或者子元素。

    最后

    至此,AI全棧實(shí)現(xiàn)就此完成,講解表達(dá)可能不夠清晰,有疑問的小伙伴歡迎評論區(qū)留言!

    這是一個簡單的demo,卻是我們走向AI全棧的一大步,繼續(xù)學(xué)習(xí),與君共勉

    源碼倉庫地址:高小莊/fullstack-aiusers (gitee.com)[3]

    參考資料
    [1]

    http://localhost:3000/users: http://localhost:3000/users

    [2]

    https://juejin.cn/post/7368820207593570313: https://juejin.cn/post/7368820207593570313

    [3]

    https://gitee.com/gaoxiaozhuang11111/fullstack-aiusers: https://gitee.com/gaoxiaozhuang11111/fullstack-aiusers


    作者:積米者
    https://juejin.cn/post/7372523264067764233

    瀏覽 70
    點(diǎn)贊
    評論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)
    評論
    圖片
    表情
    推薦
    點(diǎn)贊
    評論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    欧美精品一级二级三级 | 欧美三级片在线播放 | 骚逼免费看 | 精品人妻无码一区二区三级精东 | 欧美成人性爱大全 |