前言

咱做前端的,没人绕得开Vue。从Vue2一路用到Vue3,我刚上手时也被一堆新API搞晕过——setup、ref、reactive到底怎么用?Composition API比Options API好在哪?今天就把我踩过坑、捋顺了的Vue3入门干货,一次性分享给你,看完就能上手写项目!


一、Vue3到底香在哪?为什么现在都用它?

Vue3不是简单的“加了几个新功能”,而是一次从内到外的全面重构,解决了不少Vue2的痛点,也更适配现在的大型项目和TypeScript生态。

1. 性能直接拉满,加载速度更快

Vue2的响应式基于Object.defineProperty,不仅对数组的监听有局限,编译时也没法做太多优化。Vue3直接换了底层:

  • Proxy重写了响应式系统,能监听对象的增删改,数组的变化也能完美捕捉;
  • 编译时做了静态提升、PatchFlag标记,渲染时只更新真正变化的部分,复杂场景下性能比Vue2快了一倍不止,打包体积还小了不少。

2. 源码升级,对大型项目更友好

Vue2的早期源码没怎么考虑模块化,后期维护起来很麻烦。Vue3用TypeScript重写了整个源码,把功能拆成了独立的模块(比如响应式、虚拟DOM、编译器),不仅维护更方便,也让我们写项目时能用上完整的类型推断,减少类型错误。

3. 对TypeScript“原生拥抱”

Vue2里用TS总感觉“隔了一层”,很多类型推断都不好用。Vue3从底层就支持TS,不管是组件定义还是API调用,都能享受到完整的类型提示,写大型项目时再也不用靠猜变量类型了。

4. 新增了不少实用特性

除了大家常说的Composition API,Vue3还加了很多好用的功能:比如跨层级渲染的Teleport、处理异步组件的Suspense、更灵活的Fragment(不用再写根节点了),还有全局API改成了应用实例API,避免了全局污染。


二、手把手上手:用Vite创建第一个Vue3项目

创建Vue3项目有两种主流方式:vue-cliVite,咱直接推荐Vite——比vue-cli快了不止一星半点,现在几乎是Vue项目的标配了。

1. 准备环境

先确保你电脑上装了Node.js(推荐16+版本),打开终端就能用npm命令了。

2. 用Vite创建项目

终端里直接敲这行命令:

npm create vite@latest my-vue3-project -- --template vue-ts
  • my-vue3-project是你的项目名,随便改;
  • vue-ts代表用Vue3+TypeScript模板,不想用TS的话改成vue就行。

跟着提示走,进入项目文件夹、安装依赖、启动项目:

cd my-vue3-project
npm install
npm run dev

终端会给你一个本地地址,打开浏览器就能看到Vue3的默认页面了,是不是超简单?

3. 写个最简单的效果

打开src/App.vue,把默认内容删掉,写个计数器试试:

<template>
  <div class="app">
    <h1>Vue3入门计数器</h1>
    <p>当前计数:{{ count }}</p>
    <button @click="addCount">点我+1</button>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'

// 定义响应式数据
const count = ref(0)

// 定义方法
const addCount = () => {
  count.value++
}
</script>

<style scoped>
.app {
  text-align: center;
  margin-top: 50px;
}
button {
  padding: 8px 16px;
  font-size: 16px;
  cursor: pointer;
}
</style>

保存一下,浏览器里点按钮,数字会跟着变,你的第一个Vue3组件就跑起来了!


三、Vue3核心语法:从API到响应式,一次吃透

这部分是Vue3的灵魂,咱不用死记硬背API,跟着场景学,搞懂每个东西什么时候用就行。

1. 先搞懂:Options API vs Composition API

你写Vue2时肯定熟悉这种写法:

<script>
export default {
  data() {
    return { count: 0 }
  },
  methods: {
    addCount() {
      this.count++
    }
  },
  computed: {},
  watch: {}
}
</script>

这就是Options API,按“数据、方法、计算属性”这些选项分类写,小项目看着很清晰,但组件大了就会很痛苦——同一个功能的代码,要散在data、methods、watch里,找起来麻烦,逻辑复用还要靠mixin,很容易出问题。

Vue3主推的Composition API,就是为了解决这个问题:它不按选项拆分,而是按“业务逻辑”聚合代码,同一个功能的所有数据、方法、监听都写在一起,复用起来也方便。比如上面的计数器,用Composition API写就是:

<script setup lang="ts">
import { ref } from 'vue'

// 计数器逻辑:数据+方法都写在一起
const count = ref(0)
const addCount = () => {
  count.value++
}
</script>

逻辑一目了然,后续要加个“重置计数”的功能,直接在这堆代码里加就行,不用跑到别的选项里找。

2. 组合式API的入口:setup与script setup语法糖

setup是Composition API的“舞台”,所有响应式API都要在setup里用。Vue3.2之后出了<script setup>语法糖,现在几乎所有项目都用它,比原生setup简洁太多:

  • 不用写export default,不用手动return变量,模板里直接用;
  • 内置了definePropsdefineEmits这些API,不用额外导入;
  • 天生支持TypeScript,类型提示更友好。

比如父子组件传值,用script setup写超简单:

<!-- 子组件 Child.vue -->
<template>
  <p>收到父组件的消息:{{ parentMsg }}</p>
  <button @click="sendMsg">给父组件发消息</button>
</template>

<script setup lang="ts">
// 接收父组件传的props
const props = defineProps<{
  parentMsg: string
}>()

// 定义自定义事件,给父组件传值
const emit = defineEmits(['sendMsg'])

const sendMsg = () => {
  emit('sendMsg', '我收到消息啦!')
}
</script>

3. 响应式核心:ref和reactive怎么选?

写Vue绕不开响应式,Vue3给了两个核心API:refreactive,很多新手刚上手分不清,其实很简单:

① ref:啥都能装,简单数据首选

ref可以处理所有类型的数据:基本类型(数字、字符串、布尔)和对象/数组都能装,返回一个响应式对象,修改时要加.value,模板里会自动解包,不用写.value

<script setup lang="ts">
import { ref } from 'vue'

// 基本类型
const count = ref(0)
// 对象类型也能装
const user = ref({ name: '张三', age: 18 })

const update = () => {
  count.value++ // JS里要写.value
  user.value.name = '李四' // 修改对象也要.value
}
</script>

<template>
  <p>{{ count }}</p> <!-- 模板里不用.value -->
  <p>{{ user.name }}</p>
</template>

② reactive:对象/数组专用,复杂数据首选

reactive只能处理对象、数组这类引用类型,不用写.value,直接修改属性就行,天生就是深度响应式,嵌套的对象属性修改也能触发更新。

<script setup lang="ts">
import { reactive } from 'vue'

// 多层嵌套的表单对象
const form = reactive({
  username: '',
  password: '',
  address: {
    city: '北京'
  }
})

// 直接修改属性,不用.value
const updateCity = () => {
  form.address.city = '上海'
}
</script>

⚠️ 注意:reactive不能直接整体赋值,比如form = reactive({})会丢失响应式,要用Object.assign(form, 新对象)来更新。

③ 小技巧:toRefs解决解构问题

如果想解构reactive对象,直接解构会丢失响应式,这时候用toRefs

<script setup lang="ts">
import { reactive, toRefs } from 'vue'

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

// 解构后还是响应式的
const { name, age } = toRefs(user)

// 修改时还是要.value
name.value = '李四'
</script>

④ 怎么选?给你个懒人建议

  • 单个简单数据(比如计数器、开关状态):直接用ref;
  • 表单、配置、多层嵌套的对象/数组:直接用reactive;
  • 纠结的时候,用ref也不会错,ref装对象内部也会自动调用reactive。

4. 数据衍生与监听:computed、watch和watchEffect

① computed:计算属性,和Vue2一样好用

和Vue2的computed功能差不多,用来根据其他响应式数据衍生新数据,自带缓存,依赖变了才会重新计算:

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

const count = ref(0)
// 计算属性:count的两倍
const doubleCount = computed(() => count.value * 2)
</script>

<template>
  <p>count: {{ count }}</p>
  <p>count的两倍:{{ doubleCount }}</p>
</template>

② watch:监听指定数据的变化

想监听某个响应式数据的变化,用watch,比如监听用户输入:

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

const keyword = ref('')

// 监听keyword变化,做搜索
watch(keyword, (newVal, oldVal) => {
  console.log('用户输入了:', newVal)
  // 这里可以调用搜索接口
}, { immediate: true }) // immediate表示组件挂载时就执行一次
</script>

如果要监听reactive对象里的某个属性,或者多个数据,也可以传数组,用法很灵活。

③ watchEffect:自动收集依赖,不用指定监听源

watchEffect不用手动指定要监听谁,它会自动收集你用到的响应式数据,数据变了就重新执行函数,适合做副作用,比如更新文档标题:

<script setup lang="ts">
import { ref, watchEffect } from 'vue'

const count = ref(0)

// 自动收集count作为依赖,count变了就执行
watchEffect(() => {
  document.title = `当前计数:${count.value}`
})
</script>

5. 生命周期与组件通信

① Vue3的生命周期

Vue3的生命周期和Vue2差不多,只是在setup里要用函数式的写法,比如:

<script setup lang="ts">
import { onMounted, onUpdated, onUnmounted } from 'vue'

// 组件挂载后执行,相当于Vue2的mounted
onMounted(() => {
  console.log('组件挂载了,发请求拿数据')
})

// 组件更新后执行,相当于updated
onUpdated(() => {
  console.log('组件更新了')
})

// 组件卸载前执行,相当于beforeDestroy
onUnmounted(() => {
  console.log('组件卸载了,清理定时器')
})
</script>

② 组件通信:props、emit和ref

  • 父传子:用defineProps,刚才已经写过例子了;
  • 子传父:用defineEmits触发事件;
  • 父组件拿子组件实例:用ref+defineExpose
<!-- 子组件 Child.vue -->
<script setup lang="ts">
const msg = '我是子组件的数据'

// 暴露给父组件的属性/方法
defineExpose({ msg })
</script>

<!-- 父组件 App.vue -->
<template>
  <Child ref="childRef" />
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import Child from './Child.vue'

const childRef = ref()

onMounted(() => {
  // 挂载后就能拿到子组件暴露的数据了
  console.log(childRef.value.msg) // 输出:我是子组件的数据
})
</script>

6. 逻辑复用神器:自定义Hook

Vue3最香的一点,就是可以把重复的逻辑抽成自定义Hook,比如多个组件都要用的“本地存储”“计数器”逻辑,抽成Hook后直接用就行,不用每个组件都写一遍。

比如写个简单的计数器Hook:

// hooks/useCounter.ts
import { ref } from 'vue'

export function useCounter(initialValue = 0) {
  const count = ref(initialValue)

  const add = () => count.value++
  const subtract = () => count.value--
  const reset = () => count.value = initialValue

  return { count, add, subtract, reset }
}

在组件里直接用:

<script setup lang="ts">
import { useCounter } from './hooks/useCounter'

// 直接拿到所有逻辑,不用在组件里写重复代码
const { count, add, subtract, reset } = useCounter(10)
</script>

这样组件里的代码就清爽多了,逻辑复用也超方便,再也不用写一堆重复的代码了。

更多推荐