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>

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

相关推荐
恋猫de小郭1 天前
AI 可以让 WIFI 实现监控室内人体位置和姿态,无需摄像头?
前端·人工智能·ai编程
哀木1 天前
给自己整一个 claude code,解锁编程新姿势
前端
程序员鱼皮1 天前
GitHub 关注突破 2w,我总结了 10 个涨星涨粉技巧!
前端·后端·github
UrbanJazzerati1 天前
Vue3 父子组件通信完全指南
前端·面试
是一碗螺丝粉1 天前
5分钟上手LangChain.js:用DeepSeek给你的App加上AI能力
前端·人工智能·langchain
wuhen_n1 天前
双端 Diff 算法详解
前端·javascript·vue.js
UrbanJazzerati1 天前
Vue 3 纯小白快速入门指南
前端·面试
雮尘1 天前
手把手带你玩转Android gRPC:一篇搞定原理、配置与客户端开发
android·前端·grpc
光影少年1 天前
说说闭包的理解和应用场景?
前端·javascript·掘金·金石计划
是一碗螺丝粉1 天前
LangChain 核心组件深度解析:模型与提示词模板
前端·langchain·aigc