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会自动更新!

相关推荐
m0_694845572 小时前
网站账号太多难管理?Enterr 开源自动化工具搭建教程
运维·服务器·前端·开源·自动化·云计算
光影少年2 小时前
react中redux的connect作用是什么
前端·react.js·前端框架
芋头莎莎2 小时前
基于MQTT通讯UNIapp程序解析JSON数据
前端·uni-app·json
2601_949847752 小时前
Flutter for OpenHarmony 剧本杀组队App实战:邀请好友功能实现
开发语言·javascript·flutter
weixin_436525072 小时前
若依多租户版: 页面新增菜单, 执行菜单SQL
前端·数据库·sql
FITA阿泽要努力2 小时前
Agent Engineer-Day 1 初始智能体与大语言模型基础
java·前端·javascript
霸王蟹3 小时前
Uni-app 跨端开发框架Unibest快速体验
前端·笔记·微信·uni-app·unibest
zihan03213 小时前
element-plus, el-table 表头按照指定字段升降序的功能实现
前端·vue.js·状态模式
三翼鸟数字化技术团队3 小时前
watchEffect的两种错误用法
前端·javascript·vue.js