现代Web开发的黄金搭档:Vue.js与Tailwind CSS(二)

710 阅读7分钟

现代Web开发的黄金搭档:Vue.js与Tailwind CSS(二)

在现代Web开发的舞台上,Vue.js与Tailwind CSS的搭配可谓是一对黄金搭档。它们分别以响应式和实用主义为核心,共同构建了无数高效、美观的Web应用。今天,我们将深入剖析一个基于这两大法宝的简单应用,探讨其背后的技术细节和设计哲学。

1. Tailwind CSS:简约而不简单

根据上一篇文章介绍,这里直接在 tailwind.css 文件中添加以下配置

@tailwind base;
@tailwind components;
@tailwind utilities;

2. gpt.js:异步API调用

gpt.js 文件中的 chat 函数是一个典型的异步函数,它使用 fetch API向OpenAI的GPT-3.5 API发送请求。这里展示了如何处理HTTP请求,并在出现错误时进行捕获和日志记录。

export async function chat(messageList, apiKey) {
  try {
    const result = await fetch('https://api.302.ai/v1/chat/completions', {
            method:'post',
            headers: {
                'Content-Type': "application/json",
                // 授权
                'Authorization': `Bearer ${apiKey}`
            },
            body:JSON.stringify({
                model: 'gpt-3.5-turbo',
                messages:messageList
            })
        })
        const data = await result.json()
        return data;
  } catch (error) {
    console.log(error);
    throw(error);
  }
}

这段代码定义了一个异步函数chat,用于向特定的API发送POST请求,该API用于处理与GPT-3.5 Turbo模型的交互。以下是该代码的关键知识点分析:

  1. 异步函数 (async function): 函数前的async关键字表明这是一个异步函数,它可以返回一个Promise,并允许使用await关键字来等待异步操作的结果。

  2. Fetch APIfetch函数是现代浏览器中用于发起网络请求的标准API。在这里,它被用来向https://api.302.ai/v1/chat/completions这个URL发送一个POST请求。

  3. 请求选项 (options object): fetch函数的第二个参数是一个对象,用于配置请求的详细信息:

    • method: 'post':指定请求方法为POST。

    • headers:一个对象,描述了请求头。这里是Content-TypeAuthorization头:

      • 'Content-Type': "application/json":表明请求体是一个JSON格式的数据。
      • 'Authorization': Bearer ${apiKey}':包含API密钥,用于授权请求。
    • body:请求体,这里使用JSON.stringify将JavaScript对象转换为JSON字符串。

  4. 结果处理

    • await result.json():从响应中读取JSON数据。resultfetch返回的Promise的结果,它包含服务器的响应。json()方法返回另一个Promise,解析响应体为JSON格式。
  5. Try-Catch语句

    • 使用try-catch结构来捕获和处理fetch操作中可能出现的错误。这是处理异步操作错误的一种标准方式。
  6. 错误日志和抛出

    • console.log(error);:将错误信息输出到控制台,有助于调试。
    • throw(error);:重新抛出捕获的错误,这样调用者可以进一步处理或捕获这个错误。

总结: 这段代码展示了如何使用Fetch API向API服务发送POST请求,处理响应和错误的基本流程。它结合了异步编程的最佳实践,如使用async/awaittry-catch,以及正确的网络请求格式和错误处理机制。在实际应用中,这种模式常用于与后端服务或外部API进行交互。

3. index.js:路由配置

index.js 文件是Vue应用的入口,这里我们使用 vue-router 创建了路由实例,并导出这个路由配置。这使得Vue应用能够处理客户端的导航请求,将用户引导到对应的视图组件。

import { createRouter, createWebHistory } from 'vue-router'
// ...Router configuration
export default router

4. Home.vue:交互的界面

Home.vue 组件是应用的交互中心,它使用Vue的响应式系统和生命周期钩子来实现动态数据绑定和处理用户输入。此外,它还利用了Tailwind CSS提供的类式样式,实现了丰富的视觉效果。

<template>
   <!-- h-screen:100vh -->
   <!-- http://localhost:5173/ -->
   <div class="flex flex-col h-screen">
       <div class="flex flex-nowrap fixed w-full items-baseline 
                   top-0 px-6 py-4 bg-gray-100">
           <div class="text-2xl font-bold">ChatGPT</div>
           <div class="ml-4 text-sm text-gray-500">
             人工智能对话
           </div>
           <div class="ml-auto px-3 py-2 text-sm cursor-pointer hover:bg-white rounded-md">
               设置
           </div>
           <div class="flex-1"></div>
           <div class="sticky bottom-0 w-full p-6 pb-8 bg-gray-100">
               <div v-if="isConfig" class='mb-2 text-sm text-gray-500'>
                   请输入 API key:
               </div>
               <div class="flex">
                   <input class="input" :type="isConfig ? 'password' : 'text'"
                       :placehold="isConfig ? 'sk-xxxxxx' : '请输入'" v-model="messageContent"
                       @keydown.enter="sendOrSave()" />
                   <button @click="sendOrSave()" class="btn">{{ isConfig ? '保存' : '发送' }}</button>
               </div>
           </div>
       </div>
   </div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { chat } from '../libs/gpt'
let messageContent = ref("")
let isConfig = ref(true)  // 是否显示设置
const clickConfig = () => {
   // 当前不是配置状态
   if (!isConfig.value) {
       messageContent.value = getAPIKey()
   } else {
       clearMessageContent();
   }
   switchConfigStatus();
}

const getAPIKey = () => {
   return localStorage.getItem('apiKey');
}

const clearMessageContent = () => {
   messageContent.value = "";
}

const sendOrSave = () => {
   if (!messageContent.value.length) return;
   if (isConfig.value) {
       if (savaAPIKey(messageContent.value.trim()))
           switchConfigStatus();
       messageContent.value = "";
   }

   else {
       // 发送openai
       sendChatMessage()
   }
}

const sendChatMessage = async () => {
   const apiKey = getAPIKey();
   const messageList = [{
       role: 'system',
       content: '你是一个AI智能助手,你帮助用户解决各种问题'
   },
   {
       role: 'user',
       content: messageContent.value
   }]
   const data = await chat(messageList, apiKey)
   console.log(data);
}

const savaAPIKey = (apiKey) => {
   localStorage.setItem('apiKey', apiKey)
   return true
}

const switchConfigStatus = () => {
   isConfig.value = !isConfig.value
}
</script>
<style></style>

这段代码展示了Vue 3中使用组合式API和<script setup>语法糖编写组件的方法,包括动态属性绑定、条件渲染、响应式数据和事件处理。同时,使用了大量的Tailwind CSS的实用类快速构建布局和样式,提高了开发效率。例如:

flex-col: 子元素按列方向排列

h-screen: 让容器高度占据整个屏幕的高度,适合构建全屏应用

w-full: 使其宽度占满屏幕

items-baseline: 对齐子元素的基线

px-6 py-4: 设置内外边距

另外,代码的 20-22 行中,:type:placeholderv-model使用动态绑定,根据isConfig的值改变输入框的类型和提示文字。v-model实现了双向数据绑定,@keydown.enter监听回车键事件。

(1)clickConfig 

  • 功能:处理点击配置按钮的行为。如果应用当前不在配置状态,则从本地存储中获取API Key并将其设置为messageContent的值。如果在配置状态,则清空messageContent,并切换配置状态。
  • 调用时机:当用户点击配置按钮时会被调用。

(2)getAPIKey 

  • 功能:从本地存储中获取API Key。
  • 调用时机:当需要从本地存储中读取API Key时调用。

(3)clearMessageContent 

  • 功能:清空messageContent的值。
  • 调用时机:在clickConfig函数中,当从配置状态切换回来时调用。

(4)sendOrSave 

  • 功能:根据当前状态(配置或聊天)处理用户输入。如果是配置状态,保存API Key到本地存储,切换状态,并清空输入。如果不是配置状态,则发送消息到AI服务。
  • 调用时机:当用户在输入框中按下Enter键或点击发送按钮时调用。

(5)sendChatMessage 

  • 功能:异步发送消息到AI服务。首先获取API Key,然后准备消息列表(包括系统预设和用户输入),调用chat函数发送请求,最后打印返回的数据。
  • 调用时机:在sendOrSave函数中,当用户在聊天状态下发送消息时调用。

(6)savaAPIKey 

  • 功能:保存API Key到本地存储。
  • 调用时机:在sendOrSave函数中,当用户在配置状态下输入API Key并按下Enter键时调用。

(7)switchConfigStatus 

  • 功能:切换isConfig的状态,即切换应用是否处于配置状态。
  • 调用时机:在clickConfigsendOrSave函数中调用,用于切换配置状态。

总结: 这段代码实现了用户与AI聊天应用交互的基本逻辑,包括输入处理、状态管理、与后端服务的通信,以及配置管理。通过Vue的响应式系统,messageContentisConfig的状态变化能够及时反映到UI上,提供流畅的用户体验。

5. App.vue:路由的出口

App.vue 组件作为Vue应用的根组件,它使用 router-view 插槽来渲染当前路由对应的组件。这使得路由系统能够无缝地融入Vue应用的组件结构中。

<script setup>
//GPT 代表整个模块的默认输入
import { chat } from '.libs/gpt.js'
const main = async () => {
};
main();
</script>
<template>
  <div class="flex">
      <router-view/>
  </div>
</template>
<style scoped>
<style>

<router-view/>是Vue Router提供的组件,用于渲染当前激活的路由视图。这意味着在这个<div>容器中,将会显示根据当前URL所匹配到的组件。

6. index.html

这段代码是一个基本的HTML文档结构,用于承载一个使用Vite和Vue.js构建的前端应用。

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Vue</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

6. main.js:应用的启动

main.js 是Vue应用的启动文件,它初始化一个Vue应用实例,配置其根组件和路由。导入并应用Tailwind CSS样式。将应用挂载到DOM中,启动应用的生命周期。

import { createApp } from 'vue'
import './assets/tailwind.css'
import App from './App.vue'
import router from './routes/index'

const app = createApp(App)
app
    .use(router)
    .mount('#app')

(1)createApp是Vue 3中用于创建应用实例的主要函数,它替代了Vue 2中的new Vue({})构造函数。createApp函数返回一个应用实例,可以配置和使用插件。

(2) 最后三行代码完成了应用的配置和挂载:

  • app.use(router):将Vue Router实例注册为应用的插件。一旦注册,router就成为了全局可用的对象,可以被应用中的任何组件访问和使用。
  • app.mount('#app'):将应用实例挂载到index.html文件中 <div id="app"> 的DOM元素上。挂载后,Vue应用将接管该元素,并根据 App 组件的模板渲染内容。

7. tailwind.config.js:自定义Tailwind

tailwind.config.js 文件配置了Tailwind CSS的入口文件和主题扩展,使其能够适用于我们的项目。(详细解释见上篇文章)

export default {
  content: [
    "./index.html",
    ".src/**/*.{vue,js,ts,jsx,tsx}"
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

8. 结果展示

image.png

在输入框中输入正确的API key,点击保存,如果API key 值正确,即可将该值保存在 local storage 中。后续还会根据这两篇文章添加功能,使其达到 OpenAI 的聊天效果。

综上所述,这个简单的Vue应用展现了现代Web开发的几个重要方面:组件化架构、异步编程、响应式设计等。而Tailwind CSS的加入,更让这个应用在实用性和美观性之间找到了完美的平衡。希望通过对这个应用的分析,你能对Vue和Tailwind CSS有更深入的理解和灵感。