Vue

技术栈
前端框架
vuevue3composition-apispamvvm前端

概览

Vue 技术栈概览

Vue 是由尤雨溪(Evan You)创建的渐进式 JavaScript 框架,专注于视图层渲染,通过响应式数据绑定和组合式 API 让构建用户界面变得简单直观。

解决什么问题

  • 渐进式集成:可以只作为页面的一部分 jQuery 替代,也可构建完整 SPA
  • 响应式数据:自动追踪依赖,数据变化自动更新 DOM,无需手动操作
  • 单文件组件(SFC).vue 文件包含 template + script + style,关注点集中
  • 低学习曲线:模板语法接近 HTML,比 React JSX 更直观

关键特性

  • Composition API(Vue 3):ref/reactive + setup,逻辑复用更灵活
  • Vite 原生支持:Vue 3 默认构建工具,秒级 HMR 热更新
  • Pinia 状态管理:官方推荐替代 Vuex,TypeScript 友好
  • Vue Router:官方路由方案,支持嵌套/动态/导航守卫
  • Nuxt:Vue 全栈元框架,SSR/SSG/文件路由
  • 生态完整:Vuetify、Element Plus、Naive UI、Nuxt UI 等组件库

毕设场景

  • 毕设管理系统前端(Vue3 + Element Plus + Pinia)
  • 电商/博客全栈(Nuxt3 + Prisma + PostgreSQL)
  • 数据可视化看板(Vue3 + ECharts + Axios)
  • 结合前面录入的 NestJS/Express 后端做全栈毕设

安装

Vue 安装指南

1. 环境准备

  • Node.js:>= 18.x(推荐 20 LTS)
  • 包管理器:npm(Node 自带)或 pnpm(性能更优)
  • IDE:VS Code + Volar 插件(官方推荐,替代 Vetur)
  • 浏览器:Chrome/Edge + Vue Devtools 扩展

2. 安装命令

方式一:Vite 脚手架(推荐)

# npm
npm create vue@latest

# pnpm(更快)
pnpm create vue@latest

# 交互式选择:TypeScript / JSX / Vue Router / Pinia / Vitest...

cd my-vue-app
npm install
npm run dev
# → http://localhost:5173

方式二:CDN 引入(简单页面)

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const { createApp, ref } = Vue
  createApp({
    setup() {
      return { count: ref(0) }
    }
  }).mount('#app')
</script>

方式三:Nuxt 全栈项目

npx nuxi@latest init my-nuxt-app
cd my-nuxt-app
npm run dev

3. 常见安装问题

Q: npm create vue@latest 失败?
A: Node.js 版本需 >=18;尝试 npx create-vue@latest 或换 pnpm;国内用户可设置镜像 npm config set registry https://registry.npmmirror.com

Q: VSCode 中 .vue 文件没有高亮?
A: 安装 Vue - Official (Volar) 插件;禁用旧版 Vetur;启用 Take Over Mode

Q: TypeScript 报错 "Cannot find module '*.vue'"?
A: 确保 env.d.ts 包含 declare module '*.vue' 声明(脚手架已自动生成)

示例

Vue 3 代办事项(Composition API)

目标

用 Composition API 写一个经典的 Todo List,涵盖 refv-modelv-forcomputed 等核心概念。

完整代码

src/App.vue

<script setup lang="ts">
import { ref, computed } from 'vue'

interface Todo {
  id: number
  text: string
  done: boolean
}

const todos = ref<Todo[]>([])
const newTodo = ref('')

const remaining = computed(() => todos.value.filter(t => !t.done).length)

function addTodo() {
  const text = newTodo.value.trim()
  if (!text) return
  todos.value.push({ id: Date.now(), text, done: false })
  newTodo.value = ''
}

function removeTodo(id: number) {
  todos.value = todos.value.filter(t => t.id !== id)
}
</script>

<template>
  <div class="container">
    <h1>📋 代办事项 ({{ remaining }} 项未完成)</h1>

    <form @submit.prevent="addTodo" class="input-group">
      <input
        v-model="newTodo"
        placeholder="输入新任务..."
        class="todo-input"
      />
      <button type="submit" class="btn-add">添加</button>
    </form>

    <ul class="todo-list">
      <li
        v-for="todo in todos"
        :key="todo.id"
        :class="{ done: todo.done }"
        @click="todo.done = !todo.done"
      >
        <span class="check">{{ todo.done ? '✅' : '⬜' }}</span>
        <span class="text">{{ todo.text }}</span>
        <button @click.stop="removeTodo(todo.id)" class="btn-del">🗑️</button>
      </li>
    </ul>

    <p v-if="todos.length === 0" class="empty">暂无任务,添加一个吧~</p>
  </div>
</template>

<style scoped>
.container { max-width: 480px; margin: 2rem auto; font-family: system-ui; }
h1 { font-size: 1.5rem; margin-bottom: 1rem; }
.input-group { display: flex; gap: 8px; margin-bottom: 1rem; }
.todo-input { flex: 1; padding: 8px 12px; border: 2px solid #e2e8f0; border-radius: 8px; font-size: 1rem; }
.btn-add { padding: 8px 20px; background: #3b82f6; color: white; border: none; border-radius: 8px; cursor: pointer; }
.todo-list { list-style: none; padding: 0; }
.todo-list li { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid #f1f5f9; cursor: pointer; }
.todo-list li.done .text { text-decoration: line-through; color: #94a3b8; }
.text { flex: 1; }
.btn-del { background: none; border: none; cursor: pointer; opacity: 0.5; }
.btn-del:hover { opacity: 1; }
.empty { color: #94a3b8; text-align: center; }
</style>

运行

npm install &;& npm run dev

预期输出

浏览器打开后显示代办事项界面,可添加/勾选/删除任务,剩余计数实时更新。

教程

Vue 3 毕设实战入门

背景

Vue 3 是目前国内使用最广泛的前端框架之一,面试必问、毕设高频。其渐进式设计让你能从简单的数据绑定逐步过渡到大型 SPA 应用。

核心概念

1. 响应式系统

Vue 3 使用 Proxy 实现响应式,替代了 Vue 2 的 Object.defineProperty

import { ref, reactive, computed, watch } from 'vue'

// ref — 基本类型
const count = ref(0)
count.value++ // 模板中自动解包,写 count 即可

// reactive — 对象
const user = reactive({ name: '张三', age: 22 })

// computed — 派生状态
const doubled = computed(() => count.value * 2)

// watch — 监听变化
watch(count, (newVal, oldVal) => {
  console.log(`count 从 ${oldVal} 变为 ${newVal}`)
})

2. Composition API vs Options API

特性 Options API Composition API
数据 data() ref / reactive
方法 methods 普通函数
计算属性 computed computed()
生命周期 mounted onMounted()
逻辑复用 Mixins(冲突) 自定义 Composables
TypeScript 支持一般 原生完美支持

毕设建议:一律用 Composition API + <script setup>,这是 Vue 3 的默认推荐。

3. 组件通信

<!-- 父传子:props -->
<Child :title="pageTitle" :count="10" />

<!-- 子组件接收 -->
<script setup>
const props = defineProps<{ title: string; count: number }>()
</script>

<!-- 子传父:emit -->
<script setup>
const emit = defineEmits<{ 
  update: [value: string]
  close: []
}>()
</script>

4. 状态管理:Pinia

// stores/user.ts
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', () => {
  const token = ref('')
  const userInfo = ref(null)

  async function login(username: string, password: string) {
    const res = await fetch('/api/login', { method: 'POST', body: JSON.stringify({ username, password }) })
    const data = await res.json()
    token.value = data.token
  }

  return { token, userInfo, login }
})

分步操作:搭建毕设基础架构

Step 1: 创建项目

npm create vue@latest my-graduation-project
# 选择:TypeScript + Vue Router + Pinia + Vitest
cd my-graduation-project
npm install
npm run dev

Step 2: 安装 UI 组件库(Element Plus)

npm install element-plus @element-plus/icons-vue
// main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'

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

Step 3: 目录结构

src/
  api/          # 接口请求
  components/   # 公共组件
  composables/  # 自定义 hooks
  stores/       # Pinia store
  views/        # 页面
  router/       # 路由配置
  utils/        # 工具函数

Step 4: 前后端联调

// api/request.ts
import axios from 'axios'

const http = axios.create({
  baseURL: 'http://localhost:3000/api',  // 你的后端地址
  timeout: 10000,
})

// 请求拦截器 — 加 Token
http.interceptors.request.use(config => {
  const token = localStorage.getItem('token')
  if (token) config.headers.Authorization = `Bearer ${token}`
  return config
})

export default http

思考题

  1. refreactive 有什么区别?什么场景用哪个?
  2. 如何用 defineExpose 向父组件暴露子组件方法?
  3. Vue Router 的路由守卫如何实现登录拦截?

毕设常用搭配

场景 技术栈
管理后台 Vue3 + Element Plus + Pinia + Axios
全栈 Nuxt3 + Prisma + PostgreSQL
移动端 Vue3 + Vant UI
可视化 Vue3 + ECharts + Axios