目录
- 前言
- [一、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 扩展,可以实时查看组件状态、路由信息等。
- Chrome 扩展:Vue.js 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')
这段代码做了三件事:
createApp(App)- 创建 Vue 应用实例,参数是根组件.mount('#app')- 将应用挂载到index.html中id="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 的核心魔法是响应式 ------数据变化,页面自动更新。要实现这一点,数据必须用 ref 或 reactive 包装。
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 自动解包)
类比 Java :
ref类似 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 | 频繁切换 |
类比 Java :
v-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 和请求封装。
作者:小张 | 转载请注明出处