目录
一:路由以及基本切换
路由的安装: npm install vue-router
注意⚠️:
1. 路由组件通常存放在pages 或 views文件夹, 一般组件通常存放在components文件夹。
2. 通过点击导航, 视觉效果上“消失” 了的路由组件, 默认是被卸载掉的, 需要时再去挂载。
1: 路由的配置文件
//文件:router/index.ts /**router:路由器 route:路由 *关系:路由器管理多个路由(routes) */ //第一步:引入createRouter路由器 import { createRouter, createWebHistory } from 'vue-router' import Home from '../components/theHome.vue' import About from '../components/theAbout.vue' //第二步:创建路由器 const router = createRouter({ history: createWebHistory(),//vue3必须引入路由器的工作模式 routes: [ //一个一个的路由规则 { path: '/home', name: 'home', component: Home }, { path: '/about', name: 'about', component: About } ] }) //第三步:暴露router export default router
2:main.ts
//main.ts import './assets/main.css' import { createApp } from 'vue' //引入creatApp来创建应用(花盆) import App from './App.vue' //引入APP根组件(花的根) import router from './router' //引入路由器 const app = createApp(App) //创建一个应用(花插进花盆里) app.use(router)//使用路由器 app.mount('#app') //花摆放的位置-载入到index.html的#app容器中
3: APP.vue
//APP.vue <template> <div class="navigate"> <!-- 跳转到哪个路由,样式是怎样的 --> <RouterLink to="/home" active-class="active">首页</RouterLink> <RouterLink to="/about" active-class="active">关于</RouterLink> </div> <!-- 路由内容的展示区域-路由视图 --> <RouterView></RouterView> </template> <script setup lang="ts"> import { RouterView, RouterLink } from 'vue-router'; </script>
二:路由器工作模式
history模式
优点: URL更加美观, 不带有#, 更接近传统的网站URL。
缺点: 后期项目上线, 需要服务端配合处理路径问题, 否则刷新会有404错误。
应用场景:ToB较多
const router = createRouter({ history:createWebHistory(), //history模式 })
hash模式
优点: 兼容性更好, 因为不需要服务器端处理路径。
缺点: URL带有#不太美观, 且在SEO优化方面相对较差。
应用场景:ToC较多
const router = createRouter({ history:createWebHashHistory(), //hash模式 })
三:to的三种写法
<!-- to的第一种写法: 对象-path --> <RouterLink :to="{path:'/theComputed'}" active-class="active">计算属性</RouterLink> <!-- to的第二种写法: 对象-name --> <RouterLink :to="{name:'watch_watch'}" active-class="active">监听</RouterLink> <!-- to的第三种写法: 字符串 --> <RouterLink to="/watchEffect" active-class="active">自动监视</RouterLink>
四:路由传参
有三种传递方式: query / params / props
query / params 需要引入useRoute 获取参数
import { useRoute } from 'vue-router';
1: query
//router/index.ts { path: '/theNews', name: 'theNews', component: TheNews, children:[ { path:'detail', name:'detail', component: Detail, } ] },
//theNews.vue <template> <ul> <li v-for="news in newsList" :key="news.id"> <!-- query传参第一种写法 字符串拼接--> <!-- <RouterLink :to="`/theNews/detail?id=${news.id}&title=${news.title}&content=${news.content}`">{{news.title}}</RouterLink> --> <!-- query传参第二种写法 对象--> <RouterLink :to="{ // 使用path or name // path: '/theNews/detail', name: 'detail', query:{ id:news.id, title:news.title, content:news.content } }"> {{news.title}} </RouterLink> </li> </ul> <!-- 展示区 --> <div class="news-content"> <RouterView></RouterView> </div> </template> <script setup lang="ts" name="theAbout"> import { reactive } from 'vue'; import {RouterView,RouterLink} from 'vue-router' const newsList = reactive([ {id:'001',title:'很好的抗癌食物',content:'西蓝花'}, {id:'002',title:'如何一夜暴富',content:'学IT'}, {id:'003',title:'震惊,万万没想到',content:'这周六天班'}, {id:'004',title:'好消息!好消息!',content:'还有一天放假'} ]) </script>
//theDetail.vue <template> <ul class="news-list"> <li>编号:{{ query.id }}</li> <li>标题:{{ query.title }}</li> <li>内容:{{ query.content }}</li> </ul> </template> <script setup lang="ts" name="theNews"> import { useRoute } from 'vue-router'; import { toRefs } from 'vue'; let route = useRoute(); let { query } = toRefs(route) console.log(route) </script>
2:params
注意⚠️:此时只能使用to的name属性,不可以使用path属性
//router/index.ts { path: '/theNews', name: 'theNews', component: TheNews, children:[ { //第一种写法 字符串拼接 //需要再路径中进行占位. ?代表content可选,可以没有这个属性 path:'detail/:id/:title/:content?', name:'detail', component: Detail, } ] },
//theNews.vue <!-- params传参第一种写法 字符串拼接--> <RouterLink :to="`/theNews/detail/${news.id}/${news.title}/${news.content}`">{{news.title}}</RouterLink> <!-- params传参第二种写法 对象--> <!-- 只能使用name属性传参 --> <RouterLink :to="{ name: 'detail', params:{ id:news.id, title:news.title, content:news.content } }"> {{news.title}} </RouterLink>
//theDetail.vue <template> <ul class="news-list"> <li>编号:{{ params.id }}</li> <li>标题:{{ params.title }}</li> <li>内容:{{ params.content }}</li> </ul> </template> <script setup lang="ts" name="theNews"> import { useRoute } from 'vue-router'; import { toRefs } from 'vue'; let route = useRoute(); let { params } = toRefs(route) console.log(route) </script>
3:props
三种方式,重点掌握第三种
可以直接拿到传递的参数,标签中不需要在进行 . 了
使用宏函数 defineProps() 接收参数
//router/index.ts { path: '/theNews', name: 'theNews', component: TheNews, children:[ { path:'detail', name:'detail', component: Detail, // 第一种:对象写法,作用:把对象中的每一组key-value作为props传给Detail组件 props:{a:1,b:2,c:3}, // 第二种:布尔值写法,作用:把收到了每一组params参数,作为props传给Detail组件 //注意只能是params参数才能使用 props:true //!!!!!重点掌握 // 第三种:函数写法,作用:把返回的对象中每一组key-value作为props传给Detail组件 props(route){ //传参的route 可以拿到所有的本路由信息 return route.query } } ] }
<template> <ul class="news-list"> <li>编号:{{ id }}</li> <li>标题:{{ title }}</li> <li>内容:{{ content }}</li> </ul> </template> <script setup lang="ts" name="theNews"> //配合路由props使用,直接拿到路由传参的内容 defineProps(['id','title','content']) </script>
五:push & replace
1. 作用: 控制路由跳转时操作浏览器历史记录的模式。
2. 浏览器的历史记录有两种写入方式: 分别为push和replace:
push: 追加历史记录(默认值)。
replace: 替换当前记录, 无痕浏览, 不能返回 。
3. 开启replace模式: RouterLink中加入replace即可
<RouterLink replace to="/home" active-class="active">首页</RouterLink>
六:编程式路由 - 重点
第一步:引入useRouter
import { useRouter } from 'vue-router'
第二步:调用
const router = useRouter();
第三步:使用,push()或者replace()。
注意⚠️:push和replace里。和路由中的to写法一样,可以字符串也可以对象格式。
router.push({ name: 'detail', params:{ id:news.id, title:news.title, content:news.content } })
应用场景:
1:符合某些条件进行路由跳转 -- 比如登录成功跳首页
2:鼠标滑过内容进行路由跳转 -- 比如导航栏
<template> <ul> <li v-for="news in newsList" :key="news.id"> <RouterLink :to="{ name: 'detail', params:{ id:news.id, title:news.title, content:news.content } }"> {{news.title}} </RouterLink> <button @click="showDeatil(news)">查看详情</button> </li> </ul> <!-- 展示区 --> <div class="news-content"> <RouterView></RouterView> </div> </template> <script setup lang="ts" name="theAbout"> import { reactive } from 'vue'; import {RouterView, RouterLink, useRouter} from 'vue-router' const newsList = reactive([ {id:'001',title:'很好的抗癌食物',content:'西蓝花'}, {id:'002',title:'如何一夜暴富',content:'学IT'}, {id:'003',title:'震惊,万万没想到',content:'这周六天班'}, {id:'004',title:'好消息!好消息!'} ]) const router = useRouter(); //定义一个NewsInter接口 进行news类型检查 interface NewsInter { id:string, title:string, content:string } //不写类型会导致报any的错误,可以直接写any类型,也可以写一个interface接口 //作用:限制参数类型 function showDeatil(news:NewsInter){ //也可以使用router.replace(); router.push({ name: 'detail', params:{ id:news.id, title:news.title, content:news.content } }) } </script>
七:路由重定向 - redirect
重定向 redirect,将特定的路径,重新定向到已有路由。
const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', redirect:'/home' //重定向 } ] })
八:pinia
pinia:vue3的状态管理库
安装:npm i pinia
Store是一个保存状态、业务逻辑的实体,每个组件都可以读取、写入它。
它有三个概念: state、getter、actions, 相当于组件中的: data、 computed 和 methods。
在目录src/store文件下,声明对应的store库文件(ts)。
//theCount.js // store第一步 - 引入 defineStore(小仓库) import { defineStore } from 'pinia' /* defineStore() 第一个参数:theCount 相当于文件id 第二个参数:配置对象 */ // store第二步 - 暴露出去 export const useCountStore = defineStore('theCount',{ /* state必须写成一个函数 state:真正存储数据的地方 */ state() { return { sum:2 } }, })
//theCount.vue <template> <div class="count"> <h2>当前求和为:{{ countSrore.sum }}</h2> <!-- .number 字符串转为number类型 --> <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 setup lang="ts" name="theCount"> import { ref } from "vue"; // store第三步 - 引入store文件 import { useCountStore } from '../store/theCount.ts' // store第四步 - 调用 let countSrore = useCountStore(); // 以下两种方式都可以拿到store中的数据 console.log(countSrore.sum); console.log(countSrore.$state.sum); let n = ref(1) // 用户选择的数字 // 方法 function add(){ countSrore.sum += n.value; } function minus(){ countSrore.sum -= n.value; } </script>
九:pinia - state / actions
修改数据有三种方式:
1: 拿到数据直接修改,少量数据
// 修改数据第一种 - 修改少量数据 countSrore.sum += n.value;
2: 借用$patch
// 修改数据第二种 - 修改数据碎片 countSrore.$patch({ type:'化学', place: '净化海洋', sum: n.value })
3: 借用 store中的 actions
// 修改数据第三种 - 修改数据,并有复杂的逻辑判断。 countSrore.changeInfo(n.value)
export const useCountStore = defineStore('theCount',{ //actions里面放置的是一个一个的方法,用于响应组件中的“动作” actions:{ changeInfo(n:any){ //方法内可以进行数据修改和逻辑判断 console.log(n); //actions中使用state中的数据,只需要用this就可以拿到 this.sum += n; } }, state() { return { type:'前端', place: '改变世界', sum:2 } }, })
十: pinia - storeToRefs()
使用pinia中的 storeToRefs 进行store数据美化。只对store数据进行ref包裹
import { storeToRefs } from 'pinia'; let countSrore = useCountStore(); // storeToRefs只会关注sotre中数据,不会对方法进行ref包裹 let {sum, place, type} = storeToRefs(countSrore); console.log('!!!!!',storeToRefs(countSrore)) // toRefs会对countSrore的所有内容进行ref包裹,包括数据、方法 // let {sum, place, type} = toRefs(countSrore); // console.log('!!!!!',toRefs(countSrore))
十一: pinia - getter
当state中的数据, 需要经过处理后再使用时, 可以使用getters配置。
export const useCountStore = defineStore('theCount',{ state() { return { type:'web', place: '改变世界', sum:2 } }, getters:{ //一:可以直接传参state bigNum:state => state.sum * 10, //二:可以使用this拿数据 typeUpper():string{ return this.type.toUpperCase(); } } })
<template> <div class="count"> <h2>学习{{ type }},{{ place }},大写字母:{{ typeUpper }}</h2> <h2>当前求和为:{{ sum }},放大十倍后{{ bigNum }}</h2> </div> </template> <script setup lang="ts" name="theCount"> import { useCountStore } from '../store/theCount.ts' import { storeToRefs } from 'pinia'; let countSrore = useCountStore(); let {sum, place, type, bigNum, typeUpper} = storeToRefs(countSrore); </script>
十二: pinia - $subscribe
// 通过 store 的 $subscribe() 方法侦听 state 及其变化
talkStore.$subscribe((mutate,state)=>{ console.log('talkStore里面保存的数据发生了变化',mutate,state) localStorage.setItem('talkList',JSON.stringify(state.talkList)) })
十三: pinia - store的组合式写法
//二:组合式写法 第二个参数是方法 export const useTalkStore = defineStore('loveTalk', ()=>{ // talkList就是state const talkList = reactive( JSON.parse(localStorage.getItem('talkList') as string) || [] ) // getLoveTalk函数相当于actions async function getLoveTalk(){ // 发请求 /*连续解构赋值+重命名 ({data:{content}}连续解构 content:title 重命名为title) */ const {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json') // 把请求回来的字符串,包装成一个对象 const obj = {id:nanoid(),title} // 放到数组中 talkList.unshift(obj) } // 必须要return return {talkList, getLoveTalk} })
十四:组件之间传值
(1): props
若 父传子:属性值是非函数。
若 子传父:属性值是函数。
//father.vue <h4>汽车:{{ car }}</h4> <h4 v-show="toy">子给的玩具:{{ toy }}</h4> <Child :car="car" :sendToy="getToy"/> <script setup lang="ts" name="Father"> import Child from './Child.vue' import {ref} from 'vue' // 数据 let car = ref('小米') let toy = ref('') // 把父组件的方法 传递给子组件 function getToy(value:string){ toy.value = value } </script>
//child.vue <h4>玩具:{{ toy }}</h4> <h4>父给的车:{{ car }}</h4> <button @click="sendToy(toy)">把玩具给父亲</button> <script setup lang="ts" name="Child"> import {ref} from 'vue' // 数据 let toy = ref('奥特曼') // 声明接收props defineProps(['car','sendToy']) </script>
(2): 自定义事件
常用于 子传父
注意⚠️:区分原生事件、自定义事件
原生事件:
事件名是特定的( click、mosueenter等等 )
事件对象$event: 就是事件相关信息的对象( target、pageX、keyCode ),可以.target
自定义事件:
事件名是任意名称
事件对象$event: 是调用emit时所提供的数据,可以是任意数据类型!!!,不能.target
//child.vue <button @click="emit('send-toy',toy)">测试</button> // 数据 let toy = ref('奥特曼') // 声明自定义事件send-toy const emit = defineEmits(['send-toy']) //father.vue <Child @send-toy="saveToy"/> // 用于保存传递过来的玩具 function saveToy(value:string){ console.log('saveToy',value) toy.value = value }
(3): mitt
与消息订阅与发布(pubsub)功能类似,可以实现任意组件间通信。
有四个方法:
- on ( 绑定事件 )
- emit ( 调用事件 )
- off ( 关闭某一个事件 )
- all ( 关闭所有事件 )
第一步安装: npm install mitt
第二步引入: import emitter from '@/utils/emitter';
第三步创建: const emitter = mitt();
第四步暴露: export default emitter
//emitter.ts // 引入mitt import mitt from 'mitt' // 调用mitt得到emitter,emitter能:绑定事件、触发事件 const emitter = mitt() // on - 绑定事件(可以N个) emitter.on('test1',()=>{ console.log('test1被调用了') }) emitter.on('test2',()=>{ console.log('test2被调用了') }) // emit - 触发事件 setInterval(() => { emitter.emit('test1') emitter.emit('test2') }, 1000); setTimeout(() => { // off - 关闭某一个事件 // emitter.off('test1') // emitter.off('test2') //all - 关闭所有事件 emitter.all.clear() }, 3000); // 暴露emitter export default emitter
第一步:在接收数据的组件中:(1):绑定事件 (2):在销毁前解绑事件
//child2.vue -- 收 import {ref,onUnmounted} from 'vue' import emitter from '@/utils/emitter'; // 给emitter绑定send-toy事件 emitter.on('send-toy',(value:any)=>{ toy.value = value }) // 在组件卸载时解绑send-toy事件 onUnmounted(()=>{ emitter.off('send-toy') })
第二步:在提供数据的组件,在合适的时候触发事件
//child1.vue -- 发 import emitter from "@/utils/emitter"; <button @click="emitter.emit('send-toy',toy)">玩具给弟弟</button>
(4): v-model
实现 父 ↔ 子 之间 相互通信
组件标签上的v-model的本质::moldeValue + update:modelValue事件。
注意⚠️:HTMLInputElement 制定类型 - html的input输入类型元素
//father.vue <h4>username:{{ username }}</h4> <h4>password:{{ password }}</h4> <!-- v-model用在html标签上 - 可以实现数据双向绑定 --> <!-- <input type="text" v-model="username"> --> <!-- input第7行 双向绑定的本质写法 动态的value值,配合一个绑定事件 HTMLInputElement 制定类型 - html的input输入类型元素--> <input type="text" :value="username" @input="username =(<HTMLInputElement>$event.target).value"> <!-- v-model用在组件标签上 --> <!-- 直接写不能实现双向绑定 --> <!-- <beautifulInput v-model="username"/> --> <!-- elementUi 底层实现原理(本质写法) :modelValue: 实现数据到页面 等同于:value @update:modelValue: vue3的事件名(只不过带了冒号,等同于@input), 叫 更新-modelValue --> <beautifulInput :modelValue="username" @update:modelValue="username = $event"/> <script setup lang="ts" name="Father"> import { ref } from "vue"; import beautifulInput from './beautifulInput.vue' // 数据 let username = ref('章三') let password = ref('123456') </script>
//beautifulInput.vue <template> <!-- @input 绑定update:modelValue事件 并传参- input输入框的内容 --> <input type="text " :value="modelValue" @input="emit('update:modelValue',(<HTMLInputElement>$event.target).value)" > <br> <input type="text" :value="mingzi" @input="emit('update:mingzi',(<HTMLInputElement>$event.target).value)" > </template> <script setup lang="ts" name="beautifulInput"> //接收modelValue defineProps(['modelValue']) //接收事件 const emit = defineEmits(['update:modelValue']) </script>
使用组件类型的传值,可以传递多个model
//father.vue <template> <div class="father"> <h3>父组件</h3> <h4>username:{{ username }}</h4> <h4>password:{{ password }}</h4> <!-- 1:修改modelValue - 重命名为名字 2:可以写多个model --> <beautifulInput v-model:mingzi="username" v-model:mima="password"/> </div> </template> <script setup lang="ts" name="Father"> import { ref } from "vue"; import beautifulInput from './beautifulInput.vue' // 数据 let username = ref('章三') let password = ref('123456') </script>
//beautifulInput.vue <template> <input type="text" :value="mingzi" @input="emit('update:mingzi',(<HTMLInputElement>$event.target).value)" > <br> <input type="text" :value="mima" @input="emit('update:mima',(<HTMLInputElement>$event.target).value)" > </template> <script setup lang="ts" name="beautifulInput"> //接收modelValue defineProps(['mingzi', 'mima']) //接收事件 const emit = defineEmits(['update:mingzi','update:mima']) </script>
(5): $attrs
作用:当前组件的父组件,向当前组件的子组件通信( 祖→孙 传值)
原理:父传子过程中,子组件已接收的存在props中,未接收的存在attrs中。故子组件不接收的$attrs中的数据也可以直接传给孙组件。
//Father.vue <Child :a="a" :b="b" v-bind="{x:100,y:200}" :updateA="updateA"/> <script setup lang="ts" name="Father"> import Child from './Child.vue' import {ref} from 'vue' let a = ref(1) let b = ref(2) function updateA(value:number){ a.value = a.value * value } </script>
//Child.vue <!-- 子组件将未使用的$attr数据直接传递给孙组件 --> <GrandChild v-bind="$attrs"/> <script setup lang="ts" name="Child"> import GrandChild from './GrandChild.vue' </script>
//GrandChild.vue <template> <div class="grand-child"> <h3>孙组件</h3> <h4>a:{{ a }}</h4> <h4>b:{{ b }}</h4> <h4>x:{{ x }}</h4> <h4>y:{{ y }}</h4> <button @click="updateA(10)">点我将爷爷那的a更新</button> </div> </template> <script setup lang="ts" name="GrandChild"> //接收数据 defineProps(['a','b','x','y','updateA']) </script>
(6): $refs & $parent
$refs用于: 父→子, 值为对象, 包含所有被、ref、属性标识的、DOM、元素或组件实例。
$parent用于: 子→父, 值为对象, 当前组件的父组件实例对象。
注意⚠️:需要传递的参数必须通过宏函数 defineExpose() 向外暴露
//Father.vue <template> <div class="father"> <h3>父组件</h3> <h4>房产:{{ house }}</h4> <button @click="changeToy">修改Child1的玩具</button> <button @click="changeComputer">修改Child2的电脑</button> <!-- 父传子 --> <button @click="getAllChild($refs)">让所有孩子的书变多</button> <Child1 ref="c1"/> <Child2 ref="c2"/> </div> </template> <script setup lang="ts" name="Father"> import Child1 from './Child1.vue' import Child2 from './Child2.vue' import { ref,reactive } from "vue"; let c1 = ref() let c2 = ref() // 数据 let house = ref(4) //修改child1的玩具 function changeToy(){ c1.value.toy = '小猪佩奇' } //修改所有子组件的书,传的$refs 就会是所有子组件暴露出来的数据 function getAllChild(refs:{[key:string]:any}){ console.log('所有子组件====',refs) for (let key in refs){ refs[key].book += 3 } } // 向外部提供数据,允许子组件拿到的数据house defineExpose({house}) </script>
//Child1.vue <template> <div class="child1"> <h3>子组件1</h3> <h4>玩具:{{ toy }}</h4> <h4>书籍:{{ book }} 本</h4> <!-- 子传父 --> <button @click="minusHouse($parent)">干掉父亲的一套房产</button> </div> </template> <script setup lang="ts" name="Child1"> import { ref } from "vue"; let toy = ref('奥特曼') let book = ref(3) function minusHouse(parent:any){ parent.house -= 1 } // 把数据交给外部,允许父元素操作toy,book defineExpose({toy,book}) </script>
//Child2.vue <template> <div class="child2"> <h3>子组件2</h3> <h4>电脑:{{ computer }}</h4> <h4>书籍:{{ book }} 本</h4> </div> </template> <script setup lang="ts" name="Child2"> import { ref } from "vue"; let computer = ref('联想') let book = ref(6) // 把数据交给外部,允许父元素操作computer,book defineExpose({computer,book}) </script>
(7): provide-inject
用于祖→孙:隔代传值(不操作子组件,直接实现祖→孙)
父组件:需要引入 provide : import { provide } from "vue";
子组件:需要引入 inject : import { inject } from "vue";
//Father.vue <Child/> <script setup lang="ts" name="Father"> import Child from './Child.vue' //第一步:引入provide import {ref,reactive,provide} from 'vue' let money = ref(100) let car = reactive({ brand:'奔驰', price:100 }) function updateMoney(value:number){ money.value -= value } /* 向后代提供数据 第一个参数:数据名称 第二个参数:数据的值 可以是ref数据(不需要进行 .value操作, 实现数据响应式) 也可以是方法(可以实现子传祖) */ //第二步:调用provide,传参 provide('moneyContext',{money,updateMoney}) provide('car',car) </script>
//GrandChild.vue <button @click="updateMoney(6)">花爷爷的钱</button> <script setup lang="ts" name="GrandChild"> //第三步:引入inject import { inject } from "vue"; //第四步:调用inject,接收参数 //需要定义参数的类型,也可以设置默认值 //这里vue3没有自动识别参数类型 - {money:0, updateMoney:(param:number)=>{}}) let {money,updateMoney} = inject('moneyContext',{money:0, updateMoney:(param:number)=>{}}) //这里vue3没有自动识别参数类型 - {brand:'未知',price:0} let car = inject('car',{brand:'未知',price:0}) </script>
(8): pinia
参考上述的pinia内容即可 : pinia
(9): slot - 默认插槽
默认插槽,在子组件中使用<slot>标签进行占位,父组件中可以传递任何内容
默认插槽只有一个占位(坑)
默认插槽也有名字 name='default'
//Father.vue <Category> <ul> <li v-for="g in games" :key="g.id">{{ g.name }}</li> </ul> </Category> <script setup lang="ts" name="Father"> import Category from './Category.vue' let games = [ {id:'asgytdfats01',name:'英雄联盟'}, {id:'asgytdfats02',name:'王者荣耀'}, {id:'asgytdfats03',name:'吃鸡'}, {id:'asgytdfats04',name:'斗罗大陆'} ] </script> //Category.vue <div class="category"> <!-- 默认插槽,可以设置默认内容 进行占位,将<Category></Category> 中间的内容传进来放进插槽中 --> <slot>默认内容</slot> </div>
(10): slot - 具名插槽
简单来说,给插槽起个名字,就可以实现多个占位(坑)
插槽的名字只能放在 <组件标签> 或 <template> 标签上
注意⚠️:有简写形式:v-slot:slot1 == #slot1
//Father.vue <Category title="今日美食城市"> <!-- v-slot:slot1 可以简写成 #slot1 --> <template v-slot:slot1> <img :src="imgUrl" alt=""> </template> </Category> //Category.vue <!-- 第二种:具名插槽 - 可以设置多个名字,根据名字插入到对应的插槽中 --> <slot name ='slot1'>默认内容</slot>
(11): slot - 作用域插槽
数据在组件(子组件)本身, 但数据生成的结构需要由组件的使用者(父组件)来决定。
例如:玩具数据在Toy组件中, 但使用数据遍历出来的结构由Father组件决定数据:可以传递多个(多个数据组成对象形式传递),父组件随意定一个变量来接收,可以解构赋值
//Father.vue <div class="content"> <Toy title="玩具列表"> <!-- 随意定义一个变量用来接收子组件传过来的参数。这里的params,接收的是子传过来的一个对象 仍然可以使用简写: #toy == v-slot:toy --> <template #toy="params"> <ul> <!-- 定义数据格式 --> <li v-for="item in params.wanju" :key="item.id"> {{item.name}} </li> </ul> </template> </Toy> </div> <script setup lang="ts" name="Father"> import Toy from './Toy.vue' </script>
//Toy.vue <template> <div class="Toy"> <!-- 传参:会将所有参数以对象的形式传给父组件 --> <slot name="toy" :wanju = toyList x="a" y="b"></slot> </div> </template> <script setup lang="ts" name="Toy"> import { reactive } from "vue"; //定义数据 let toyList = reactive([ {id:'001',name:'泡泡玛特'}, {id:'002',name:'飞机模型'}, {id:'003',name:'毛绒玩具'} ]) </script>