Vue3 入门详解:从基础到实战

Vue3 作为 Vue 框架的重大升级版本,于2020年正式发布,相比 Vue2 带来了性能大幅提升组合式API(Composition API)更好的TypeScript支持Tree-Shaking优化等核心特性,彻底解决了 Vue2 中大型项目代码复用困难、选项式API(Options API)逻辑分散的痛点,同时保留了Vue的易用性,成为当前前端开发的主流框架之一。本文将从环境搭建、核心概念、实战案例到项目规范,全方位讲解Vue3基础,帮助新手快速入门并落地开发。

一、Vue3 核心优势(为什么选择Vue3?)

在开始学习前,先明确Vue3的核心升级点,理解其设计初衷,能让后续学习更有方向:

  1. 性能优化:重写了虚拟DOM,渲染速度提升约50%;支持Tree-Shaking,打包体积更小(按需引入API,未使用的代码会被剔除);
  2. 组合式API(Composition API) :替代Vue2的选项式API,将分散在datamethodswatch中的同一业务逻辑聚合在一起,更易维护和复用,尤其适合大型项目;
  3. 更好的TS支持:Vue3源码由TypeScript编写,天生对TypeScript友好,类型推导更完善,开发时能获得更优的代码提示;
  4. 新特性加持 :新增Teleport(组件瞬移)、Suspense(异步组件加载)、v-model重构(支持多值绑定)、全局API重构(更易树摇)等;
  5. 向下兼容:大部分Vue2的语法在Vue3中仍可使用,支持渐进式迁移,老项目可逐步升级。

二、环境搭建:Vue3 项目创建与运行

Vue3 官方推荐使用 Vite 作为构建工具(替代Vue2的Vue-CLI),Vite基于ESModule实现按需编译,启动速度、热更新速度远超Webpack,是当前Vue3项目的标配。以下是完整的环境搭建步骤,包含前置依赖和项目创建。

2.1 前置依赖:Node.js 安装

Vue3 项目依赖Node.js(提供npm包管理工具),要求Node.js版本 ≥ 14.18.0 或 ≥ 16.0.0

  • 下载地址:Node.js 官方网站(选择LTS长期支持版,兼容性更好)

  • 验证安装:安装完成后,打开终端/命令行,输入以下命令,显示版本号即安装成功:

    bash 复制代码
    node -v  # 查看node版本
    npm -v   # 查看npm版本

2.2 创建Vue3 项目(Vite 方式)

使用Vite的官方创建命令,一步生成Vue3项目,支持选择Vue2/Vue3、TypeScript/JavaScript,操作简单:

  1. 执行创建命令(项目名可自定义,建议小写+横杠,如vue3-demo):

    bash 复制代码
    npm create vite@latest 你的项目名 -- --template vue
    • 如需创建Vue3 + TypeScript 项目,将模板改为vue-ts

      bash 复制代码
      npm create vite@latest 你的项目名 -- --template vue-ts
  2. 进入项目目录,安装依赖:

    bash 复制代码
    cd 你的项目名  # 进入项目根目录
    npm install    # 安装项目依赖(生成node_modules文件夹)
  3. 启动开发服务器:

    bash 复制代码
    npm run dev
  4. 访问项目:终端会显示本地访问地址(通常是http://127.0.0.1:5173/),打开浏览器访问即可看到Vue3默认页面。

2.3 项目目录结构解析(核心目录说明)

创建完成后,项目核心目录结构如下(剔除了无关配置文件,聚焦开发核心):

复制代码
vue3-demo/
├── node_modules/  # 项目依赖包(npm install生成)
├── public/        # 静态资源目录(不会被Vite编译,如图片、favicon)
├── src/           # 开发核心目录(所有业务代码都写在这里)
│   ├── assets/    # 静态资源(会被Vite编译,如css、图片、字体)
│   ├── components/ # 公共组件目录(可复用的组件,如按钮、卡片)
│   ├── App.vue    # 根组件(所有页面/组件的入口)
│   └── main.js    # 项目入口文件(创建Vue实例,挂载根组件)
├── package.json   # 项目配置文件(依赖、脚本命令如npm run dev)
└── index.html     # Vite入口html(唯一的html文件,单页应用核心)

核心文件说明

  • main.js:Vue3的入口文件,负责创建应用实例、挂载根组件,是项目的启动入口;
  • App.vue:根组件,所有自定义组件、页面都会被引入到这个组件中,相当于项目的"根容器";
  • package.json:管理项目依赖和脚本命令,比如npm run dev对应启动开发服务器,npm run build对应打包生产环境代码。

三、Vue3 核心基础:从入口到组件

3.1 项目入口文件:main.js 详解

Vue3 重构了全局API,将Vue2的new Vue()改为**createApp**创建应用实例,更符合模块化设计,且支持多实例隔离。以下是Vue3原生main.js的核心代码及解析(无任何第三方插件):

javascript 复制代码
// 1. 从vue中导入createApp方法(Vue3的核心API,创建应用实例)
import { createApp } from 'vue'
// 2. 导入根组件App.vue(项目所有组件的入口)
import App from './App.vue'
// 3. 导入全局样式(可选,如assets/css/global.css)
import './assets/main.css'

// 4. 创建应用实例 + 挂载根组件到DOM节点(#app)
// createApp(App):基于根组件创建Vue应用实例
// .mount('#app'):将应用实例挂载到index.html中id为app的DOM节点上
createApp(App).mount('#app')

关键变化(对比Vue2)

  • Vue2:new Vue({ el: '#app', render: h => h(App) }),通过构造函数创建实例;
  • Vue3:createApp(App).mount('#app'),通过函数式API创建实例,更轻量、无全局污染;
  • 核心优势:createApp支持链式调用,且多个实例相互独立(如const app1 = createApp(App1); const app2 = createApp(App2)),适合在一个页面中嵌入多个Vue应用。

3.2 Vue 单文件组件(SFC):核心组成

Vue的核心特性之一是单文件组件(Single File Component,简称SFC) ,即.vue文件,一个文件包含**模板(template)、脚本(script)、样式(style)**三部分,实现了组件的"代码封装",让结构、逻辑、样式高度内聚。

所有.vue组件都遵循统一的核心结构,缺一不可(样式可选),基础模板如下:

vue 复制代码
<template>
  <!-- 模板部分:组件的HTML结构,负责页面渲染 -->
  <!-- 支持Vue的指令(v-bind、v-for、v-if等),根节点可多个(Vue3新增,Vue2要求单根节点) -->
  <div class="component-container">
    <h1>{{ msg }}</h1>
    <button @click="handleClick">点击按钮</button>
  </div>
</template>

<script setup>
  <!-- 脚本部分:组件的逻辑代码(数据、方法、生命周期等) -->
  <!-- setup是Vue3的语法糖,组合式API的核心,后续详细讲解 -->
  // 响应式数据
  const msg = 'Hello Vue3!'
  // 事件方法
  const handleClick = () => {
    alert('你点击了按钮!')
  }
</script>

<style scoped>
  <!-- 样式部分:组件的CSS样式 -->
  <!-- scoped属性:样式私有化,仅作用于当前组件(避免样式污染) -->
  .component-container {
    padding: 20px;
    background: #f5f5f5;
  }
  h1 {
    color: #42b983; /* Vue官方主色 */
  }
</style>

三部分详细说明

  1. template 模板

    • 负责组件的DOM结构渲染,支持所有Vue内置指令(v-bindv-onv-forv-if等);
    • Vue3 取消了"单根节点限制",模板中可以直接写多个同级根节点(如<h1></h1><p></p>),无需用<div>包裹,解决了Vue2的嵌套冗余问题;
    • 插值表达式{``{ 变量名 }}:用于渲染脚本部分的响应式数据,支持简单的表达式(如{``{ num + 1 }}{``{ msg.toUpperCase() }})。
  2. script 脚本

    • 负责组件的核心逻辑:定义数据、方法、生命周期钩子、引入依赖、注册组件等;
    • setup 是Vue3的语法糖 (后续重点讲解),是组合式API的入口,写在<script setup>中的代码会在组件创建时自动执行,无需额外配置;
    • 脚本部分的变量、方法,可直接在模板中使用(如上述的msghandleClick),无需像Vue2那样通过this调用。
  3. style 样式

    • 负责组件的样式定义,支持原生CSS、SCSS/LESS(需安装对应依赖,如npm install sass);
    • scoped 属性:样式私有化核心,添加后,当前组件的样式仅作用于自身,不会影响父组件、子组件或其他组件,解决了大型项目的样式污染问题;
    • 如需让样式作用于子组件,可使用Vue的深度选择器::v-deep(如::v-deep .child-class { color: red; })。

四、Vue3 核心语法:组合式API(Composition API)

组合式API是Vue3的核心灵魂 ,也是与Vue2选项式API最核心的区别。它解决了Vue2中"同一业务逻辑的代码分散在datamethodswatchcomputed中"的问题,让相关逻辑聚合在一起,更易维护和复用。

4.1 为什么放弃Vue2的选项式API?

Vue2的选项式API(Options API)采用"选项分离"的方式组织代码,比如:

javascript 复制代码
// Vue2 选项式API
export default {
  data() {
    return { count: 0 } // 数据写在data
  },
  methods: {
    addCount() { this.count++ } // 方法写在methods
  },
  watch: {
    count(newVal) { console.log('count变化了:', newVal) } // 监听写在watch
  }
}

这种方式在小型项目中很易用,但在大型项目中,一个业务逻辑可能涉及数据、方法、监听、计算属性等,需要在多个选项之间来回切换,代码可读性和可维护性大幅下降,且代码复用只能通过mixins(混入),存在命名冲突、逻辑来源不清晰的问题。

而Vue3的组合式API,将同一业务逻辑的所有代码聚合在一起,即使项目再大,也能快速定位逻辑,且支持组合式函数实现代码复用,无命名冲突问题。

4.2 组合式API 入口:setup 语法糖

<script setup> 是Vue3推荐的组合式API写法,也是最简洁、最高效 的写法(替代了原始的setup函数),具有以下核心特性:

  1. 自动执行 :写在<script setup>中的代码,会在组件创建之前beforeCreate生命周期前)自动执行,无需手动调用;
  2. 无需导出 :脚本中的变量、方法、组件等,无需通过export default导出,可直接在模板中使用
  3. 简化注册 :引入的组件无需手动注册(如import Button from './components/Button.vue',直接在模板中写<Button/>即可);
  4. 天生支持TS:与TypeScript无缝集成,类型推导更完善,开发体验更好。

注意<script setup> 是Vue3.2及以上版本的特性,当前Vite创建的Vue3项目默认都是3.2+,可放心使用。

4.3 响应式数据:ref 和 reactive(核心)

Vue的核心特性是数据驱动视图 :当数据发生变化时,视图会自动更新。而实现这一特性的基础,是响应式数据 。Vue3 提供了两个核心API创建响应式数据:refreactive,适用于不同场景。

4.3.1 ref:用于基本类型数据(字符串、数字、布尔值)

ref 是Vue3中最常用的响应式API,专门用于包装基本类型数据,使其成为响应式数据,同时也支持包装对象类型数据(推荐用于基本类型)。

使用步骤

  1. vue中导入ref
  2. 使用ref(初始值)创建响应式数据;
  3. 模板中直接使用变量名,脚本中通过变量名.value访问/修改值。

示例代码

vue 复制代码
<template>
  <div>
    <!-- 模板中直接使用,无需.value -->
    <p>当前计数:{{ count }}</p>
    <button @click="addCount">点击+1</button>
    <p>用户名:{{ name }}</p>
  </div>
</template>

<script setup>
// 1. 导入ref
import { ref } from 'vue'

// 2. 创建响应式数据(基本类型)
const count = ref(0) // 数字
const name = ref('Vue3入门') // 字符串
const isShow = ref(true) // 布尔值

// 3. 脚本中修改响应式数据,需通过.value
const addCount = () => {
  count.value++ // 必须写.value,否则数据不更新,视图也不刷新
  if (count.value >= 5) {
    isShow.value = false
  }
}
</script>
4.3.2 reactive:用于对象/数组类型数据

reactive 专门用于包装对象或数组类型数据 ,使其成为响应式数据,返回一个响应式的代理对象(Proxy),直接操作对象属性即可实现数据响应式,无需.value

使用步骤

  1. vue中导入reactive
  2. 使用reactive({ ... })reactive([ ... ])创建响应式数据;
  3. 模板和脚本中直接访问/修改对象属性/数组元素。

示例代码

vue 复制代码
<template>
  <div>
    <p>姓名:{{ user.name }}</p>
    <p>年龄:{{ user.age }}</p>
    <button @click="updateAge">年龄+1</button>
    <ul>
      <li v-for="(item, index) in list" :key="index">{{ item }}</li>
    </ul>
    <button @click="addItem">添加列表项</button>
  </div>
</template>

<script setup>
// 1. 导入reactive
import { reactive } from 'vue'

// 2. 创建响应式对象
const user = reactive({
  name: '张三',
  age: 20
})

// 2. 创建响应式数组
const list = reactive(['Vue3', 'Vite', 'Composition API'])

// 3. 直接修改属性/元素,无需.value
const updateAge = () => {
  user.age++
}
const addItem = () => {
  list.push('ref & reactive')
}
</script>
4.3.3 ref vs reactive:核心区别与使用场景
特性 ref reactive
适用类型 基本类型(推荐)、对象/数组 对象/数组(仅支持)
访问方式 脚本中需.value,模板中无需 模板/脚本中均直接访问属性/元素
返回值 包含value的响应式对象 响应式代理对象(Proxy)
解构响应性 直接解构会丢失响应性(需toRefs) 直接解构会丢失响应性(需toRefs)

核心使用原则

  • 基本类型数据(string/number/boolean):优先使用ref
  • 对象/数组类型数据:优先使用reactive
  • 简单场景用ref,复杂业务对象用reactive,两者可结合使用(如reactive对象中包含ref数据)。

4.4 计算属性:computed

计算属性用于基于现有响应式数据,生成新的派生数据 ,具有缓存特性:只有当依赖的响应式数据发生变化时,计算属性才会重新计算;如果依赖数据未变化,直接返回缓存的结果,相比普通方法更高效。

使用场景:数据格式化、多数据组合计算、条件判断派生数据等(如:根据性别值显示"男/女"、根据商品数量和单价计算总价)。

使用步骤

  1. vue中导入computed
  2. 调用computed(() => { 计算逻辑,返回结果 })
  3. 模板中直接使用,无需加括号。

示例代码

vue 复制代码
<template>
  <div>
    <p>原始价格:{{ price }} 元</p>
    <p>折扣后价格:{{ discountPrice }} 元</p> <!-- 直接使用 -->
    <p>商品状态:{{ goodsStatus }}</p>
  </div>
</template>

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

// 原始响应式数据
const price = ref(100)
const stock = ref(5) // 库存

// 计算属性:折扣后价格(依赖price)
const discountPrice = computed(() => {
  return (price.value * 0.8).toFixed(2) // 8折,保留2位小数
})

// 计算属性:商品状态(依赖stock)
const goodsStatus = computed(() => {
  return stock.value > 0 ? '有货' : '无货'
})
</script>

4.5 监听器:watch

监听器用于监听响应式数据的变化 ,并在数据变化时执行自定义逻辑(如:数据变化后发起请求、更新其他数据、做日志记录等)。Vue3的watch支持监听单个数据、多个数据、对象属性,功能比Vue2更强大。

4.5.1 监听单个ref数据
vue 复制代码
<template>
  <p>计数:{{ count }}</p>
  <button @click="count++">+1</button>
</template>

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

const count = ref(0)

// 监听单个ref数据:watch(监听的变量, 回调函数(newVal, oldVal))
watch(count, (newVal, oldVal) => {
  console.log(`count从${oldVal}变为${newVal}`)
  // 数据变化后的逻辑,如发起请求
})
</script>
4.5.2 监听多个数据

将多个监听的变量放入数组中,回调函数的参数是一个数组,对应新值和旧值。

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

const name = ref('张三')
const age = ref(20)

// 监听多个数据
watch([name, age], ([newName, newAge], [oldName, oldAge]) => {
  console.log(`姓名:${oldName}→${newName},年龄:${oldAge}→${newAge}`)
})
</script>
4.5.3 监听reactive对象/对象属性
  • 监听整个reactive对象:直接传入对象,回调函数会在对象任意属性变化时触发;
  • 监听reactive对象的单个属性:需传入函数,返回要监听的属性(避免监听整个对象)。
vue 复制代码
<script setup>
import { reactive, watch } from 'vue'

const user = reactive({
  name: '张三',
  age: 20,
  address: {
    city: '北京'
  }
})

// 1. 监听整个reactive对象(任意属性变化都会触发)
watch(user, (newVal, oldVal) => {
  console.log('user对象变化了:', newVal)
})

// 2. 监听reactive对象的单个属性(推荐,性能更好)
watch(() => user.age, (newAge, oldAge) => {
  console.log(`年龄变化:${oldAge}→${newAge}`)
})

// 3. 监听嵌套属性
watch(() => user.address.city, (newCity) => {
  console.log('城市变化:', newCity)
})

// 触发监听
const updateUser = () => {
  user.age = 21 // 触发2、1
  user.address.city = '上海' // 触发3、1
}
</script>

4.6 Vue3 生命周期钩子

生命周期钩子用于在组件的不同阶段执行自定义逻辑 (如:组件创建时初始化数据、挂载后操作DOM、销毁前清除定时器)。Vue3的组合式API中,生命周期钩子以函数形式 提供,需从vue中导入后使用,相比Vue2更贴合模块化设计。

4.6.1 Vue3 生命周期钩子列表(与Vue2对应)

Vue3 保留了Vue2的核心生命周期,仅修改了两个钩子的名称,新增了setup(替代beforeCreatecreated),以下是组合式API中的生命周期钩子与Vue2的对应关系:

Vue3 组合式API钩子 对应Vue2选项式API 执行时机 核心用途
setup beforeCreate/created 组件创建之前 初始化数据、创建响应式数据
onBeforeMount beforeMount 组件挂载到DOM之前 准备挂载相关逻辑
onMounted mounted 组件挂载到DOM之后 操作DOM、发起初始请求
onBeforeUpdate beforeUpdate 数据变化,视图更新之前 准备更新视图的逻辑
onUpdated updated 数据变化,视图更新之后 视图更新后操作DOM
onBeforeUnmount beforeDestroy 组件销毁之前 清除定时器、取消事件监听
onUnmounted destroyed 组件销毁之后 清理遗留资源
onErrorCaptured errorCaptured 捕获子组件的错误时 处理子组件错误,避免程序崩溃
4.6.2 生命周期钩子使用示例

组合式API中,生命周期钩子只能在<script setup>中调用,且会在对应阶段自动执行,示例代码:

vue 复制代码
<template>
  <p>{{ count }}</p>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount, onUpdated } from 'vue'

const count = ref(0)
let timer = null

// 组件挂载后执行:开启定时器
onMounted(() => {
  console.log('组件挂载完成,可操作DOM')
  // 开启定时器,每秒count+1
  timer = setInterval(() => {
    count.value++
  }, 1000)
})

// 视图更新后执行
onUpdated(() => {
  console.log('视图更新完成,当前count:', count.value)
})

// 组件销毁前执行:清除定时器(避免内存泄漏)
onBeforeUnmount(() => {
  console.log('组件即将销毁,清理资源')
  clearInterval(timer) // 清除定时器
})
</script>

核心注意点

  • setup 中无需使用onBeforeCreatecreated,因为setup的执行时机正好覆盖这两个阶段,所有初始化逻辑直接写在setup中即可;
  • 组件销毁前,必须清理定时器、事件监听、网络请求等资源,避免内存泄漏,这是前端开发的重要规范;
  • 生命周期钩子可以多次调用,执行顺序为调用顺序(如多次调用onMounted,会按顺序依次执行)。

五、Vue3 组件通信:父子组件数据传递

组件是Vue开发的核心,实际项目中会将页面拆分为多个组件(如头部组件、导航组件、内容组件),组件之间的数据传递(通信)是必备技能。Vue3中组件通信的核心方式与Vue2一致,但写法因组合式API略有简化,以下讲解最常用的父子组件通信(占实际开发的80%以上)。

5.1 父传子:props 传递数据

核心逻辑 :父组件通过自定义属性 向子组件传递数据,子组件通过defineProps接收并使用,props是单向数据流(子组件不能直接修改父组件传递的props,避免数据混乱)。

步骤1:创建子组件(如src/components/Child.vue

使用defineProps接收父组件传递的数据,defineProps是Vue3的内置宏(无需导入),直接在<script setup>中使用。

vue 复制代码
<!-- Child.vue 子组件 -->
<template>
  <div class="child">
    <h3>子组件</h3>
    <p>父组件传递的名称:{{ name }}</p>
    <p>父组件传递的年龄:{{ age }}</p>
  </div>
</template>

<script setup>
// 接收父组件的props,支持指定类型(基础类型校验)
const props = defineProps({
  name: String, // 名称:字符串类型
  age: Number   // 年龄:数字类型
})

// 脚本中可通过props.xxx访问,模板中直接使用xxx
console.log('父组件传递的name:', props.name)
</script>

<style scoped>
.child {
  padding: 20px;
  border: 1px solid #42b983;
  margin-top: 20px;
}
</style>
步骤2:父组件引入并传递数据(如App.vue

父组件通过自定义属性 (如nameage)将响应式数据传递给子组件,属性值绑定响应式数据(用v-bind:或简写:)。

vue 复制代码
<!-- App.vue 父组件 -->
<template>
  <div class="parent">
    <h3>父组件</h3>
    <button @click="changeAge">修改年龄</button>
    <!-- 引入子组件,通过自定义属性传递数据(v-bind绑定响应式数据) -->
    <Child :name="userName" :age="userAge" />
  </div>
</template>

<script setup>
// 1. 导入子组件(无需手动注册,直接使用)
import Child from './components/Child.vue'
// 2. 定义父组件的响应式数据
import { ref } from 'vue'
const userName = ref('张三')
const userAge = ref(20)

// 3. 修改父组件数据,子组件会自动更新(props单向数据流)
const changeAge = () => {
  userAge.value++
}
</script>

<style scoped>
.parent {
  padding: 20px;
  border: 1px solid #666;
}
</style>

5.2 子传父:emit 触发事件传递数据

核心逻辑 :子组件通过defineEmits定义自定义事件,通过emit触发事件并传递数据;父组件通过自定义事件监听@事件名)接收子组件传递的数据,并在回调函数中处理。

步骤1:子组件定义并触发自定义事件(修改Child.vue
  1. 使用defineEmits定义自定义事件(内置宏,无需导入);
  2. 调用emit方法触发事件,第二个参数为要传递给父组件的数据。
vue 复制代码
<!-- Child.vue 子组件 -->
<template>
  <div class="child">
    <h3>子组件</h3>
    <p>子组件输入:<input v-model="inputVal" /></p>
    <!-- 点击按钮,触发自定义事件,传递数据 -->
    <button @click="sendToParent">将数据传递给父组件</button>
  </div>
</template>

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

// 1. 定义自定义事件(如:send-data)
const emit = defineEmits(['send-data'])
// 子组件的响应式数据
const inputVal = ref('')

// 2. 触发自定义事件,传递数据(emit('事件名', 数据1, 数据2,...))
const sendToParent = () => {
  if (inputVal.value) {
    emit('send-data', inputVal.value) // 触发send-data事件,传递输入值
    inputVal.value = '' // 清空输入框
  }
}
</script>
步骤2:父组件监听自定义事件(修改App.vue

父组件通过@自定义事件名监听子组件的事件,在回调函数中接收子组件传递的数据并处理。

vue 复制代码
<!-- App.vue 父组件 -->
<template>
  <div class="parent">
    <h3>父组件</h3>
    <p>子组件传递的数据:{{ childData }}</p>
    <!-- 监听子组件的send-data事件,回调函数接收数据 -->
    <Child @send-data="handleChildData" />
  </div>
</template>

<script setup>
import Child from './components/Child.vue'
import { ref } from 'vue'

// 接收子组件数据的响应式变量
const childData = ref('')

// 监听子组件事件的回调函数,参数为子组件传递的数据
const handleChildData = (data) => {
  childData.value = data
  console.log('接收到子组件的数据:', data)
}
</script>

父子组件通信核心原则

  • 父传子:props 单向数据流,子组件只读不修改;
  • 子传父:emit 触发事件,父组件监听处理;
  • 如需子组件修改父组件数据,通过"子组件emit事件,父组件在回调中修改自身数据"实现,间接完成数据更新。

六、Vue3 实战小案例:待办事项(TodoList)

为了将以上基础知识点融会贯通,接下来实现一个简易的待办事项(TodoList) 案例,涵盖:响应式数据、事件处理、v-for/v-bind指令、组件通信(可选)、computed计算属性,是Vue3入门的经典实战案例。

6.1 案例需求

  1. 输入待办内容,点击按钮添加到待办列表;
  2. 显示所有待办事项,包含"完成/未完成"状态;
  3. 点击待办事项,切换完成/未完成状态;
  4. 计算并显示"未完成待办数量";
  5. 支持删除单个待办事项。

6.2 完整代码实现(App.vue)

vue 复制代码
<template>
  <div class="todo-list" style="width: 400px; margin: 50px auto;">
    <h2 style="color: #42b983; text-align: center;">Vue3 TodoList</h2>
    <!-- 输入框 + 添加按钮 -->
    <div class="todo-input" style="display: flex; gap: 10px; margin-bottom: 20px;">
      <input
        v-model="inputVal"
        type="text"
        placeholder="请输入待办事项"
        style="flex: 1; padding: 8px; border: 1px solid #eee; border-radius: 4px;"
        @keyup.enter="addTodo" // 回车也能添加
      />
      <button
        @click="addTodo"
        style="padding: 8px 16px; background: #42b983; color: #fff; border: none; border-radius: 4px; cursor: pointer;"
      >
        添加
      </button>
    </div>

    <!-- 待办列表 -->
    <ul style="list-style: none; padding: 0; margin: 0;">
      <li
        v-for="(todo, index) in todoList"
        :key="index"
        style="display: flex; justify-content: space-between; align-items: center; padding: 10px; border: 1px solid #eee; border-radius: 4px; margin-bottom: 10px;"
        :style="{ textDecoration: todo.done ? 'line-through' : 'none', color: todo.done ? '#999' : '#333' }"
      >
        <!-- 点击切换完成状态 -->
        <span @click="toggleTodo(index)">{{ todo.text }}</span>
        <!-- 删除按钮 -->
        <button
          @click="deleteTodo(index)"
          style="color: #f56c6c; background: transparent; border: none; cursor: pointer;"
        >
          删除
        </button>
      </li>
    </ul>

    <!-- 未完成数量(计算属性) -->
    <p style="text-align: center; margin-top: 20px; color: #666;">
      未完成待办:{{ unDoneCount }} / 总数量:{{ todoList.length }}
    </p>
  </div>
</template>

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

// 1. 响应式数据:输入框值
const inputVal = ref('')

// 2. 响应式数据:待办列表(数组+对象,用reactive/ref均可,这里用ref更简洁)
const todoList = ref([
  { text: '学习Vue3组合式API', done: false },
  { text: '实现TodoList案例', done: false }
])

// 3. 计算属性:未完成待办数量
const unDoneCount = computed(() => {
  return todoList.value.filter(todo => !todo.done).length
})

// 4. 方法:添加待办
const addTodo = () => {
  // 去除首尾空格,判空
  const val = inputVal.value.trim()
  if (!val) {
    alert('请输入待办内容!')
    return
  }
  // 添加到列表
  todoList.value.push({ text: val, done: false })
  // 清空输入框
  inputVal.value = ''
}

// 5. 方法:切换待办完成状态
const toggleTodo = (index) => {
  todoList.value[index].done = !todoList.value[index].done
}

// 6. 方法:删除待办
const deleteTodo = (index) => {
  todoList.value.splice(index, 1)
}
</script>

6.3 案例运行效果

  1. 页面显示默认的2条待办事项,输入框可输入新内容,点击"添加"或回车即可新增;
  2. 点击待办事项文字,会切换划线(完成)/无划线(未完成)状态;
  3. 点击"删除"可移除对应待办事项;
  4. 底部实时显示未完成待办数量和总数量,数据自动更新。

该案例涵盖了Vue3入门的所有核心知识点,是检验基础是否掌握的最佳方式,建议手动敲写代码,理解每一行的逻辑。

七、Vue3 开发规范与最佳实践

为了让代码更易维护、团队协作更高效,结合Vue3的特性,整理了以下入门级开发规范,新手严格遵循,能养成良好的编码习惯:

7.1 组件相关规范

  1. 组件名:小写+横杠 (如todo-item.vueuser-card.vue),避免大驼峰或纯大写,符合HTML自定义标签规范;
  2. 组件文件:一个组件对应一个.vue文件,公共组件放在src/components/,页面组件放在src/views/(大型项目);
  3. 组件嵌套:避免超过3层嵌套(如父→子→孙→曾孙),多层嵌套会导致组件通信复杂,可使用Pinia(状态管理)优化;
  4. 样式规范:所有组件样式都添加scoped属性,避免样式污染;如需修改子组件样式,使用::v-deep深度选择器,且尽量少用。

7.2 代码编写规范

  1. 响应式数据:基本类型用ref,对象/数组用reactive,避免混用导致的响应性丢失;
  2. 变量/方法命名:小驼峰 (如userNameaddTodo),语义化命名(避免abfn1等无意义命名);
  3. 模板指令:v-bind简写为:v-on简写为@v-for必须配合:key(优先使用唯一ID,而非索引);
  4. 生命周期:组件销毁前必须清理定时器、事件监听、网络请求等资源,避免内存泄漏;
  5. props规范:子组件接收props时,尽量指定类型校验 (如StringNumber),提高代码健壮性,大型项目可添加默认值和必传校验。

7.3 性能优化规范

  1. 避免频繁修改响应式数据:多次修改响应式数据时,尽量合并操作,减少视图更新次数;
  2. 计算属性替代频繁调用的方法:对于需要重复计算的逻辑,使用computed(利用缓存特性),而非普通方法;
  3. v-forv-if不混用:如需条件筛选列表,先通过computed过滤数据,再用v-for渲染,避免每次渲染都执行条件判断;
  4. 按需引入:使用Vue3的Tree-Shaking特性,只导入需要的API(如import { ref, computed } from 'vue'),而非导入整个Vue。

八、总结与后续学习方向

8.1 本文核心知识点总结

  1. 环境搭建:Vue3推荐使用Vite创建项目,前置依赖Node.js ≥14.18.0,核心命令npm create vite@latest
  2. 核心文件:main.jscreateApp创建应用实例,.vue单文件组件包含template/script/style三部分;
  3. 组合式API:<script setup>是核心入口,响应式数据用ref(基本类型)和reactive(对象/数组);
  4. 核心语法:computed(计算属性,缓存)、watch(监听器,数据变化触发)、生命周期钩子(函数式,如onMounted);
  5. 组件通信:父子组件核心方式为"父传子props,子传父emit",props是单向数据流;
  6. 实战案例:TodoList涵盖了Vue3入门的所有核心知识点,是基础落地的关键。

8.2 后续学习方向(从入门到进阶)

  1. 状态管理 :学习Vue3官方推荐的状态管理工具Pinia(替代Vuex),解决跨组件、跨页面的数据共享问题;
  2. 路由管理 :学习Vue Router 4(Vue3配套路由),实现单页应用的页面跳转、路由传参、路由守卫;
  3. 高级特性 :学习Teleport(组件瞬移)、Suspense(异步组件)、provide/inject(跨层级组件通信)、自定义指令;
  4. TypeScript集成:深入学习Vue3 + TypeScript开发,实现强类型约束,提高大型项目的可维护性;
  5. 工程化优化:学习Vite配置(如代理、环境变量)、ESLint/Prettier代码规范、Vue3性能优化(如虚拟滚动、懒加载);
  6. 实战项目:从简易项目(如博客、商城)入手,逐步开发中型项目,将知识点融会贯通。

Vue3的学习遵循**"渐进式"**原则,无需一次性掌握所有特性,先掌握基础语法和核心概念,再通过实战逐步进阶。本文的内容足够支撑你完成Vue3的入门开发,后续只需在实战中不断积累,就能快速掌握Vue3并应用到实际项目中。

祝大家学习顺利,早日成为Vue3开发高手!

相关推荐
cici158743 小时前
基于MATLAB的四旋翼无人机三维动态仿真实例
开发语言·matlab·无人机
摘星编程3 小时前
用React Native开发OpenHarmony应用:NativeStack原生导航
javascript·react native·react.js
m0_706653233 小时前
高性能网络协议栈
开发语言·c++·算法
一起养小猫3 小时前
Flutter for OpenHarmony 实战:从零开发一款五子棋游戏
android·前端·javascript·flutter·游戏·harmonyos
永远睡不够的入3 小时前
类和对象(上)
开发语言·c++·算法
晚霞的不甘3 小时前
Flutter for OpenHarmony全面升级「今日运势」 应用的视觉与交互革新
前端·学习·flutter·前端框架·交互
想带你从多云到转晴3 小时前
01、JAVAEE--多线程(一)
java·开发语言·javaee
孞㐑¥3 小时前
算法—链表
开发语言·c++·经验分享·笔记·算法
枷锁—sha3 小时前
【CTFshow-pwn系列】06_前置基础【pwn 035】详解:利用 SIGSEGV 信号处理机制
java·开发语言·安全·网络安全·信号处理