文章目录
- 前言
- 一、Vue3到底香在哪?为什么现在都用它?
- 1. 性能直接拉满,加载速度更快
- 2. 源码升级,对大型项目更友好
- 3. 对TypeScript“原生拥抱”
- 4. 新增了不少实用特性
- 二、手把手上手:用Vite创建第一个Vue3项目
- 1. 准备环境
- 2. 用Vite创建项目
- 3. 写个最简单的效果
- 三、Vue3核心语法:从API到响应式,一次吃透
- 1. 先搞懂:Options API vs Composition API
- 2. 组合式API的入口:setup与script setup语法糖
- 3. 响应式核心:ref和reactive怎么选?
- ① ref:啥都能装,简单数据首选
- ② reactive:对象/数组专用,复杂数据首选
- ③ 小技巧:toRefs解决解构问题
- ④ 怎么选?给你个懒人建议
- 4. 数据衍生与监听:computed、watch和watchEffect
- ① computed:计算属性,和Vue2一样好用
- ② watch:监听指定数据的变化
- ③ watchEffect:自动收集依赖,不用指定监听源
- 5. 生命周期与组件通信
- ① Vue3的生命周期
- ② 组件通信:props、emit和ref
- 6. 逻辑复用神器:自定义Hook
前言
咱做前端的,没人绕得开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-cli和Vite,咱直接推荐Vite——比vue-cli快了不止一星半点,现在几乎是Vue项目的标配了。
1. 准备环境
先确保你电脑上装了Node.js(推荐16+版本),打开终端就能用npm命令了。
2. 用Vite创建项目
终端里直接敲这行命令:
npmcreate vite@latest my-vue3-project ----templatevue-tsmy-vue3-project是你的项目名,随便改;vue-ts代表用Vue3+TypeScript模板,不想用TS的话改成vue就行。
跟着提示走,进入项目文件夹、安装依赖、启动项目:
cdmy-vue3-projectnpminstallnpmrun 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变量,模板里直接用; - 内置了
defineProps、defineEmits这些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:ref和reactive,很多新手刚上手分不清,其实很简单:
① 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.tsimport{ref}from'vue'exportfunctionuseCounter(initialValue=0){constcount=ref(initialValue)constadd=()=>count.value++constsubtract=()=>count.value--constreset=()=>count.value=initialValuereturn{count,add,subtract,reset}}在组件里直接用:
<script setup lang="ts"> import { useCounter } from './hooks/useCounter' // 直接拿到所有逻辑,不用在组件里写重复代码 const { count, add, subtract, reset } = useCounter(10) </script>这样组件里的代码就清爽多了,逻辑复用也超方便,再也不用写一堆重复的代码了。