Skip to content

Vue 基础与组件开发 ​

S3-039建议学进阶约 90 分钟JDK 21企业开发基础 · 前端基础与 Vue

1. 本节定位 ​

《Ajax 与前后端交互》用 fetch 拿到了数据,然后要手工 document.createElement 更新 DOM——繁琐且容易漏。Vue 把这一步变成"数据变了,视图自动变" ✓

text
命令式(原生 JS):手动找到元素 → 修改内容 → 处理事件 → 同步状态
声明式(Vue):    "视图 = f(数据)"  → 你只改数据,框架管渲染 ★

2. 学习目标 ​

  • 理解声明式渲染与命令式的差别 ★★;
  • 会用 ref / reactive 管理响应式状态(Vue 3)★★;
  • 会用 v-if / v-for / v-model 三大指令 ★;
  • 理解组件与 props / emit(父子通信)★★;
  • 知道 Vue 与 React 的取舍(一句话级);
  • 知道"前端框架的默认转义"(《JSP 与 JSTL(技术演进理解)》 的 XSS 防线)★。

3. 前置知识检查 ​

4. 为什么需要这个知识 ​

后端学 Vue 的深度标准:

能看懂前端组件在"调你的哪些接口"、能写简单的展示页、能一起讨论"接口该怎么设计才好用" ✓ ——不需要成为前端专家 ✓

5. 核心概念 ​

5.1 声明式 vs 命令式 ★★ ​

命令式(《HTML 与 CSS 基础》 原生 JS):

javascript
document.querySelector('#count').textContent = orders.length;      // 手动更新 ✓

声明式(Vue):

html
<p v-text="count"></p>       <!-- 数据变 → 自动更新 ✓ -->
javascript
const count = ref(0);         // 响应式数据
count.value = orders.length;  // 只改数据 ✓

5.2 Vue 3 单文件组件 ​

vue
<script setup>
import { ref, onMounted } from 'vue';
import { api } from '@/api';                        // [S3-038](./S3-038) 的封装

const orders = ref([]);
const loading = ref(false);

async function load() {
  loading.value = true;
  try {
    const page = await api('/api/orders?page=1&size=20');
    orders.value = page.data;                        // ★ 数据变 → 视图自动变
  } finally {
    loading.value = false;
  }
}
onMounted(load);                                     // 组件挂载后加载 ✓
</script>

<template>
  <table>
    <tr v-for="o in orders" :key="o.orderNo">        <!-- :key 必须唯一 ★ -->
      <td v-text="o.orderNo"></td>
      <td v-text="(o.amountInCents / 100).toFixed(2)"></td>
    </tr>
  </table>
</template>

★ 三段结构:<script setup>(逻辑)+ <template>(视图)+ <style>(样式)——一个文件一个组件 ✓

★ v-text 与插值等价:<td v-text="o.orderNo"> ≈ <td v-text="o.orderNo"></td>(框架会把表达式的结果渲染进去)✓ (本知识库文档用 v-text 展示,是因为站点的构建器会把双花括号当插值解析——《JSP 与 JSTL(技术演进理解)》 的同款限制,请留意 ⚠️)

5.3 响应式:ref 与 reactive ​

javascript
const count = ref(0);          // 基本类型用 ref;JS 里用 .value 访问 ★
count.value++;

const form = reactive({ productId: '', quantity: 1 });   // 对象用 reactive
form.quantity = 2;
API适用访问方式
ref任何类型(推荐默认)★.value(模板里不需要)
reactive对象/数组直接 .属性(解构会失去响应性 ⚠️)

★ reactive 的坑:解构赋值后变成普通变量 ✗ ——要么不解构,要么用 toRefs ✓

5.4 常用指令 ​

指令作用示例
v-if / v-show条件渲染(前者销毁/重建,后者只是隐藏)★v-if="loading"
v-for列表(必须配 :key)★v-for="o in orders" :key="o.orderNo"
v-model ★★表单双向绑定<input v-model="form.productId">
v-text / v-html输出(v-html 不转义 ✗ XSS,《JSP 与 JSTL(技术演进理解)》)—
@click事件@click="remove(o)"

v-model 的本质:

html
<input v-model="form.productId">
<!-- 等价于 -->
<input :value="form.productId" @input="form.productId = $event.target.value">

★ 它是"数据 → 视图"与"视图 → 数据"的双向绑定 ✓ ——配合 《参数校验与全局异常处理》 的后端校验,形成"前端即时提示 + 后端兜底" ✓

5.5 组件与 props / emit ★★ ​

vue
<!-- 子组件 OrderRow.vue:只负责"展示一行" -->
<script setup>
defineProps({ order: { type: Object, required: true } });
const emit = defineEmits(['remove']);
</script>
<template>
  <tr>
    <td v-text="order.orderNo"></td>
    <td><button @click="emit('remove', order.orderNo)">删除</button></td>
  </tr>
</template>
vue
<!-- 父组件:负责"拿数据"并监听子组件事件 -->
<OrderRow v-for="o in orders" :key="o.orderNo" :order="o" @remove="removeOrder" />

通信规则:

方向机制记法
父 → 子props(单向数据流 ★)"props 不该被子组件修改" ✓
子 → 父emit 事件 ✓子组件"上报",父组件决定 ✓

★ 为什么"props 单向"重要:数据只有一个"权威来源" ——双向修改会让"谁改的"无法追溯 ✗

这与后端"单向依赖"(《Maven 进阶:依赖管理与多模块》 的模块依赖方向)是同一个原则 ✓

5.6 与 React 的一句话对比 ​

VueReact
模板HTML 模板(指令)✓JSX(JS 里写 HTML)
响应式自动追踪(改数据即更新)✓手动 setState ⚠️
心智更"开箱即用" ✓更"灵活/自由"

国内生态两者都主流——选团队熟悉的 ✓

6. 递进示例 ​

6.1 最小示例 ​

html
<div id="app"></div>
<script type="module">
import { createApp, ref } from 'vue';
createApp({ setup() { return { msg: ref('Hello Vue') }; },
           template: `<b v-text="msg"></b>` }).mount('#app');
</script>

6.2 业务示例:加载 + 展示 + 删除 ​

见 5.2 与 5.5 的组合 ✓ ——它就是"订单列表页"的前端版(对应 《Spring MVC 请求映射与参数绑定》 的后端九场景)✓

6.3 反例:在组件里"偷懒"直接操作 DOM ​

javascript
document.querySelector('table').innerHTML = rows.map(...).join('');      // ✗

问题:绕过了响应式 ——Vue 不知道数据变了 ⚠️;而且 innerHTML 拼接用户数据就是 XSS ✗✗(《JSP 与 JSTL(技术演进理解)》)

修:改数据(orders.value = ...),让 Vue 渲染 ✓

7. 完整可运行代码 ​

运行方式:Vue 组件需要构建工具(Vite)。本知识库采用"最小内联"方式—— 完整可运行的 Vue 3 CDN 示例如下(保存为 .html 用浏览器打开 ✓):

html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>Vue 最小示例</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script></head>
<body>
<div id="app">
  <h1 v-text="title"></h1>
  <ul><li v-for="o in orders" :key="o.orderNo" v-text="o.orderNo + ' / ' + o.status"></li></ul>
  <p v-if="orders.length === 0" v-text="'没有订单'"></p>
</div>
<script>
const { createApp, ref } = Vue;
createApp({
  setup() {
    const title = ref('我的订单');
    const orders = ref([
      { orderNo: 'ORD-1', status: '已支付' },
      { orderNo: 'ORD-2', status: '已创建' },
    ]);
    return { title, orders };
  }
}).mount('#app');
</script>
</body>
</html>

★ 这段 HTML 的价值:不装任何工具就能体会"数据驱动视图" ——把 orders 改成空数组刷新,v-if 的那行就会出现 ✓

8. Debug 观察点 ​

  1. Vue DevTools:能看到组件树与每个组件的响应式数据 ★(前端版的"Debug 观察点")✓;
  2. 改数据不生效:检查是不是解构丢了响应性(5.3 的 reactive 坑)✗;
  3. v-for 不加 :key:控制台警告 ⚠️(列表更新时可能错乱)✗;
  4. v-html 注入:v-html="'<script>alert(1)</script>'"——观察它执行 ✗✗(亲眼看一次 XSS,《JSP 与 JSTL(技术演进理解)》)✓。

9. 常见错误 ​

#错误说明
1v-for 不加 :key列表更新错乱 ✗
2解构 reactive失去响应性 ✗
3v-html 渲染用户数据XSS ✗✗(《JSP 与 JSTL(技术演进理解)》)
4直接改 props ✗单向数据流(5.5)——改用 emit ✓
5在模板里写复杂逻辑抽成 computed ✓(模板应保持"声明视图")

10. 动手实验 ​

  1. 用 CDN 示例(第 7 节)体验"改数据 → 视图变" ✓;
  2. 加一个输入框 + 按钮:点击后把输入的订单号加入列表(v-model + 数组 push)✓;
  3. 验证单向数据流:子组件里改 props——观察 Vue 的警告 ✗;
  4. 用 v-html 注入脚本——亲眼看 XSS(仅在本地实验 ⚠️)✓;
  5. 把 《Ajax 与前后端交互》 的 api() 接进来,做成"真调接口的订单页" ✓。

扩展任务:给列表加"筛选":状态下拉框(v-model)变化时重新请求接口——注意"筛选用查询参数还是 POST body"的选择(《Spring MVC 请求映射与参数绑定》 的综合任务)✓

11. 练习题 ​

  1. 判断:const arr = ref([]) 后 arr.value.push(1) 会触发视图更新。(对 / 错)
  2. 填空:父传子用 ______,子通知父用 ______;v-for 必须配 ______。
  3. 简答:v-if 与 v-show 的取舍?
  4. (代码阅读)<input v-model="form.qty"> 等价于哪两件事?
  5. (编程)实现"订单列表 + 删除(调用 DELETE 接口)+ 成功后刷新"。
参考答案(要点)
  1. 对 ✓(ref 的 .value 变化会被追踪)
  2. props;emit 事件;:key ✓
  3. v-if 真正增删元素(初始 false 不渲染);v-show 只切 display ✓ 取舍:频繁切换用 v-show(开销小);初始大概率不显示/含重组件用 v-if ✓
  4. :value="form.qty" + @input="form.qty = $event.target.value"(5.4 的等价展开)✓
  5. 见 5.2/5.5 组合:删除用 emit('remove', orderNo) → 父组件调 DELETE → 成功后从 orders.value 里过滤掉 ✓(注意 《Ajax 与前后端交互》 的 204 处理 ✓)

12. 掌握标准 ​

  • [ ] 理解"声明式渲染" ★★;
  • [ ] 会用 ref / reactive 与三大指令(v-if / v-for / v-model);
  • [ ] 理解组件与 props 单向数据流 ★★;
  • [ ] 知道 v-html 的 XSS 风险(《JSP 与 JSTL(技术演进理解)》);
  • [ ] 知道"改数据不生效"要先查响应性。

13. 扩展资源 ​

14. 下一步学习 ​

《登录状态与 Token 机制》:登录之后,"凭证"在前端怎么存、怎么带、怎么续。

进度只保存在本机浏览器,可随时清除

个人学习项目:AI 生成内容均经人工复核,第三方资源仅作索引