Vue3 五天速成(上)https://developer.aliyun.com/article/1504164?spm=a2c6h.13148508.setting.29.36834f0eJwPRIa
6. watchEffect
相较于watch,watchEffect不需要指定监视对象,而是响应式的追踪对象;
<template> <div> <h5>情况4:监视【ref】定义的值是【对象类型】的数据</h5> 姓名: {{ person.name }} <br> 年龄: {{ person.age }} <br> 车辆:{{ person.car.c1 }}, {{ person.car.c2 }} <br> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="changeC1">修改c1</button> <button @click="changeC2">修改c2</button> <button @click="changeCar">修改car</button> </div> </template> <script lang="ts"> export default { name: 'Person' } </script> <script setup lang="ts"> import {reactive, watch, watchEffect} from 'vue' let person = reactive({ name: '张三', age: 18, car:{ c1: 'asd', c2: 'das' } }) function changeName (){ person.name = '李四' } function changeAge (){ person.age += 1 } function changeC1(){ person.car.c1 = 'qqq' } function changeC2(){ person.car.c2 = 'www' } function changeCar(){ person.car = {c1:'yyy', c2:'jjj'} } // 此时newVal和oldVal是数组与前面的对应 /* watch([()=>person.name, ()=>person.age], (newVal, oldVal)=>{ let [newname, newage] = newVal if( newage > 23 ){ console.log('发送请求') } }) */ // 如果采用watchEffect,全自动的watch; watchEffect(()=>{ if( person.age > 23 ){ console.log('发送请求') } }) </script> <style scoped> button { margin: 10px; } </style>
7. 标签的ref
属性
利用ref
于document.getElementById('')
的区别在于,前者是局部的,不会受到整体的干扰;
<template> <h2> 北京 </h2> <h2 ref="title"> 师范 </h2> <h2> 大学 </h2> <button @click="output"> 点击一下输出样式 </button> </template> <script lang="ts"> export default { name: 'Person' } </script> <script setup lang="ts"> import {ref, defineExpose} from 'vue' // 这里变量名和template中的ref中的变量名对应上了 let title = ref() function output(){ console.log(title.value) } // 在这里可以使调用该模块的模块得到该模块的内容 defineExpose({title}) </script> <style scoped> /* 这里的scoped是局部样式,防止和子文件样式出现重复而全部修改 无脑加上就好 */ </style>
day 3
8. 回顾TS中的接口_泛型_自定义类型
一般在文件目录src
下面有一个types
文件夹,types
中有一个index.ts
文件可以直接导入(导入过程不需要写文件)
这里文件表示泛型和接口的意思,一般是结合后端使用,避免缺失参数;
// 定义一个接口,用于限制person对象的具体属性 string,number小写 // 使用export暴露 export interface PersonInter { id: string, name: string, // ?问号表示可有可无 age?: number } // 一个自定义类型 export type Persons = Array<PersonInter> // 也可以这样写 export type Persons = PersonInter[]
<template> <div class="app"> </div> </template> <script lang="ts"> export default { name: 'Person' } </script> <script setup lang="ts"> // PersonInter是类型,必须使用type前置 import {type PersonInter, type Persons} from '@/types' import {reactive} from 'vue' // 单个 let person:PersonInter = {id: 'asdasdas01', name: '张三', age: 18} // 数组 第一种形式 尖括号表示的是泛型:即符合规范 let persons:Array<PersonInter> = [ {id: 'asdasdas01', name: '张三', age: 18}, {id: 'asdasdas02', name: '李四', age: 20}, {id: 'asdasdas03', name: '王五', age: 33} ] // 数组 第二种形式 这里结合index.ts文件 let personList:Persons = [ {id: 'asdasdas01', name: '张三', age: 18}, {id: 'asdasdas02', name: '李四', age: 20}, {id: 'asdasdas03', name: '王五', age: 33} ] let personsReactive = reactive<Persons>([ {id: 'asdasdas01', name: '张三', age: 18}, {id: 'asdasdas02', name: '李四', age: 20}, {id: 'asdasdas03', name: '王五', age: 33} ]) </script> <style scoped> .app { background-color: #ddd; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } </style>
9. props
的使用
将父组件中的数据传给子组件
vue
中html
标签属性前面加冒号表示表达式
<h2 a="1+1" :b="1+1"></h2>
父组件如下:
<template> <Person a="哈哈" :list="persons"/> </template> <script lang="ts"> export default { name: 'App' } </script> <script lang="ts" setup> import Person from './components/Person.vue' import {reactive} from 'vue' import {type Persons} from '@/types' let persons = reactive<Persons>([ {id: 'asdasdas01', name: '张三', age: 18}, {id: 'asdasdas02', name: '李四', age: 20}, {id: 'asdasdas03', name: '王五', age: 33} ]) </script> <style scoped> </style>
子组件如下:
<template> <div class="app"> <ul> <li v-for="a in x.list" :key="a.id">{{ a.name }} - {{ a.age }}</li> </ul> </div> </template> <script lang="ts"> export default { name: 'Person' } </script> <script setup lang="ts"> import {type Persons } from '@/types'; // 下面可以不用引入defineProps,withDefaults import {defineProps, withDefaults} from 'vue' // 接收a // defineProps(['a']) // 接收a和list,同时将props保存起来 // let x = defineProps(['a', 'list']) // x = {a:'哈哈'} // 接收list+限制类型 // let x = defineProps<{list:Persons}>() // 接收list + 限制类型 + 限制必要性 + 指定默认值 // list后面加?表示父组件可传可不传, 第二个list后面需要跟函数 let x = withDefaults(defineProps<{list?:Persons}>(),{ list:()=>[{id:'adasdasdaszxc01', name:'kang', age:20}] }) </script> <style scoped> .app { background-color: #ddd; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } </style>
10. 生命周期
组件的生命周期有四个阶段:创建,挂载,更新,销毁;
钩子:生命周期函数 (阶段前,阶段完毕) 钩子一共有8个
<template> <div class="app"> {{ message }} </div> </template> <script lang="ts"> export default { name: 'Person' } </script> <script setup lang="ts"> import {onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted} from 'vue' let message = '哈哈' onBeforeMount(()=>{ console.log('ok') }) onUnmounted(()=>{ console.log('一般来说需要搭配v-if') }) </script> <style scoped> .app { background-color: #ddd; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } </style>
11. 自定义Hooks
Hooks是组合式功能的体现,其创建过程类似于8节中在文件目录src下创建一个hooks的文件夹,里面存放useXxxx.ts文件,实例如下:
useAdd.ts
import { computed } from '@vue/reactivity' import {ref, onMounted} from 'vue' export default function(){ let sum = ref(0) let bigSum = computed(()=>{ return sum.value * 10 }) function add(){ sum.value += 1 } onMounted(()=>{ sum.value += 1 }) return {sum, add, bigSum} }
useDog.ts
import {reactive, onMounted} from 'vue' import axios from 'axios' export default function(){ let dogList = reactive([ 'https://images.dog.ceo/breeds/pembroke/n02113023_4136.jpg' ]) // axios 的 实例用法1 /* function addDog(){ axios.get('https://dog.ceo/api/breed/pembroke/images/random').then( response => {dogList.push(response.data.message)}, error => {console.log(error)} ) } */ // axios 的 实例用法2 async function addDog() { try { let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random') dogList.push(result.data.message) } catch (error) { console.log(error) } } onMounted(()=>{ addDog() }) return {dogList, addDog} }
这样组件中可以直接应用hooks文件下的内容:
<template> <div class="app"> <h2> sum: {{ sum }}; bigSum: {{ bigSum }}</h2> <button @click="add"> 点我sum+1</button> <hr> <img v-for="(dog,index) in dogList" :src="dog" :key="index"> <br> <button @click="addDog"> 点我再添加一只狗 </button> </div> </template> <script lang="ts"> export default { name: 'Person' } </script> <script setup lang="ts"> import useAdd from '@/hooks/useAdd' import useDog from '@/hooks/useDog' const {sum, add, bigSum} = useAdd() const {dogList, addDog} = useDog() </script> <style scoped> .app { background-color: #ddd; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } img { height: 150px; margin-right: 10px; } </style>
一般在文件目录src
下面有一个types
文件夹,types
中有一个index.ts
文件可以直接导入(导入过程不需要写文件)
12. 路由 基本切换效果
路由和路由器不一样
路由类似于键值对,route
路由器类似于管理路由,router
路由的创建与Hooks和TS接口类似,其创建过程类似于8节中在文件目录src下创建一个router的文件夹,里面存放index.ts文件(无需导入文件名),实例如下:
index.ts 实例如下:
// 创建一个路由器,并暴露出去 // 第一步:引入createRouter import {createRouter, createWebHistory} from 'vue-router' // 第二步:引入可能要呈现的组件 import Home from '@/components/Home.vue' import About from '@/components/About.vue' import News from '@/components/News.vue' // 第三步:创建路由器 const router = createRouter({ // history表示路由的工作模式 history: createWebHistory(), routes:[// 一个个路由规则 { path: '/home', component: Home }, { path: '/news', component: News }, { path: '/about', component: About } ] }) export default router
在这里我们只是创建了路由,于此同时,我们需要把路由加载到项目中,所以接下来我们需要在main.ts
文件中操作;
// 引入createApp 用于创建应用 import { createApp } from 'vue' // 引入app根组件 import App from './App.vue' // 引入路由器 import router from './router' // 创建一个应用 const app = createApp(App) // 使用路由器 app.use(router) // 挂载整个应用到app容器中 app.mount('#app')
为了在App.vue
引入组件,我们还需要做一些操作,使用RouterView
, RouterLink
:
<template> <h2> Vue 路由测试 </h2> <!-- 导航区 --> <div class="navigate"> <RouterLink to="/home">主页</RouterLink> <RouterLink to="/news">新闻</RouterLink> <RouterLink to="/about">关于</RouterLink> </div> <!-- 展示区 --> <div class="main_content"> <RouterView/> </div> </template> <script lang="ts"> export default { name: 'App' } </script> <script lang="ts" setup> import {RouterView, RouterLink} from 'vue-router' </script> <style scoped> h2 { background: skyblue; text-align: center; } div.navigate { text-align: center; } div.navigate a{ text-align: center; margin: 50px; } div.main_content{ text-align: center; height: 500px; } </style>
就完毕了,这里要用RouterLink替换a标签,并把href改成to,用RouterView表示component的插入位置;
13. 路由 两个注意点
路由组件一般放在pages
或者views
文件夹,一般组件通常放在components
文件夹;
通过点击导航,视觉上效果消失了的组件,默认是被销毁掉的,需要的时候再去挂载;
14. 路由 路由器的工作模式
路由器有两种工作模式,history和hash,前端一般用history,后端一般用hash;
15. 路由 to的两种写法
%% 第一种: %% <RouterLink to="/home" active-class="active">主页</RouterLink> %% 第二种: %%: <RouterLink :to="{path='/home'}" active-class="active">主页</RouterLink>
这里再介绍一下 active-class ,由于RouterLink在渲染的时候会变成a标签,所以样式可以类似于设置如下:
a.active { text-align: center; margin: 50px; color: red; }
16. 路由 命名路由
可以在路由的index.ts文件中添加添加名字:
// 创建一个路由器,并暴露出去 // 第一步:引入createRouter import {createRouter, createWebHistory, createWebHashHistory} from 'vue-router' // 第二步:引入可能要呈现的组件 import Home from '@/views/Home.vue' import About from '@/views/About.vue' import News from '@/views/News.vue' // 第三步:创建路由器 const router = createRouter({ // 路由的工作模式:history模式 不带#号 优先 history: createWebHistory(), // 路由的工作模式:hash模式 带#号 // history: createWebHashHistory(), routes:[// 一个个路由规则 { name: 'zhuye', path: '/home', component: Home }, { name: 'xinwen', path: '/news', component: News }, { name: 'guanyu', path: '/about', component: About } ] }) export default router
17. 路由 嵌套路由
子级不用加斜杠,但是to的时候需要完整的使用\news\detail
;
// 创建一个路由器,并暴露出去 // 第一步:引入createRouter import {createRouter, createWebHistory, createWebHashHistory} from 'vue-router' // 第二步:引入可能要呈现的组件 import Home from '@/views/Home.vue' import About from '@/views/About.vue' import News from '@/views/News.vue' import Detail from '@/views/Detail.vue' // 第三步:创建路由器 const router = createRouter({ // 路由的工作模式:history模式 不带#号 优先 history: createWebHistory(), // 路由的工作模式:hash模式 带#号 // history: createWebHashHistory(), routes:[// 一个个路由规则 { name: 'zhuye', path: '/home', component: Home }, { name: 'xinwen', path: '/news', component: News, children:[ { path: 'detail', //子级不用写斜杠 component: Detail, } ] }, { name: 'guanyu', path: '/about', component: About } ] }) export default router
18. 路由 query 参数
在传参数给组件之前,我们需要给接收参数的组件添加一个Hooks:useRoute
<template> <ul class="news-list"> <li>编号:{{ route.query.id }}</li> <li>标题:{{ route.query.title }}</li> <li>内容:{{ route.query.content }}</li> </ul> </template> <script setup lang="ts"> import {useRoute} from 'vue-router' import {toRefs} from 'vue' let route = useRoute() // 如果需要解构 let {query} = toRefs(route) </script> <style scoped> </style>
这里在RouterLink
中添加query参数:
<ul> <li v-for="news in NewsList" :key="news.id"> <!-- 第一种写法 --> <!-- <RouterLink :to="`/news/detail?id=${news.id}&title=${news.title}&content=${news.content}`">{{ news.title }}</RouterLink> --> <!-- 第二种写法 --> <RouterLink :to="{ path: '/news/detail', query: { id: news.id, title: news.title, content: news.content } }"> {{ news.title }} </RouterLink> </li> </ul>
19. 路由 params 参数
首先需要在router中设置占位:就是在path中添加/:
// 创建一个路由器,并暴露出去 // 第一步:引入createRouter import {createRouter, createWebHistory, createWebHashHistory} from 'vue-router' // 第二步:引入可能要呈现的组件 import Home from '@/views/Home.vue' import About from '@/views/About.vue' import News from '@/views/News.vue' import Detail from '@/views/Detail.vue' // 第三步:创建路由器 const router = createRouter({ // 路由的工作模式:history模式 不带#号 优先 history: createWebHistory(), // 路由的工作模式:hash模式 带#号 // history: createWebHashHistory(), routes:[// 一个个路由规则 { name: 'zhuye', path: '/home', component: Home }, { name: 'xinwen', path: '/news', component: News, children:[ { name: 'xiangqing', path: 'detail/:id/:title/:content?', // 需要占位 ?代表可传可不传 component: Detail, } ] }, { name: 'guanyu', path: '/about', component: About } ] }) export default router
params只能使用route
的name
,而不能使用path
,同时传参数只能使用基本类型,而不能使用数组和对象;
<ul> <li v-for="news in NewsList" :key="news.id"> <!-- 第一种写法 --> <!-- <RouterLink :to="`/news/detail/${news.id}/${news.title}/${news.content}`">{{ news.title }}</RouterLink> --> <!-- 第二种写法 --> <RouterLink :to="{ name: 'xiangqing', params: {// 只能传基本类型,不能是对象和数组 id: news.id, title: news.title, content:news.content, } }"> {{ news.title }} </RouterLink> </li> </ul>
20. 路由 props配置
props:true 布尔值写法,只能搭配params使用,path需要占位
props(route){return route.query} 函数式写法,其中route可以操作params和query
props:{id:xx,title:xx,content:xx}
其原理就相当于在 中 添加了
21. 路由 replace属性
类似于浏览器的前进后退机制;
<div class="navigate"> <RouterLink replace to="/home" active-class="active">主页</RouterLink> <RouterLink replace :to="{name:'xinwen'}" active-class="active">新闻</RouterLink> <RouterLink replace to="/about" active-class="active">关于</RouterLink> </div>
直接在RouterLink
后面添加replace
,不会留下记录;
22. 路由 编程式路由导航
可以做到不点标签直接跳转的效果,脱离RouterLink实现跳转;router.push()
router.replace()
括号中to能怎么写,括号就能怎么写;
setTimeout(()=>{ // 在此处执行跳转 }, 3000) // 3秒后执行函数
实例代码如下:
import {onMounted} from 'vue' import { useRouter } from 'vue-router'; const router = useRouter() onMounted(()=>{ setTimeout(()=>{ router.push('/news') }, 3000) })
按钮点击实例代码如下:
<template> <div class="news"> <!-- 导航区 --> <ul> <li v-for="news in NewsList" :key="news.id"> <button @click="showContent(news)">点击获取详情</button> <RouterLink :to="{ name: 'xiangqing', params: {// 只能传基本类型,不能是对象和数组 id: news.id, title: news.title, content:news.content, } }"> {{ news.title }} </RouterLink> </li> </ul> <!-- 展示区 --> <div class="news-content"> <RouterView></RouterView> </div> </div> </template> <script setup lang="ts"> import type path from 'path'; import {reactive} from 'vue' import {RouterView, RouterLink, useRouter} from 'vue-router' interface News{ id: string, title: string, content: string, } const NewsList = reactive<News[]>([ {id: 'asdasd01', title:'新闻阿斯达所多01', content: '在成长速度还01'}, {id: 'asdasd02', title:'新闻阿斯达所多02', content: '在成长速度还02'}, {id: 'asdasd03', title:'新闻阿斯达所多03', content: '在成长速度还03'}, {id: 'asdasd04', title:'新闻阿斯达所多04', content: '在成长速度还04'}, ]) const router = useRouter() function showContent(news:News){ router.push( { name: 'xiangqing', params: {// 只能传基本类型,不能是对象和数组 id: news.id, title: news.title, content:news.content, } }) } </script> <style scoped> div.news { background-color: yellow; height: 500px; } div.news-content { background-color: aqua; height: 300px; width: 200px; } </style>
23. 路由 重定向
自动切换url,在router文件中补充规则:
{ path: '/', redirect: '/home' }
day 4
24. pinia 选项式写法
pinia环境首先需要安装
npm install pinia
安装之后,pinia的创建与Hooks和TS接口类似,其创建过程类似于8节中在文件目录src下创建一个store的文件夹,里面存放对应components中的文件实例如下:
在main.ts文件中需要引入pinia
import {createApp} from 'vue' import App from './App.vue' import {createPinia} from 'pinia' import router from './router' const app = createApp(App) const pinia = createPinia() app.use(pinia) app.use(router) app.mount('#app')
Count.vue如下:
<template> <div class="count"> <h2>当前求和为: {{ sum }}, bigsum:{{ bigSum }}, smallsum:{{ smallSum }}</h2> <!-- 这里.number表示value数值后面的符号为单位 --> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="add">加</button> <button @click="minus">减</button> </div> </template> <script lang="ts"> export default { name: 'Count' } </script> <script setup lang="ts"> import {useCountStore} from '@/store/Count' import {storeToRefs} from 'pinia' let countStore = useCountStore() // storeToRefs只会关注store中的数据 let {sum, n, bigSum, smallSum} = storeToRefs(countStore) // 以下两种方式都能拿到state中的数据 // console.log(countStore.sum) // console.log(countStore.$state.sum) // 方法 function add(){ // 第一种修改方式 // countStore.sum += countStore.n // 第二种修改方式 // countStore.$patch({ // sum: 100 // }) // 第三种修改方式 复杂内容可以放到store中的actions中 countStore.increment() } function minus(){ sum.value -= n.value } </script> <style scoped> .count { background-color: skyblue; padding: 10px; border-radius: 10px; box-shadow: 0 0 10px; margin-bottom: 20px; } select, button { margin: 0 10px; height: 25px; } </style>
Count.ts 如下:
import {defineStore} from 'pinia' import {ref} from 'vue' export const useCountStore = defineStore('Count', { // 写函数 actions:{ increment(){ this.sum += this.n } }, // 真正存储数据的地方 state(){ return { sum: ref(0), n: ref(1), } }, // 类似于vue中的computed属性 getters:{ bigSum: state=>state.sum*10, smallSum():number{ return this.sum - 1000 } } })
LoveTalk.vue如下:
<template> <div class="talk"> <button @click="getMessage"> 点击获取一句土味情话</button> <ul> <li v-for="talk in talkList" :key="talk.id">{{ talk.content }}</li> </ul> </div> </template> <script lang="ts"> export default { name: 'LoveTalk' } </script> <script setup lang="ts"> import {useLoveStore} from '@/store/LoveTalk' import {storeToRefs} from 'pinia' let loveStore = useLoveStore() let {talkList} = storeToRefs(loveStore) loveStore.$subscribe((mutate, state)=>{ // mutate是变化的东西,state是后面的状态 console.log('lovestore保存的数据发生了变化!', mutate, state) // 存储在本地的talkList变量中,由于talkList只能接受字符串数据,所以需要JSON.stringify localStorage.setItem('talkList', JSON.stringify(state.talkList)) }) async function getMessage(){ loveStore.getTalk() } </script> <style scoped> .talk { background-color: orange; padding: 10px; border-radius: 10px; box-shadow: 0 0 10px; } </style>
LoveTalk.ts如下:
import {defineStore} from 'pinia' import {nanoid} from 'nanoid' // 生成id import axios from 'axios' export const useLoveStore = defineStore('LoveTalk', { actions:{ async getTalk(){ let {data:{content}} = await axios.get('https://api.uomg.com/api/rand.qinghua') let obj = {id: nanoid(), content: content} this.talkList.unshift(obj) } }, // 真正存储数据的地方 state(){ return { talkList: JSON.parse(localStorage.getItem('talkList') as string ) || [] } } })
25. pinia 组合式写法
这里相对于选项式,我们需要把state中的部分转化为响应式才有效,然后添加return返回:
LoveTalk.ts如下:
import {defineStore} from 'pinia' import {nanoid} from 'nanoid' import axios from 'axios' import {reactive} from 'vue' export const useLoveStore = defineStore('LoveTalk', ()=>{ // 这里写state中的部分 let talkList = reactive(JSON.parse(localStorage.getItem('talkList') as string ) || []) async function getTalk(){ console.log('获取一句') try { let {data:{content}} = await axios.get('https://api.uomg.com/api/rand.qinghua') let obj = {id: nanoid(), content: content} talkList.unshift(obj) } catch (error) { console.log('获取失败!') } } return {talkList, getTalk} })
day 5
26. 组件通信_props
Father.vue 如下:
<template> <div class="father"> <h2>这是父组件</h2> <h4>汽车:{{car}}</h4> <h4 v-if="toy">获取到儿子的玩具:{{ toy }}</h4> <Child :car="car" :sendToy="getToy"></Child> </div> </template> <script lang="ts"> export default { name: 'Props' } </script> <script setup lang="ts"> import Child from './Child.vue' import {ref} from 'vue' // 数据 let car = ref('benz') let toy = ref('') // 方法 function getToy(val:string){ toy.value = val } </script> <style scoped> div.father { margin: 30px; background-color: yellow; width: 500px; height: 300px; border-radius: 10px; box-shadow: 0 0 10px; } </style>
Child.vue如下:
<template> <div class="child"> <h2>这是子组件</h2> <h4>玩具:{{ toy }}</h4> <h4>父亲给的车:{{ car }}</h4> <button @click="sendToy(toy)">点击把玩具交给父亲</button> </div> </template> <script lang="ts"> export default { name: 'Child' } </script> <script setup lang="ts"> import { ref } from 'vue' // 数据 let toy = ref('atm') defineProps(['car', 'sendToy']) </script> <style scoped> div.child { background-color: gray; width: 80%; height: 70%; margin-left: 20px; border-radius: 10px; box-shadow: 0 0 10px; } </style>
27. 组件通信_custom-event
标签中使用@开头的props表示的是事件,比如@click是点击事件,如果我们需要打印事件,我们可以使用$event当做参数放入函数中获取事件;
事件一般用串串的形式,而回调函数使用小驼峰
Father.vue 代码如下:
<template> <div class="father"> <h2>这是父组件</h2> <h3 v-if="toy">儿子给的玩具{{ toy }}</h3> <!-- 这里send-toy表示时间的名字,getToy表示事件触发时使用的函数回调 --> <!-- 事件一般用串串的形式,而回调函数使用小驼峰 --> <Child @send-toy="getToy"></Child> <!-- 这里可以使用$event 提取出事件进行打印 --> <!-- <Child @send-toy="getToy(param1, param2, $event)"></Child> --> </div> </template> <script lang="ts"> export default { name: 'Event' } </script> <script setup lang="ts"> import Child from './Child.vue' import {ref} from 'vue' let toy = ref('') function getToy(val:string){ toy.value = val } </script> <style scoped> div.father { margin: 30px; background-color: yellow; width: 500px; height: 300px; border-radius: 10px; box-shadow: 0 0 10px; } </style>
Child.vue 代码如下:
<template> <div class="child"> <h2>这是子组件</h2> <h3>儿子的玩具{{ toy }}</h3> <button @click="emit('send-toy', toy)"> 发送给父亲</button> </div> </template> <script lang="ts"> export default { name: 'Child' } </script> <script setup lang="ts"> import {ref} from 'vue' let toy = ref('atm') let emit = defineEmits(['send-toy']) </script> <style scoped> div.child { background-color: gray; width: 80%; height: 70%; margin-left: 20px; border-radius: 10px; box-shadow: 0 0 10px; } </style>
Vue3 五天速成(下)https://developer.aliyun.com/article/1504170?spm=a2c6h.13148508.setting.27.36834f0eJwPRIa