Vue3 入门,从项目创建到组合式 API 全解析(含 provide/inject)

Vue3 是 Vue 的新一代版本,核心更新是组合式 API,它让代码组织更灵活、逻辑复用更高效。

一、为什么要学 Vue3?

相比 Vue2,Vue3 的核心优势:

  • 性能更强:打包体积更小、运行速度更快;
  • 组合式 API:替代 Vue2 的选项式 API,更灵活地组织代码逻辑;
  • 更好的 TypeScript 支持:原生兼容 TS,类型提示更友好;
  • 新特性:如Teleport、Suspense等,提升开发效率。

二、Vue3 项目创建:使用 create-vue

Vue3 推荐用官方脚手架create-vue创建项目(替代 Vue2 的 Vue CLI)。

步骤 1:创建项目

打开终端执行:

bash 复制代码
npm create vue@latest

根据提示选择配置(新手推荐:TypeScript选No,其余默认),完成后进入项目目录并启动:

bash 复制代码
cd 项目名
npm install
npm run dev

步骤 2:项目目录和关键文件

Vue3 项目核心目录结构:

plaintext 复制代码
项目名/
├── public/         # 静态资源(如favicon)
├── src/
│   ├── assets/     # 静态资源(图片、样式)
│   ├── components/ # 公共组件
│   ├── App.vue     # 根组件(组合式API写法)
│   └── main.js     # 入口文件(创建Vue实例)
└── package.json    # 项目配置

关键文件:main.js(Vue3 创建实例的方式):

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

// Vue3通过createApp创建实例
createApp(App).mount('#app')

三、组合式 API 核心用法

组合式 API 的入口是setup,它是 Vue3 组件的逻辑核心,所有组合式 API 都在setup中使用。

1. setup 选项:组合式 API 的入口

setup是组件的 "逻辑区域",在组件创建前执行,返回的对象会暴露给模板使用。

示例:

vue 复制代码
<!-- <script>
// setup
// 1. 执行时机,比beforeCreate还要早
// 2. setup函数中,获取不到this (this是undefined)
// 3. 数据 和 函数,需要在 setup 最后 return,才能在模板中使用
// 问题:每次都要return很麻烦
// 4. 通过 setup 语法糖简化代码
export default{
  setup () {
    console.log('setup函数')
    const message = '你好'
    return {message}
  },
  beforeCreate () {
    console.log('beforeCreate函数')
  }
}

</script> -->

<script setup>
const message = '我是语法糖'
</script>


<template>
  <div>vue3学习</div>
  <div>{{message}}</div>
</template>

2. reactiveref:响应式数据定义

Vue3 通过reactiveref定义响应式数据(数据变化时自动更新视图)。

(1)reactive:定义对象 / 数组类型的响应式数据

vue 复制代码
<script>
import { reactive } from 'vue' // 引入reactive

export default {
  setup() {
    // 用reactive包裹对象,使其变为响应式
    const user = reactive({
      name: '张三',
      age: 20
    })

    // 修改响应式数据
    const updateUser = () => {
      user.age = 21 // 自动触发视图更新
    }

    return { user, updateUser }
  }
}
</script>

<template>
  <div>姓名:{{ user.name }}</div>
  <div>年龄:{{ user.age }}</div>
  <button @click="updateUser">增加年龄</button>
</template>

(2)ref:定义基本类型(或单值)的响应式数据

vue 复制代码
<script>
import { ref } from 'vue' // 引入ref

export default {
  setup() {
    // 用ref包裹基本类型,返回的是响应式对象
    const count = ref(0)

    // 修改ref数据时,需通过.value访问
    const increment = () => {
      count.value++
    }

    return { count, increment }
  }
}
</script>

<template>
  <!-- 模板中使用ref数据,无需.value -->
  <div>计数:{{ count }}</div>
  <button @click="increment">+1</button>
</template>

3. computed:计算属性

Vue3 的computed是一个函数,用于定义基于响应式数据的计算属性。

示例

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

export default {
  setup() {
    const price = ref(10)
    const count = ref(2)

    // 计算总价(响应式)
    const total = computed(() => {
      return price.value * count.value
    })

    return { price, count, total }
  }
}
</script>

<template>
  <div>单价:{{ price }}</div>
  <div>数量:{{ count }}</div>
  <div>总价:{{ total }}</div>
</template>

4. watch:监听数据变化

Vue3 的watch用于监听响应式数据的变化,执行自定义逻辑。

示例

vue 复制代码
<script setup>
  import { ref, watch } from 'vue'
const count = ref(0)
const nickname = ref('张三')

const addCount = () => {
  count.value++
}
const changeName = () => {
  nickname.value = '李四'
}

// 1. 监视单个数据的变化
//    watch(ref对象,(newValue, oldValue) => {...})
// watch(count,(newValue,oldValue) => {
//   console.log(newValue,oldValue)
// })

// 2. 监听多个数据的变化
//    watch([ref对象1,ref对象2....],(newArr,oldArr) => {...})
// watch([count, nickname], (newArr, oldArr) => {
//   console.log(newArr, oldArr)
// })

// 3. immediate 立刻执行
watch(count,(newValue,oldValue) => {
  console.log(newValue,oldValue)
}, {
  immediate: true
})

// 4. deep 深度监视,默认 watch 进行的是 浅层监视
//    const ref1 = ref(简单类型) 可以直接监视
//    const ref2 = ref(复杂类型) 监视不到复杂类型内部数据的变化
const userInfo = ref({
  name: 'zs',
  age: 18
})
const changeUserInfo = () => {
  // 修改了 userInfo.value 修改了对象的地址,才能监听到
  // userInfo.value = { name: 'ls', age: 20 }
  userInfo.value.age++
}
// 4. deep 深度监视,默认 watch 进行的是 浅层监视
// watch(userInfo,(newValue,oldValue) => {
//   console.log(newValue,oldValue)
// },{
//   deep: true
// })

// 5. 对于对象中的属性,进行监听
watch(() => userInfo.value.age, (newValue, oldValue) => {
  console.log(newValue,oldValue)
})
</script>

<template>
  <div>{{ count }}</div>
  <button @click="addCount">+1</button>
  <div>{{ nickname }}</div>
  <button @click="changeName">改名字</button>
  <div>{{ userInfo }}</div>
  <button @click="changeUserInfo">修改</button>
</template>

5. 父子组件通信

(1)父传子:props

父组件通过属性传递数据,子组件通过defineProps接收(Vue3 的宏)。

父组件:

vue 复制代码
<template>
  <Child :msg="parentMsg" />
</template>

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

export default {
  components: { Child },
  setup() {
    const parentMsg = ref('来自父组件的消息')
    return { parentMsg }
  }
}
</script>

子组件

vue 复制代码
<template>
  <div>{{ msg }}</div>
</template>

<script>
// 用defineProps接收父组件传递的props
const props = defineProps({
  msg: {
    type: String,
    required: true
  }
})

export default {
  setup() {
    // 可以在setup中通过props访问
    console.log(props.msg)
  }
}
</script>

(2)子传父:emit

子组件通过defineEmits定义事件,父组件监听事件接收数据。

子组件:

vue 复制代码
<template>
  <button @click="sendData">向父组件传值</button>
</template>

<script>
// 用defineEmits定义可触发的事件
const emit = defineEmits(['child-event'])

export default {
  setup() {
    const sendData = () => {
      // 触发事件,传递数据
      emit('child-event', '子组件的数据')
    }
    return { sendData }
  }
}
</script>

父组件:

vue 复制代码
<template>
  <Child @child-event="handleChildEvent" />
</template>

<script>
import Child from './Child.vue'

export default {
  components: { Child },
  setup() {
    const handleChildEvent = (data) => {
      console.log('子组件传递的数据:', data)
    }
    return { handleChildEvent }
  }
}
</script>

6. 模板引用:defineExpose

Vue3 中,子组件的属性 / 方法默认不会暴露给父组件,需用defineExpose主动暴露。

子组件:

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

export default {
  setup() {
    const count = ref(0)
    const increment = () => {
      count.value++
    }

    // 暴露count和increment给父组件
    defineExpose({ count, increment })
    return { count, increment }
  }
}
</script>

父组件:

vue 复制代码
<template>
  <Child ref="childRef" />
  <button @click="callChildMethod">调用子组件方法</button>
</template>

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

export default {
  components: { Child },
  setup() {
    const childRef = ref(null)

    const callChildMethod = () => {
      // 访问子组件暴露的属性和方法
      childRef.value.increment()
      console.log(childRef.value.count)
    }

    return { childRef, callChildMethod }
  }
}
</script>

7.provide inject:跨组件通信

provideinject用于跨层级组件通信(父组件提供数据,子孙组件注入数据)。

顶层组件:

vue 复制代码
<script setup>
import CenterCom from './components/center-com.vue';
import {provide ,ref} from 'vue'
// 1. 跨层传递普通数据
provide('theme-color', 'pink')
// 2. 跨层传递响应式数据
const count = ref(100)
provide('count', count)

setTimeout(() => {
  count.value = 500
}, 5000)

// 3. 跨层传递函数 => 给子孙后代传递可以修改数据的方法
provide('changeCount', (newCount) => {
  count.value = newCount
})

</script>

<template>
<div><h1>我是顶层组件</h1>
<CenterCom></CenterCom>
</div>
</template>

中层组件

vue 复制代码
<script setup>
import BottonCom from './botton-com.vue';
</script>

<template>
<div><h2>我是中层组件</h2>
<BottonCom></BottonCom>
</div>
</template>

底部组件

vue 复制代码
<script setup>
import { inject } from 'vue'
const themeColor = inject('theme-color')
const count = inject('count')
const changeCount = inject('changeCount')
const clickFn = () => {
    changeCount(1500)
}
</script>

<template>
<div><h3>我是底部组件---{{ themeColor }}------{{ count }}</h3>
<button @click="clickFn">更新count</button>
</div>
</template>

注意:主要看顶层组件和底部组件,中层组件只是用来过渡的

相关推荐
无限大66 小时前
为什么键盘有"机械"和"薄膜"之分?——按键的触感革命
前端
不会飞的鲨鱼6 小时前
抖音验证码滑动轨迹原理(续)
javascript·爬虫·python
Mintopia6 小时前
🌐 长期视角:WebAIGC 技术的社会价值边界与伦理底线
前端·人工智能·aigc
Hilaku6 小时前
2025快手直播至暗时刻:当黑产自动化洪流击穿P0防线,我们前端能做什么?🤷‍♂️
前端·javascript·安全
San30.6 小时前
深度解析 React 组件化开发:从 Props 通信到样式管理的进阶指南
前端·javascript·react.js
C_心欲无痕6 小时前
vue3 - 内置组件KeepAlive优化组件状态缓存
前端·vue.js·缓存
Swift社区6 小时前
跨端路由设计:如何统一 RN 与 Web 的页面模型
前端·react.js·web3
fantasy_arch6 小时前
SVT-AV1帧类型决策-场景切换检测
前端·网络·av1
LYFlied6 小时前
前端工程化核心面试题与详解
前端·面试·工程化