技术文章

JVM炼丹师
2026-06-14 06:24
阅读 2518

刚入职两个月,我靠Vue.js熬过了试用期

上周五晚上十一点半,我盯着VSCode里闪烁的光标,默默数了一下左边栏装的插件:Volar、ESLint、Prettier、GitLens、Vue VSCode Snippets……好家伙,快三十个了。说实话,刚入职这家新公司两个月,我正处于一种极其迷茫的状态。本来我是搞云原生和K8s的,天天跟YAML文件、Pod调度、Service Mesh打交道,结果入职没两周,前端组的哥们因为“世界那么大”跑路了。领导一拍脑袋,说我看你平时自己捣鼓过前端,这个内部工具重构你先顶一下。

当时我真的想砸键盘,但摸了摸干瘪的钱包,再想想最近大环境不好,跳槽还得看简历丰富度,咬咬牙接了。毕竟,多掌握一门前端框架,以后跳槽写“全栈工程师”也能多要两千块钱不是?

接手这个项目后,我发现是个Vue2的老古董,满屏的this指向问题让我这个习惯了K8s声明式配置的人痛不欲生。于是我跟领导申请,直接上Vue3 + Vite。更坑的是产品经理,上周五开需求评审会,他神秘兮兮地说:“现在AI这么火,咱们内部运维工具也得跟上时代。我要加个智能问答,还要能语音播报出来,要那种声音特别逼真的!”

我心想你当我是哆啦A梦呢?但没办法,打工人嘛。后端大佬倒是给力,花了一周时间搭了个基于 RAG (检索增强生成) 的架构,把公司的运维文档全喂进去了。语音这块,我找了个叫 ElevenLabs 的API,这玩意儿生成的语音确实有点东西,连呼吸声和停顿都能模拟出来。我的任务,就是把这些后端和第三方的能力,用Vue3在前端揉捏起来。

既然是写入门指南,我就结合这个实战需求,循序渐进地聊聊Vue3的核心玩法。

告别Options API,拥抱Setup语法糖

习惯了K8s里那种“声明式”的写法,刚接触Vue2的Options API(data, methods, mounted那一坨)时,我感觉代码逻辑被切得稀碎。但Vue3的<script setup>语法糖简直是人类之光,它让代码的组织方式更符合人类直觉。

来看看我是怎么把ElevenLabs和RAG问答结果结合起来的。我写了一个核心的问答组件:

<template>
  <div class="ai-chat-container">
    <!-- 聊天历史区域,加了ref用于操作DOM -->
    <div class="chat-history" ref="chatListRef">
      <div v-for="msg in messages" :key="msg.id" class="message-item">
        <p>{{ msg.content }}</p>
        <button @click="playVoice(msg.content)" :disabled="isPlaying">
          {{ isPlaying ? '播报中...' : '语音播报' }}
        </button>
      </div>
    </div>
    
    <!-- 输入区域 -->
    <div class="input-area">
      <input v-model="userInput" @keyup.enter="askQuestion" placeholder="输入运维问题..." />
      <button @click="askQuestion" :disabled="isLoading">发送</button>
    </div>
  </div>
</template>

<script setup>
import { ref, nextTick, onMounted } from 'vue'

// 1. 响应式状态:用ref包装基本类型
const userInput = ref('')
const messages = ref([])
const isLoading = ref(false)
const isPlaying = ref(false)
const chatListRef = ref(null) // 获取DOM元素

// 2. 方法定义:直接写函数,不需要methods
const askQuestion = async () => {
  if (!userInput.value.trim()) return
  
  const question = userInput.value
  messages.value.push({ id: Date.now(), content: `Q: ${question}` })
  userInput.value = ''
  isLoading.value = true
  
  try {
    // 调用后端RAG接口获取答案
    const res = await fetch('/api/rag/query', {
      method: 'POST',
      body: JSON.stringify({ query: question })
    })
    const data = await res.json()
    messages.value.push({ id: Date.now() + 1, content: `A: ${data.answer}` })
    
    // 等待DOM更新后滚动到底部
    await nextTick()
    if (chatListRef.value) {
      chatListRef.value.scrollTop = chatListRef.value.scrollHeight
    }
  } catch (error) {
    console.error('RAG请求失败:', error)
  } finally {
    isLoading.value = false
  }
}

// 3. 调用ElevenLabs API
const playVoice = async (text) => {
  isPlaying.value = true
  try {
    // 这里省略了具体的ElevenLabs SDK调用细节
    // 实际项目中建议把音频流处理封装成独立的composable
    const audioUrl = await getElevenLabsAudio(text) 
    const audio = new Audio(audioUrl)
    audio.play()
    audio.onended = () => { isPlaying.value = false }
  } catch (e) {
    console.error('语音合成失败:', e)
    isPlaying.value = false
  }
}

onMounted(() => {
  console.log('组件挂载完成,可以开始搞事情了')
})
</script>

踩坑与调试:那些年我交过的学费

写这段代码的时候,我踩了几个典型的坑,分享给刚入门的兄弟。

第一个是响应式丢失问题。一开始我把messages定义成了reactive([]),然后在获取数据后直接messages = res.data,结果页面死活不更新。后来查了文档才发现,reactive不能直接重新赋值整个对象或数组,必须用ref,或者用Object.assign。对于数组和基础类型,无脑用ref就对了,记得在<script>里取值要加.value,但在<template>里Vue会自动解包,不用加。

第二个是DOM更新的时机。在askQuestion里,我往messages里push了新消息后,想立刻让滚动条滚到底部。如果直接写chatListRef.value.scrollTop = ...,是拿不到最新高度的,因为Vue的DOM更新是异步的。这时候必须请出nextTick,等Vue把虚拟DOM渲染到真实DOM后,再去操作滚动条。

说到调试,强烈建议装个 Vue Devtools 浏览器插件。有一次我排查一个状态没更新的问题,用Devtools一看,发现是我在嵌套对象里直接修改了属性,没触发响应式。看着Devtools里清晰的时间线和组件树,比干瞪眼看代码强一百倍。

体验优化:别让用户等得想砸电脑

前端不仅是把功能实现,还得考虑用户体验。产品经理要语音播报,但ElevenLabs的API生成音频是需要时间的,如果用户点击后页面卡死等个三五秒,他绝对会骂娘。

所以我做了两个优化:

  1. 防抖与节流:给发送按钮和语音按钮加了状态锁(isLoadingisPlaying),防止用户疯狂连击。
  2. 流式输出体验:后端的RAG接口其实支持SSE(Server-Sent Events),前端我用EventSource接收流式数据,让答案像打字机一样一个个字蹦出来。虽然代码稍微复杂点,但用户体验直接拉满。

另外,考虑到公司有些老古董电脑还在用低版本的Chrome,我在Vite配置里加了@vitejs/plugin-legacy,顺便把CSS的gap属性做了降级处理,保证在老浏览器上界面不会裂开。

写在最后

折腾了快一个月,这个带AI语音和RAG问答的内部工具总算上线了。测试小姐姐跑了两轮没提什么大Bug,领导在周会上还夸了我两句,这试用期算是稳稳熬过去了。

不过,虽然Vue3用起来确实挺爽,组件化开发和响应式系统也让我这个后端佬感受到了前端的魅力,但我心里其实还是挺纠结的。夜深人静的时候打开招聘软件,看着那些云原生架构师的JD,心里还是会痒痒。

但转念一想,技多不压身。现在这年头,纯后端或者纯前端都太卷了,懂点K8s部署,又能手撕Vue3前端,万一哪天公司裁员,我这种“全干工程师”说不定还能多活几天。

如果你也是零基础想入门Vue.js,听我一句劝:别去啃那些厚厚的老教程,直接上Vue3 + Vite,装好Volar插件,找个实际的小项目(比如给自己写个记账本,或者像我一样接个公司的奇葩需求)边做边查。遇到报错别慌,把报错信息扔给AI,或者去StackOverflow搜搜。

行了,不说了,产品经理又在工作群里@我了,说语音播报的声音不够“御姐”,让我去ElevenLabs后台换个音色。打工人,打工魂,我去调参了!

评论 0

最热最新
暂无评论
JVM炼丹师Lv.1
0
影响力
0
文章
0
粉丝