Vue基础语法与响应式系统详解

📘 Vue基础语法与响应式系统详解

作为后端开发者,您已经熟悉数据和状态管理的概念。Vue的响应式系统是其核心特色,让您只需关注数据变化,UI会自动更新。

🎯 Vue 3 的两种API风格

1. 选项式 API (Options API) - 适合初学者

复制代码
<template>
  <div>{{ count }}</div>
  <button @click="increment">+1</button>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  },
  computed: {
    doubleCount() {
      return this.count * 2
    }
  },
  watch: {
    count(newVal, oldVal) {
      console.log(`从 ${oldVal} 变为 ${newVal}`)
    }
  },
  mounted() {
    console.log('组件已挂载')
  }
}
</script>

2. 组合式 API (Composition API) - 推荐!更灵活

复制代码
<template>
  <div>{{ count }}</div>
  <button @click="increment">+1</button>
  <p>双倍: {{ doubleCount }}</p>
</template>

<script setup>
// 导入响应式API
import { ref, computed, watch, onMounted } from 'vue'

// 1. 响应式变量
const count = ref(0)           // 基本类型用 ref
const user = reactive({         // 对象类型用 reactive
  name: '张三',
  age: 25
})

// 2. 计算属性
const doubleCount = computed(() => count.value * 2)

// 3. 方法
const increment = () => {
  count.value++                // 注意:ref 需要 .value
  user.age = 30                // reactive 直接修改属性
}

// 4. 侦听器
watch(count, (newVal, oldVal) => {
  console.log(`count变化: ${oldVal} -> ${newVal}`)
})

// 5. 生命周期
onMounted(() => {
  console.log('组件挂载完成')
})
</script>

🔄 响应式核心:ref vs reactive

ref - 创建任何类型的响应式数据

复制代码
import { ref } from 'vue'

// 基本类型
const count = ref(0)
console.log(count.value) // 0

// 对象类型也可以
const user = ref({ name: '张三' })
console.log(user.value.name) // 张三

// 数组
const list = ref([1, 2, 3])
console.log(list.value[0]) // 1

reactive - 创建响应式对象

复制代码
import { reactive } from 'vue'

const state = reactive({
  count: 0,
  user: {
    name: '张三',
    age: 25
  },
  list: [1, 2, 3]
})

// 直接访问,不需要 .value
state.count = 1
state.user.name = '李四'
state.list.push(4)

何时使用哪种?

场景 使用 原因
基本类型 (string, number, boolean) ref reactive 不处理基本类型
对象类型 都可,reactive更简洁 reactive 修改时不需要 .value
数组 都可,ref更常见 方便重新赋值整个数组
函数返回值 ref 可以返回单个响应式变量

📊 响应式API全览

复制代码
import { 
  ref,           // 创建响应式数据
  reactive,      // 创建响应式对象
  computed,      // 计算属性
  watch,         // 侦听单个数据
  watchEffect,   // 自动追踪依赖
  readonly,      // 创建只读对象
  toRef,         // 响应式对象转ref
  toRefs,        // 响应式对象解构
  shallowRef,    // 浅层ref
  shallowReactive, // 浅层reactive
  triggerRef     // 手动触发更新
} from 'vue'

🎪 模板语法

1. 文本插值

复制代码
<template>
  <p>Message: {{ message }}</p>
  <p>计算属性: {{ reversedMessage }}</p>
  <p>方法调用: {{ formatDate(new Date()) }}</p>
</template>

2. 属性绑定

复制代码
<template>
  <!-- 动态属性 -->
  <div :id="dynamicId"></div>
  <img :src="imageSrc" :alt="altText">
  
  <!-- 类名绑定 -->
  <div :class="{ active: isActive, 'text-danger': hasError }"></div>
  <div :class="[isActive ? 'active' : '', 'base']"></div>
  
  <!-- 样式绑定 -->
  <div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
  <div :style="[baseStyles, overridingStyles]"></div>
</template>

3. 条件渲染

复制代码
<template>
  <!-- v-if / v-else-if / v-else -->
  <div v-if="type === 'A'">A</div>
  <div v-else-if="type === 'B'">B</div>
  <div v-else>其他</div>
  
  <!-- v-show (只切换display) -->
  <div v-show="isVisible">可见时显示</div>
</template>

4. 列表渲染

复制代码
<template>
  <!-- 数组渲染 -->
  <ul>
    <li v-for="(item, index) in items" :key="item.id">
      {{ index }} - {{ item.name }}
    </li>
  </ul>
  
  <!-- 对象渲染 -->
  <ul>
    <li v-for="(value, key, index) in user" :key="key">
      {{ index }}. {{ key }}: {{ value }}
    </li>
  </ul>
  
  <!-- 范围循环 -->
  <span v-for="n in 10" :key="n">{{ n }}</span>
</template>

5. 事件处理

复制代码
<template>
  <!-- 内联处理器 -->
  <button @click="count++">增加</button>
  
  <!-- 方法处理器 -->
  <button @click="greet('Hello')">打招呼</button>
  
  <!-- 事件修饰符 -->
  <form @submit.prevent="onSubmit">
    <button @click.stop="doThis">阻止冒泡</button>
    <a @click.once="doThis">只触发一次</a>
  </form>
  
  <!-- 按键修饰符 -->
  <input @keyup.enter="submit" />
  <input @keyup.ctrl.enter="submit" />
</template>

6. 表单绑定

复制代码
<template>
  <!-- 文本 -->
  <input v-model="text" placeholder="请输入" />
  <p>输入的内容: {{ text }}</p>
  
  <!-- 多行文本 -->
  <textarea v-model="message"></textarea>
  
  <!-- 复选框 -->
  <input type="checkbox" id="checkbox" v-model="checked" />
  <label for="checkbox">{{ checked }}</label>
  
  <!-- 多个复选框 -->
  <input type="checkbox" id="jack" value="Jack" v-model="checkedNames" />
  <input type="checkbox" id="john" value="John" v-model="checkedNames" />
  
  <!-- 单选按钮 -->
  <input type="radio" id="one" value="One" v-model="picked" />
  <input type="radio" id="two" value="Two" v-model="picked" />
  
  <!-- 选择框 -->
  <select v-model="selected">
    <option disabled value="">请选择</option>
    <option>A</option>
    <option>B</option>
  </select>
</template>

🔍 计算属性 vs 侦听器

计算属性 (computed) - 依赖缓存

复制代码
import { ref, computed } from 'vue'

const firstName = ref('张')
const lastName = ref('三')

// 自动追踪 firstName 和 lastName
const fullName = computed(() => {
  console.log('计算属性执行')
  return `${firstName.value}${lastName.value}`
})
// 只有当 firstName 或 lastName 变化时才会重新计算

侦听器 (watch) - 响应变化

复制代码
import { ref, watch, watchEffect } from 'vue'

const count = ref(0)
const user = reactive({ name: '张三', age: 25 })

// 1. 侦听单个 ref
watch(count, (newVal, oldVal) => {
  console.log(`count变化: ${oldVal} -> ${newVal}`)
})

// 2. 侦听 reactive 对象的属性
watch(() => user.name, (newVal, oldVal) => {
  console.log(`名字变化: ${oldVal} -> ${newVal}`)
})

// 3. 侦听多个源
watch([count, () => user.name], ([newCount, newName]) => {
  console.log(`count: ${newCount}, name: ${newName}`)
})

// 4. watchEffect - 自动追踪依赖
watchEffect(() => {
  console.log(`count: ${count.value}, name: ${user.name}`)
})

🔄 响应式转换工具

toRef / toRefs - 保持响应式

复制代码
import { reactive, toRef, toRefs } from 'vue'

const state = reactive({
  name: '张三',
  age: 25
})

// 解构会丢失响应性
const { name, age } = state // ❌ 不是响应式

// 使用 toRef 保持响应性
const nameRef = toRef(state, 'name') // ✅ 响应式

// 使用 toRefs 解构所有属性
const { name, age } = toRefs(state) // ✅ 都是响应式

🎮 实战例子:计数器组件

复制代码
<template>
  <div class="counter">
    <h2>计数器: {{ count }}</h2>
    <p>双倍值: {{ doubleCount }}</p>
    <p>平方值: {{ squareCount }}</p>
    
    <div class="buttons">
      <button @click="increment">增加 (+1)</button>
      <button @click="decrement">减少 (-1)</button>
      <button @click="reset">重置</button>
    </div>
    
    <div class="history">
      <h3>历史记录</h3>
      <ul>
        <li v-for="(record, index) in history" :key="index">
          {{ record.timestamp }}: {{ record.oldValue }} → {{ record.newValue }}
        </li>
      </ul>
    </div>
  </div>
</template>

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

// 响应式状态
const count = ref(0)
const history = ref([])

// 计算属性
const doubleCount = computed(() => count.value * 2)
const squareCount = computed(() => count.value * count.value)

// 方法
const increment = () => {
  count.value++
}

const decrement = () => {
  count.value--
}

const reset = () => {
  count.value = 0
}

// 侦听器
watch(count, (newVal, oldVal) => {
  console.log(`计数器变化: ${oldVal} -> ${newVal}`)
  
  // 记录历史
  history.value.unshift({
    timestamp: new Date().toLocaleTimeString(),
    oldValue: oldVal,
    newValue: newVal
  })
  
  // 最多保留5条记录
  if (history.value.length > 5) {
    history.value.pop()
  }
})

// 生命周期
onMounted(() => {
  console.log('计数器组件已挂载')
})
</script>

<style scoped>
.counter {
  padding: 20px;
  border: 1px solid #ccc;
  border-radius: 8px;
  max-width: 400px;
  margin: 0 auto;
}

.buttons {
  display: flex;
  gap: 10px;
  margin: 20px 0;
}

.buttons button {
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  background-color: #3498db;
  color: white;
  cursor: pointer;
  transition: background-color 0.3s;
}

.buttons button:hover {
  background-color: #2980b9;
}

.history {
  margin-top: 20px;
  text-align: left;
}

.history ul {
  list-style: none;
  padding: 0;
}

.history li {
  padding: 5px 0;
  border-bottom: 1px solid #eee;
}
</style>

🔧 响应式进阶技巧

1. 响应式数组处理

复制代码
import { ref } from 'vue'

const list = ref([1, 2, 3])

// 以下操作是响应式的
list.value.push(4)          // 末尾添加
list.value.pop()            // 末尾移除
list.value.shift()          // 开头移除
list.value.unshift(0)       // 开头添加
list.value.splice(1, 1)     // 删除/替换
list.value.sort()           // 排序
list.value.reverse()        // 反转

// 以下操作不是响应式的
list.value[0] = 100         // ❌ Vue 3 中现在也是响应式的
list.value.length = 0       // ❌ 仍然不是响应式的

2. 深度侦听

复制代码
import { reactive, watch } from 'vue'

const obj = reactive({
  nested: {
    deep: {
      value: 1
    }
  }
})

// 深度侦听
watch(
  () => obj.nested.deep,
  (newVal, oldVal) => {
    console.log('深度变化', newVal, oldVal)
  },
  { deep: true }  // 深度侦听
)

// 立即执行
watch(
  () => obj.nested.deep.value,
  (newVal, oldVal) => {
    console.log('值变化', newVal, oldVal)
  },
  { immediate: true }  // 立即执行一次
)

🎯 后端开发者特别关注点

与后端状态的对比

Vue 响应式 后端状态管理 区别
ref/reactive 数据库/缓存 自动UI更新 vs 持久化存储
computed SQL视图/计算字段 自动缓存依赖 vs 需要手动刷新
watch 触发器/监听器 前端UI响应 vs 后端业务逻辑

最佳实践

复制代码
// 1. 类型提示 (TypeScript)
import type { Ref } from 'vue'

interface User {
  id: number
  name: string
  age: number
}

// 明确类型
const user: Ref<User> = ref({ id: 1, name: '张三', age: 25 })

// 2. 批量更新
import { nextTick } from 'vue'

const updateMultiple = async () => {
  count.value++
  user.value.age = 30
  list.value.push(4)
  
  // DOM更新后执行
  await nextTick()
  console.log('DOM已更新')
}

// 3. 防抖/节流的watch
import { debounce } from 'lodash-es'

watch(
  () => searchInput.value,
  debounce((newVal) => {
    searchAPI(newVal)
  }, 300)
)

📚 学习建议

  1. 动手实践 :在 Vue Playground (https://play.vuejs.org) 中尝试

  2. 理解原理:响应式是 Proxy 实现的

  3. 对比学习:对比 React 的 useState 和 Vue 的 ref

  4. 调试工具:安装 Vue DevTools 浏览器扩展

🎪 快速测验

复制代码
<template>
  <div>
    <h1>响应式测验</h1>
    <p>count: {{ count }}</p>
    <p>user: {{ user }}</p>
    <button @click="update">更新</button>
  </div>
</template>

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

// 问题1: 以下哪个是正确的?
const count = ref(0)
const user = reactive({ name: '张三' })

const update = () => {
  // 问题2: 如何正确更新?
  count.value += 1          // ✅
  // count += 1             // ❌
  
  user.name = '李四'        // ✅
  // user.value.name = '李四' // ❌
}
</script>

掌握 Vue 响应式是掌握 Vue 的关键。作为后端开发者,您已经理解状态管理,现在只需将这种理解应用到前端UI交互中。记住:Vue 让您只关注数据的变化,UI会自动更新!

相关推荐
陈随易1 小时前
Postman 被曝自动往 Claude Code 和 Codex 装插件
前端·后端·程序员
Dream-Y.ocean3 小时前
鸿蒙PC平台宅匠 3D 室内设计适配实战:基于 Electron 壳方案的 Three.js 双渲染引擎与参数化户型编辑器开发
javascript·3d·harmonyos
Csvn4 小时前
性能监控
前端
Highcharts.js4 小时前
Highcharts Gantt使用符号的自定义数据标签开发甘特图示例
javascript·甘特图·highcharts·图表开发·实战代码·响应式图表
Csvn5 小时前
构建工具
前端
FRing035 小时前
vue2干货总结3(computed计算属性、watch监听器)
开发语言·前端·vue.js·vue2
Java小卷6 小时前
JuanForm vs FormCreate!Vue3 表单引擎联动配置 PK!
vue.js·低代码
kyriewen7 小时前
Skills 榜前 10 我全装了:只有 3 个能直接用
前端·javascript·程序员
IT_陈寒7 小时前
当Java的hashCode遇上Lombok,我的缓存被全清了
前端·人工智能·后端
工控丫丫8 小时前
帆软看板中经常使用的公式函数及经常搭的组合
前端·javascript·数据库