Vue3(篇一) 核心概念——响应式模板语法与组件基础

目录

  • 前言
  • [一、Vue 是什么?](#一、Vue 是什么?)
    • [1.1 是什么](#1.1 是什么)
    • [1.2 Vue 的核心思想](#1.2 Vue 的核心思想)
  • 二、技术选型概览
  • 三、环境准备
    • [3.1 创建项目](#3.1 创建项目)
    • [3.2 配置开发工具](#3.2 配置开发工具)
    • [3.3 安装调试工具](#3.3 安装调试工具)
    • [3.4 配置开发服务器](#3.4 配置开发服务器)
  • 四、项目结构解析
    • [4.1 入口文件:index.html](#4.1 入口文件:index.html)
    • [4.2 应用入口:main.ts](#4.2 应用入口:main.ts)
    • [4.3 根组件:App.vue](#4.3 根组件:App.vue)
  • [五、响应式数据:ref 与 reactive](#五、响应式数据:ref 与 reactive)
    • [5.1 为什么需要响应式?](#5.1 为什么需要响应式?)
    • [5.2 ref:包装任意类型](#5.2 ref:包装任意类型)
    • [5.3 reactive:包装对象类型](#5.3 reactive:包装对象类型)
    • [5.4 ref vs reactive:如何选择?](#5.4 ref vs reactive:如何选择?)
  • 六、数据绑定
    • [6.1 文本绑定:双花括号](#6.1 文本绑定:双花括号)
    • [6.2 属性绑定:v-bind(简写 `:`)](#6.2 属性绑定:v-bind(简写 :))
    • [6.3 事件绑定:v-on(简写 `@`)](#6.3 事件绑定:v-on(简写 @))
    • [6.4 表单绑定:v-model(双向绑定)](#6.4 表单绑定:v-model(双向绑定))
  • 七、计算属性与监听器
    • [7.1 计算属性:computed](#7.1 计算属性:computed)
    • [7.2 监听器:watch](#7.2 监听器:watch)
  • 八、条件渲染与列表渲染
    • [8.1 条件渲染:v-if、v-else、v-show](#8.1 条件渲染:v-if、v-else、v-show)
    • [8.2 列表渲染:v-for](#8.2 列表渲染:v-for)
    • [8.3 v-if 与 v-for 的优先级](#8.3 v-if 与 v-for 的优先级)
  • 总结

前言

如何高效地构建用户界面?

传统的做法是用原生 JS 操作 DOM------获取元素、绑定事件、更新内容。这种方式在小项目中尚可,但一旦项目变大,代码就会变得难以维护。你需要在数据变化时手动更新页面,在用户操作时手动同步数据,繁琐且容易出错。

Vue3 正是为了解决这个问题而生------它提供了一套"响应式"机制,让数据和页面自动同步。你只需要关注数据本身,页面会自动更新。

本文将从零开始搭建 Vue3 项目,理解其核心概念:响应式数据数据绑定

一、Vue 是什么?

1.1 是什么

一句话总结:Vue 是一套数据驱动、简化 DOM 操作的前端渐进式框架,用来高效构建网页界面。

1.2 Vue 的核心思想

Vue 的核心理念是:数据驱动视图

传统方式 Vue 方式
数据变化 → 手动更新 DOM 数据变化 → 自动更新 DOM
用户操作 → 手动同步数据 用户操作 → 自动同步数据
代码分散在多处 代码集中在组件

用一句话概括:你改变数据,Vue 改变页面

二、技术选型概览

在开始之前,先了解一下 Vue3 项目的常见技术选型:

类别 选项 推荐 说明
API 风格 选项式 API / 组合式 API 组合式 API Vue3 新特性,更灵活、更易复用
组件格式 HTML 模板 / 单文件组件 单文件组件 .vue 文件,结构清晰
语言 JavaScript / TypeScript TypeScript 类型安全,开发体验更好
构建工具 Vue CLI / Vite Vite 速度更快,Vue 官方推荐
路由 vue-router vue-router 官方路由方案
状态管理 Vuex / Pinia Pinia Vue3 官方推荐,更简洁
UI 组件库 Element Plus / Ant Design Vue Element Plus Vue3 生态最成熟的组件库

本文将基于以上推荐组合进行讲解。

三、环境准备

3.1 创建项目

Vue3 官方推荐使用 Vite 创建项目。打开终端,执行:

bash 复制代码
npm init vite@latest

按提示输入项目名称、选择框架(Vue)、选择语言(TypeScript):

复制代码
✔ Project name: vue3-demo
✔ Select a framework: › Vue
✔ Select a variant: › TypeScript

进入项目目录并安装依赖:

bash 复制代码
cd vue3-demo
npm install

启动开发服务器:

bash 复制代码
npm run dev

打开浏览器访问 http://localhost:5173,看到 Vue 的欢迎页面,说明项目创建成功。

3.2 配置开发工具

推荐使用 VSCode 作为开发工具,并安装以下插件:

插件名 作用
Vue - Official (原名 Volar) Vue3 语法高亮、类型提示
ESLint 代码规范检查

安装后,打开 .vue 文件就能获得良好的开发体验。

3.3 安装调试工具

浏览器安装 Vue DevTools 扩展,可以实时查看组件状态、路由信息等。

安装后,按 F12 打开开发者工具,就能看到 Vue 标签页。

3.4 配置开发服务器

有时需要修改开发服务器配置,比如端口号、代理等。编辑 vite.config.ts

typescript 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  server: {
    port: 3000,           // 修改端口
    open: true,           // 启动时自动打开浏览器
    proxy: {              // 配置代理(解决跨域)
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true
      }
    }
  }
})

跨域问题 :前端运行在 localhost:3000,后端运行在 localhost:8080,直接请求会产生跨域错误。配置代理后,前端请求 /api/xxx 会被转发到 http://localhost:8080/api/xxx

四、项目结构解析

用 VSCode 打开项目,目录结构如下:

复制代码
vue3-demo/
├── index.html          # 入口 HTML
├── package.json        # 项目配置
├── tsconfig.json       # TypeScript 配置
├── vite.config.ts      # Vite 配置
├── public/             # 静态资源(不参与打包)
└── src/
    ├── main.ts         # 应用入口
    ├── App.vue         # 根组件
    └── ...             # 其他源码

4.1 入口文件:index.html

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Vue3 Demo</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
  • <div id="app"> 是挂载点,Vue 应用会渲染到这里
  • <script> 引入 main.ts,这是应用的入口

4.2 应用入口:main.ts

typescript 复制代码
import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

这段代码做了三件事:

  1. createApp(App) - 创建 Vue 应用实例,参数是根组件
  2. .mount('#app') - 将应用挂载到 index.htmlid="app" 的元素上

类比 Java :这就像 Java 的 main 方法,是整个应用的启动入口。

4.3 根组件:App.vue

vue 复制代码
<script setup lang="ts">
import { ref } from 'vue'

const msg = ref('Hello, Vue3!')
</script>

<template>
  <h1>{{ msg }}</h1>
</template>

<style scoped>
h1 {
  color: #42b983;
}
</style>

这是 Vue 的单文件组件(.vue),由三部分组成:

部分 作用 类比
<script setup> JavaScript/TypeScript 逻辑 Java 类的方法
<template> HTML 模板 JSP/Thymeleaf 模板
<style scoped> CSS 样式(scoped 表示仅作用于当前组件) 组件内部样式

setup 关键字:这是 Vue3 组合式 API 的标志,表示在组件创建时执行此代码块。

五、响应式数据:ref 与 reactive

Vue 的核心魔法是响应式 ------数据变化,页面自动更新。要实现这一点,数据必须用 refreactive 包装。

5.1 为什么需要响应式?

看一个普通变量的例子:

vue 复制代码
<script setup lang="ts">
let msg = 'Hello'

function change() {
  msg = 'World'  // 修改普通变量
}
</script>

<template>
  <h1>{{ msg }}</h1>
  <button @click="change">修改</button>
</template>

点击按钮后,msg 确实变成了 'World',但页面上的 {``{ msg }} 不会更新。

原因:Vue 无法追踪普通变量的变化。

5.2 ref:包装任意类型

ref 包装变量,让它变成响应式:

vue 复制代码
<script setup lang="ts">
import { ref } from 'vue'

const msg = ref('Hello')

function change() {
  msg.value = 'World'  // 注意:需要 .value
}
</script>

<template>
  <h1>{{ msg }}</h1>  <!-- 模板中不需要 .value -->
  <button @click="change">修改</button>
</template>

这次点击按钮,页面会自动更新。

关键规则

  • <script> 中访问/修改:msg.value
  • <template> 中使用:直接用 msg(Vue 自动解包)

类比 Javaref 类似 Java 的包装类(如 AtomicReference),内部封装了值和变更通知机制。

5.3 reactive:包装对象类型

reactive 用于包装对象,让它变成响应式:

vue 复制代码
<script setup lang="ts">
import { reactive } from 'vue'

const user = reactive({
  name: '张三',
  age: 18
})

function grow() {
  user.age++  // 不需要 .value
}
</script>

<template>
  <p>姓名:{{ user.name }}</p>
  <p>年龄:{{ user.age }}</p>
  <button @click="grow">长大一岁</button>
</template>

5.4 ref vs reactive:如何选择?

特性 ref reactive
适用类型 任意类型(基本类型、对象) 仅对象类型
访问方式 需要 .value 直接访问属性
重新赋值 可以整体替换 不能整体替换(会丢失响应式)
推荐场景 基本类型、需要重新赋值的场景 复杂对象、表单数据

推荐做法

  • 基本类型:用 ref
  • 对象类型:两者皆可,如果对象结构固定用 reactive,如果需要整体替换用 ref
vue 复制代码
<script setup lang="ts">
import { ref, reactive } from 'vue'

// 推荐:基本类型用 ref
const count = ref(0)
const name = ref('张三')

// 推荐:复杂对象、结构固定用 reactive
const form = reactive({
  username: '',
  password: ''
})

// 也行:对象用 ref(需要整体替换时推荐)
const user = ref({ name: '张三', age: 18 })
user.value = { name: '李四', age: 20 }  // 整体替换 OK
</script>

六、数据绑定

响应式数据需要"绑定"到模板中才能展示和交互。Vue 提供了多种绑定方式。

6.1 文本绑定:双花括号

vue 复制代码
<template>
  <h1>{{ msg }}</h1>
  <p>计算结果:{{ 1 + 2 }}</p>
  <p>当前时间:{{ new Date().toLocaleString() }}</p>
</template>

{``{ }} 中可以写任何 JavaScript 表达式。

类比 JSP :类似于 <%= msg %> 或 EL 表达式 ${msg}

6.2 属性绑定:v-bind(简写 :

将数据绑定到 HTML 属性:

vue 复制代码
<script setup lang="ts">
import { ref } from 'vue'
const url = ref('https://vuejs.org')
const imgPath = ref('/images/logo.png')
</script>

<template>
  <!-- 完整写法 -->
  <a v-bind:href="url">Vue 官网</a>
  
  <!-- 简写(推荐) -->
  <a :href="url">Vue 官网</a>
  <img :src="imgPath" alt="Logo">
</template>
写法 说明
v-bind:属性名="表达式" 完整写法
:属性名="表达式" 简写(推荐)

6.3 事件绑定:v-on(简写 @

绑定事件处理函数:

vue 复制代码
<script setup lang="ts">
import { ref } from 'vue'

const count = ref(0)

function increment() {
  count.value++
}

function decrement() {
  count.value--
}
</script>

<template>
  <button v-on:click="decrement">-</button>
  <span>{{ count }}</span>
  <button @click="increment">+</button>  <!-- 简写(推荐) -->
</template>
写法 说明
v-on:事件名="函数名" 完整写法
@事件名="函数名" 简写(推荐)

传递参数和事件对象

vue 复制代码
<script setup lang="ts">
function handleClick(msg: string, event: MouseEvent) {
  console.log(msg, event.target)
}
</script>

<template>
  <!-- 传递参数 -->
  <button @click="handleClick('删除', $event)">删除</button>
  
  <!-- 简写:直接访问事件对象 -->
  <button @click="(e) => console.log(e.target)">点击</button>
</template>

6.4 表单绑定:v-model(双向绑定)

表单元素(input、select、checkbox 等)需要双向绑定:数据变化更新视图,视图变化更新数据。

vue 复制代码
<script setup lang="ts">
import { ref } from 'vue'

const user = ref({
  name: '',
  age: 18,
  sex: '男',
  hobbies: []
})
</script>

<template>
  <div>
    <!-- 文本框 -->
    <label>姓名:</label>
    <input type="text" v-model="user.name">
  </div>
  
  <div>
    <!-- 数字框 -->
    <label>年龄:</label>
    <input type="number" v-model="user.age">
  </div>
  
  <div>
    <!-- 单选框 -->
    <label>性别:</label>
    <input type="radio" value="男" v-model="user.sex"> 男
    <input type="radio" value="女" v-model="user.sex"> 女
  </div>
  
  <div>
    <!-- 复选框(绑定数组) -->
    <label>爱好:</label>
    <input type="checkbox" value="游泳" v-model="user.hobbies"> 游泳
    <input type="checkbox" value="打球" v-model="user.hobbies"> 打球
    <input type="checkbox" value="阅读" v-model="user.hobbies"> 阅读
  </div>
  
  <div>
    <!-- 下拉框 -->
    <label>城市:</label>
    <select v-model="user.city">
      <option value="">请选择</option>
      <option value="北京">北京</option>
      <option value="上海">上海</option>
    </select>
  </div>
  
  <pre>{{ user }}</pre>
</template>

类比 Java:类似于 JavaBean 的属性绑定,但自动双向同步。

表单类型 v-model 绑定类型 说明
text、textarea string 文本内容
number number 数值
radio string 单选值
checkbox boolean / string\[\] 单个用 boolean,多个用数组
select string / string\[\] 单选用 string,多选用数组

七、计算属性与监听器

7.1 计算属性:computed

有时需要基于现有数据计算出新值:

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

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

// 计算属性:自动缓存,依赖变化才重新计算
const fullName = computed(() => {
  return lastName.value + firstName.value
})
</script>

<template>
  <p>全名:{{ fullName }}</p>
</template>

计算属性 vs 方法

vue 复制代码
<script setup lang="ts">
// 计算属性:有缓存,依赖不变就不重新计算
const fullName = computed(() => {
  console.log('computed 执行')
  return lastName.value + firstName.value
})

// 方法:每次调用都执行
function getFullName() {
  console.log('方法执行')
  return lastName.value + firstName.value
}
</script>

<template>
  <!-- 计算属性:多次使用只计算一次 -->
  <p>{{ fullName }}</p>
  <p>{{ fullName }}</p>
  
  <!-- 方法:每次都执行 -->
  <p>{{ getFullName() }}</p>
  <p>{{ getFullName() }}</p>
</template>
特性 计算属性 方法
缓存 ✅ 有(依赖不变不重新计算) ❌ 无(每次调用都执行)
使用方式 {``{ fullName }} {``{ getFullName() }}
适用场景 需要复用计算结果 每次都需要新结果

类比 Java:计算属性类似于带缓存的 getter 方法。

7.2 监听器:watch

监听数据变化,执行副作用(如保存到 localStorage、发送请求):

vue 复制代码
<script setup lang="ts">
import { ref, watch } from 'vue'

const name = ref(localStorage.getItem('name') || '')

// 监听 name 变化,自动保存到 localStorage
watch(name, (newValue) => {
  localStorage.setItem('name', newValue)
})
</script>

<template>
  <input type="text" v-model="name" placeholder="输入姓名">
</template>

监听对象属性

vue 复制代码
<script setup lang="ts">
import { reactive, watch } from 'vue'

const user = reactive({
  name: '张三',
  age: 18
})

// 监听对象的某个属性
watch(
  () => user.age,  // getter 函数
  (newValue, oldValue) => {
    console.log(`年龄从 ${oldValue} 变为 ${newValue}`)
  }
)
</script>

监听多个数据

vue 复制代码
<script setup lang="ts">
import { ref, watch } from 'vue'

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

// 监听多个数据
watch([firstName, lastName], ([newFirst, newLast], [oldFirst, oldLast]) => {
  console.log(`姓名变化:${oldLast}${oldFirst} -> ${newLast}${newFirst}`)
})
</script>

八、条件渲染与列表渲染

8.1 条件渲染:v-if、v-else、v-show

根据条件决定是否渲染元素:

vue 复制代码
<script setup lang="ts">
import { ref } from 'vue'
const isLoggedIn = ref(false)
const role = ref('admin')
</script>

<template>
  <!-- v-if:条件为真才渲染(会销毁和重建 DOM) -->
  <div v-if="isLoggedIn">欢迎回来!</div>
  <div v-else>请先登录</div>
  
  <!-- v-else-if:多条件 -->
  <div v-if="role === 'admin'">管理员页面</div>
  <div v-else-if="role === 'user'">用户页面</div>
  <div v-else>游客页面</div>
  
  <!-- v-show:始终渲染,只是切换 display 样式 -->
  <div v-show="isLoggedIn">这个元素始终存在于 DOM 中</div>
</template>
指令 特点 适用场景
v-if 真正销毁和重建 DOM 条件很少改变
v-show 只切换 CSS display 频繁切换

类比 Javav-if 类似于条件语句,v-show 类似于控制可见性的布尔标志。

8.2 列表渲染:v-for

遍历数组或对象:

vue 复制代码
<script setup lang="ts">
import { ref } from 'vue'

const students = ref([
  { id: 1, name: '张三', age: 18 },
  { id: 2, name: '李四', age: 19 },
  { id: 3, name: '王五', age: 20 }
])

const user = ref({
  name: '张三',
  age: 18,
  sex: '男'
})
</script>

<template>
  <!-- 遍历数组 -->
  <ul>
    <li v-for="student in students" :key="student.id">
      {{ student.name }} - {{ student.age }}岁
    </li>
  </ul>
  
  <!-- 遍历对象 -->
  <ul>
    <li v-for="(value, key) in user" :key="key">
      {{ key }}: {{ value }}
    </li>
  </ul>
</template>

:key 的重要性

vue 复制代码
<!-- ❌ 不推荐:使用索引作为 key -->
<li v-for="(student, index) in students" :key="index">

<!-- ✅ 推荐:使用唯一标识符作为 key -->
<li v-for="student in students" :key="student.id">

类比 Java:key 类似于 Map 的 key,用于标识列表项的唯一性。Vue 通过 key 来判断哪些元素发生了变化,从而高效更新 DOM。

8.3 v-if 与 v-for 的优先级

重要规则:不要把 v-if 和 v-for 放在同一个元素上

vue 复制代码
<!-- ❌ 不推荐:v-if 和 v-for 同级 -->
<li v-for="student in students" v-if="student.age > 18" :key="student.id">

<!-- ✅ 推荐:使用 template 包裹 -->
<template v-for="student in students" :key="student.id">
  <li v-if="student.age > 18">{{ student.name }}</li>
</template>

<!-- ✅ 推荐:使用计算属性过滤 -->
<script setup lang="ts">
import { computed } from 'vue'

const adultStudents = computed(() => {
  return students.value.filter(s => s.age > 18)
})
</script>

<li v-for="student in adultStudents" :key="student.id">

总结

本文从零开始搭建了 Vue3 项目,并深入讲解了其核心概念:

概念 作用 关键点
响应式数据 数据变化自动更新视图 ref 适用于任意类型,reactive 适用于对象
数据绑定 数据与视图同步 {``{ }} 文本、: 属性、@ 事件、v-model 表单
计算属性 缓存计算结果 依赖变化才重新计算
监听器 数据变化时执行副作用 如保存到 localStorage
条件渲染 按条件显示元素 v-if 真正销毁,v-show 只是隐藏
列表渲染 遍历显示列表 必须提供唯一的 :key

下一步:在掌握了数据绑定后,我们需要学习如何与后端 API 交互。下一篇文章将讲解 axios 和请求封装。


作者:小张 | 转载请注明出处

相关推荐
咩咩啃树皮1 小时前
第43篇:Vue3计算属性(computed)完全精讲——缓存机制、依赖计算、业务最优解
前端·vue.js·缓存
用户938515635072 小时前
从零搭建 AI 日记助手:用 Milvus 向量数据库 + RAG 让机器读懂你的每一天
javascript·人工智能·全栈
徐小夕3 小时前
花了一周,3亿tokens,我开源了一款 Word 文档智能审查平台,文稿自动质检+可视化分析,告别低效人工审核
前端·算法·github
a1117764 小时前
坦克大战3D Three.js 3D (开源项目)
开发语言·javascript·3d
kyriewen4 小时前
别再乱用useEffect了——你写的10个里有8个不该存在
前端·javascript·react.js
Ivanqhz5 小时前
Rust &‘static str浅析
java·前端·javascript·rust
IT_陈寒5 小时前
SpringBoot这个分页坑,我踩了三天才爬出来
前端·人工智能·后端
颜酱5 小时前
05 | 召回前置准备:根据业务数据库生成各数据库(读取配置阶段)
前端·人工智能·后端
zandy10116 小时前
衡石 Agentic BI的ReAct 推理框架在 Agentic BI 中的工程化实践
前端·javascript·react.js