Files
gc-plan/week6/教案.md
2026-04-29 23:45:17 +08:00

9.7 KiB
Raw Blame History

Week 6Vue 3 前端框架入门

目标:掌握 Vue 3 Composition API + Vite + Vue Router + Pinia能独立开发 SPA 应用。

前置:已完成 Week 5 Spring Boot 后端,有 HTML/CSS/JS 基础。

本周产出:一个独立的 Todo SPA 应用,包含计数器、动态表单、组件拆分、路由导航、状态管理和 HTTP 封装。

启动方式

cd week6
npm install    # 首次运行
npm run dev    # 启动开发服务器 → http://localhost:5173

Day 1Vue 3 介绍、Vite 脚手架、组件基础

知识点

  • Vue 3 是什么:渐进式前端框架,核心是"响应式数据 + 声明式渲染"
  • SFCSingle File Component.vue 文件 = <template> + <script setup> + <style scoped>
  • Composition API vs Options API为什么选 Composition API
  • Vite新一代构建工具冷启动快ES Module、HMR 极速
  • 项目结构:main.js(入口)、App.vue(根组件)、components/views/

动手 → 理解

  1. 打开 src/main.js:理解 createApp(App).mount('#app') 的过程
  2. 打开 src/App.vue:观察 <script setup> 语法糖
  3. 修改 index.html 中的 <title>,观察浏览器标签变化
  4. HomeView.vue<template> 中加一行 <p>Hello Vue!</p>,观察热更新
  5. style scoped 去掉,观察样式是否泄漏到其他组件

核心文件

  • index.html — 入口 HTML<div id="app"> 挂载点
  • src/main.js — 应用初始化
  • src/App.vue — 根组件
  • vite.config.js — Vite 配置(插件、代理、端口)

思考题<script setup> 和普通的 <script> 有什么区别?为什么 Vue 3 推荐 setup 语法糖?


Day 2响应式数据ref/reactive、计算属性

知识点

  • ref()包装基本类型number、string、boolean通过 .value 访问
  • reactive():包装对象/数组,直接访问属性(不需要 .value
  • computed():计算属性,依赖变化时自动重新计算,带缓存
  • watch() / watchEffect():侦听数据变化执行副作用
  • 响应式的本质Proxy 拦截 getter/setter数据变 → 视图自动变
  • 模板中 ref 自动解包(不需要写 .value

动手 → 理解

  1. 打开 CounterView.vue,在模板中写 {{ localCount }},不需要 .value
  2. <script> 中用 console.log(localCount.value) 打印当前值
  3. 修改 computed 的计算逻辑,改为判断"是否大于 10"
  4. 添加一个 watch,在 localCount 变化时 console.log('变了:', newVal)
  5. 对比:把 reactive 改为 ref 写一个对象,体会 .value 的区别

核心文件

  • views/CounterView.vue — ref + computed 演示
  • stores/counter.js — Pinia store第 6 天预热)

思考题:为什么模板中 ref 自动解包,但 <script> 里必须用 .value


Day 3指令系统v-if/v-for/v-model

知识点

  • v-if / v-else-if / v-else:条件渲染(销毁/重建 DOM
  • v-show:条件显示(仅切换 display)—— 适合频繁切换
  • v-for:列表渲染,必须有 :key 唯一标识
  • v-bind(缩写 :):动态绑定属性 :class:style:href
  • v-model:双向绑定,表单输入 ↔ 数据自动同步
  • v-on(缩写 @):事件绑定 @click@submit@keyup
  • v-model 修饰符:.trim.number.lazy

动手 → 理解

  1. 打开 FormDemoView.vue,在输入框中输入,观察下方实时预览
  2. 用 DevTools 检查:勾选"显示详细信息"时,v-if 的元素是否在 DOM 中?
  3. v-if 换成 v-show,再次检查 DOM 差异
  4. v-for<li> 中故意不写 :key,观察控制台警告
  5. 尝试加一个 v-model.lazy 的输入框,对比 input 事件和 change 事件的区别

核心文件

  • views/FormDemoView.vue — 指令综合演示

思考题:什么时候用 v-if,什么时候用 v-show?为什么 v-forv-if 不建议同时使用?


Day 4组件通信props/emits、插槽

知识点

  • defineProps():父组件向子组件传递数据(单向数据流)
  • defineEmits():子组件向父组件发送事件(回调模式)
  • Prop 校验:typerequireddefaultvalidator
  • 单向数据流原则:子组件不能直接修改 props
  • v-model 组件版:modelValue + update:modelValue
  • <slot> 插槽:父组件向子组件传递模板内容
  • 命名插槽、作用域插槽(高级)

动手 → 理解

  1. 打开 TodoForm.vueTodoItem.vue,对照看 props 和 emits
  2. TodoItem 中尝试直接 todo.done = true(不通过 emit观察 Pinia 是否感知
  3. TodoItem 的 props 加一个 validator,限制 todo.text 不能为空
  4. 练习:新建一个 <Card> 组件,用 <slot> 让父组件填充内容
  5. 尝试用 v-model 模式改写 TodoForm(父组件用 v-model 双向绑定输入值)

核心文件

  • components/TodoForm.vue — props 接收 / emits 发送
  • components/TodoItem.vue — props 接收 / emits 发送
  • views/TodoView.vue — 父组件,组装 TodoForm + TodoItem

思考题:为什么不直接在子组件中操作 Pinia store而是通过 props/emits 层层传递?


Day 5Vue Router 路由

知识点

  • vue-router前端路由URL 变化时不刷新页面
  • createRouter({ history: createWebHistory() })HTML5 History 模式
  • <router-link>:导航链接(渲染为 <a> 标签)
  • <router-view>:路由出口(匹配的组件渲染在这里)
  • 路由懒加载:() => import('../views/XXX.vue')
  • meta:路由元信息(标题、权限等)
  • 导航守卫:beforeEachafterEach
  • 动态路由:/user/:id

动手 → 理解

  1. 点击导航栏各链接,观察 URL 变化和页面内容,按 F12 Network 面板看是否有网络请求
  2. router/index.js 中临时把 createWebHistory 改为 createWebHashHistory,观察 URL 变化(# 号出现)
  3. 修改某个路由的 meta.title,观察浏览器标签变化
  4. 在导航守卫中加 console.log('从', from.path, '到', to.path)
  5. 添加一个 /user/:name 动态路由页面

核心文件

  • router/index.js — 路由配置 + 导航守卫
  • components/NavBar.vue<router-link> 导航

思考题:为什么前端路由要用 History 模式而不是 Hash 模式History 模式上线需要注意什么Nginx 配置)?


Day 6Pinia 状态管理

知识点

  • 为什么需要状态管理:跨组件/跨页面共享数据
  • defineStore('name', () => { ... })Setup Store 语法(与 Composition API 一致)
  • Stateref() / reactive() 定义状态
  • Getterscomputed() 定义派生状态
  • Actions普通函数可异步
  • storeToRefs():解构时保持响应性
  • Store 在 main.js 中通过 app.use(createPinia()) 注入
  • 组件中直接 const store = useXxxStore() 使用

动手 → 理解

  1. 打开 stores/todo.js,观察 state/getters/actions 的定义方式
  2. 在浏览器中操作 Todo打开 Vue DevTools 的 Pinia 面板查看状态变化
  3. CounterView.vue 中对比Pinia store 的 counter 和本地 ref 的 counter
  4. storeToRefs(todoStore) 解构 todos,验证响应性
  5. 练习:新建一个 theme store管理暗色/亮色主题切换

核心文件

  • stores/counter.js — 简单 store 示例
  • stores/todo.js — 完整 CRUD store
  • stores/auth.js — 认证 Token store
  • main.jsapp.use(createPinia())

思考题Pinia 和 Vuex 有什么区别?为什么 Vue 3 官方推荐 Pinia


Day 7Axios 封装与请求拦截

知识点

  • axios.create():创建实例,配置 baseURL、timeout
  • 请求拦截器:自动附加 Token、设置 Content-Type
  • 响应拦截器:统一处理 401/403/500
  • Authorization: Bearer <token>JWT 标准格式
  • Vite proxy 配置:开发环境代理 /api 到 Spring Boot
  • .env 环境变量:VITE_API_BASE_URL

动手 → 理解

  1. 打开 api/http.js,对照 Week 5 的 app.jsapi() 函数,对比封装方式
  2. 用 DevTools Network 面板观察请求头中是否有 Authorization
  3. 故意把 token 改错,观察响应拦截器的 401 处理
  4. 检查 vite.config.js 的 proxy 配置,理解开发环境代理原理
  5. 添加 VITE_API_BASE_URL 环境变量,修改 baseURL 读取环境变量

核心文件

  • api/http.js — Axios 实例 + 拦截器
  • vite.config.jsserver.proxy — 开发代理
  • stores/auth.js — Token 存储与模拟登录

思考题:为什么开发环境需要 Vite proxy而生产环境用 Nginx 反向代理?


Week 6 总结

维度 掌握内容
脚手架 Vite 创建项目、目录结构、HMR 热更新
响应式 ref / reactive / computed / watch
指令 v-if / v-show / v-for / v-model / v-bind / v-on
组件 SFC、props / emits、slot、scoped style
路由 Vue Router、懒加载、导航守卫
状态 Pinia Setup Store、storeToRefs
网络 Axios 封装、拦截器、Vite proxy

与 Week 5 的对比

Week 5 前端 Week 6 前端
框架 原生 HTML + 原生 JS Vue 3 + Vite
状态管理 localStorage + DOM 操作 Pinia 响应式 store
路由 无(单页面切换 display Vue Router History 模式
HTTP fetch 手写 Axios 拦截器封装
组件化 SFC + props/emits
开发体验 手动刷新 HMR 热更新

预习 Week 7:将本项目的 Vue 前端与 Week 5 的 Spring Boot 后端对接,用 Axios 替换模拟登录,实现前后端分离的学生管理系统 v3。