Vue3的手脚架使用和组件父子间通信-插槽(Options API)学习笔记
2019ab/CSDN发表于:2024-05-12 07:26:02浏览:96次
Vue CLI安装和使用
- 全局安装最新vue3
npm install @vue/cli -g
- 升级Vue CLI:
如果是比较旧的版本,可以通过下面命令来升级
npm update @vue/cli -g
通过脚手架创建项目
vue create 01_product_demo
Vue3父子组件的通信
父传子
父组件
<template>
<div>
<div class="item" v-for="(item,index) in user_list" :key="index">
<user-component :name="item.name" :age="item.age"></user-component>
</div>
</div>
</template>
<script>
import UserComponent from './components/UserComponent'
export default {
components:{
UserComponent
},
data(){
return {
user_list:[
{name:'why',age:18},
{name:'zhang',age:26},
]
}
}
}
</script>
<style scoped>
</style>
子组件 UserComponent.vue
<template>
<div>
<div>名字:{{ name }}</div>
<div>年龄:{{ age }}</div>
</div>
</template>
<script>
export default {
props:{
name:{
type:String,
default:''
},
age:{
type:Number,
default:0
},
// 对象类型
friend:{
type:Object,
default:()=>({name:"james"})
},
// 数组类型
hobbies:{
type:Array,
default:()=>['篮球','rap','唱跳']
}
}
}
</script>
<style scoped>
</style>
子传父
父组件
<template>
<div>
<div>{{ counter }}</div>
<JiaComponent @jia="jia"></JiaComponent>
<JianComponent @jian="jian"></JianComponent>
</div>
</template>
<script>
import JiaComponent from './JiaComponent'
import JianComponent from './JianComponent'
export default {
components:{
JiaComponent,
JianComponent
},
data(){
return {
counter:1
}
},
methods:{
jian:function(data){
this.counter = this.counter - data;
},
jia:function(data){
this.counter = this.counter + data;
}
}
}
</script>
<style scoped>
</style>
子组件1 JiaComponent.vue
<template>
<div>
<div>
<button @click="jia(1)">+1</button>
<button @click="jia(5)">+5</button>
<button @click="jia(10)">+10</button>
</div>
</div>
</template>
<script>
export default {
emits:['jia'], // 使用的时候会有提醒
// emits:{
// // 验证
// jia:function(data){
// if(data <= 5){
// return true
// }
// return false
// }
// },
methods:{
jia(data){
this.$emit('jia',data);
}
}
}
</script>
<style scoped>
</style>
子组件2 JianComponent.vue
<template>
<div>
<div>
<button @click="jian(1)">-1</button>
<button @click="jian(5)">-5</button>
<button @click="jian(10)">-10</button>
</div>
</div>
</template>
<script>
export default {
methods:{
jian(data){
this.$emit("jian",data);
}
}
}
</script>
<style scoped>
</style>
插槽基本使用
父组件
<template>
<div>
<TitleComponents title="标题" desc="描述描述描述描述描述"></TitleComponents>
<!-- 1.插入button -->
<TitleComponents title="标题" desc="描述描述描述描述描述">
<button>按钮</button>
</TitleComponents>
<!-- 2.插入a链接 -->
<TitleComponents title="标题" desc="描述描述描述描述描述">
<a href="https://www.baidu.com">百度一下</a>
</TitleComponents>
<!-- 3.插入image -->
<TitleComponents title="标题" desc="描述描述描述描述描述">
<img src="https://gimg3.baidu.com/search/src=http%3A%2F%2Fpics6.baidu.com%2Ffeed%2F34fae6cd7b899e512cb62692d10fdf3ec9950db4.jpeg%40f_auto%3Ftoken%3D8ce9dbae74003846c318068640c41183&refer=http%3A%2F%2Fwww.baidu.com&app=2021&size=f360,240&n=0&g=0n&q=75&fmt=auto?sec=1698944400&t=e504855a1a7b815dfa76940bb9ac2a07" />
</TitleComponents>
<!-- 4. 默认显示 -->
<TitleComponents title="标题" desc="描述描述描述描述描述"></TitleComponents>
</div>
</template>
<script>
import TitleComponents from './TitleComponents.vue'
export default {
components:{
TitleComponents
}
}
</script>
<style scoped>
</style>
子组件 TitleComponents.vue
<template>
<div>
<h1>{{ title }}</h1>
<div>{{ desc }}</div>
<slot>
<div>这里是默认内容</div>
</slot>
</div>
</template>
<script>
export default {
props:{
title:{
type:String,
default:'默认标题'
},
desc:{
type:String,
default:''
}
}
}
</script>
<style scoped>
</style>
插槽_具名插槽
父组件
<template>
<div>
<NavComponent>
<template v-slot:left>
<button>返回</button>
</template>
<template v-slot:center>
首页
</template>
<template v-slot:right>
<a href="#" >登录</a>
</template>
</NavComponent>
<NavComponent>
<template v-slot:[position]>
<button>返回</button>
</template>
</NavComponent>
<button @click="position = 'left'">左边</button>
<button @click="position = 'center'">中间</button>
<button @click="position = 'right'">右边</button>
</div>
</template>
<script>
import NavComponent from './NavComponent.vue';
export default {
components:{
NavComponent
},
data(){
return {
position:'left'
}
}
}
</script>
<style scoped>
</style>
子组件 NavComponent.vue
<template>
<div>
<div style="display: flex;flex-direction: row;height: 100px;">
<div class="left">
<slot name="left">左边</slot>
</div>
<div class="center">
<slot name="center">中间</slot>
</div>
<div class="right">
<slot name="right">右边</slot>
</div>
</div>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.left{
width:30%;
background-color: aqua;
}
.center{
width: 40%;
background-color: bisque;
}
.right{
width: 30%;
background-color: blueviolet;
}
</style>
组件插槽_作用域插槽
父组件
<template>
<div>
<NavComponet :nav_list="nav_list">
<template v-slot:default="porps_val">
{{ porps_val.item }}---{{ porps_val.abc }}
</template>
</NavComponet>
<!-- -->
<hr>
<NavComponet :nav_list="nav_list">
<template #default="porps_val">
<a href="#">{{ porps_val.item }}---{{ porps_val.abc }}</a>
</template>
</NavComponet>
</div>
</template>
<script>
import NavComponet from './NavComponet.vue'
export default {
components:{
NavComponet
},
data(){
return {
nav_list:[
{id:1,title:'标题1'},
{id:1,title:'标题2'},
{id:1,title:'标题3'},
]
}
}
}
</script>
<style scoped>
</style>
子组件 NavComponet.vue
<template>
<div>
<div class="nav">
<div v-for="(item,index) in nav_list" :key="index" class="nav_item">
<div>
{{ item.title }}
</div>
<slot :item="item.title" abc="cba">
<div>{{ item.title }}</div>
</slot>
</div>
</div>
</div>
</template>
<script>
export default {
props:{
nav_list:{
type:Array,
default:()=>{
return [
{id:1,title:'衣服'},
{id:1,title:'食物'},
{id:1,title:'玩具'},
];
}
}
},
data(){
return {
}
}
}
</script>
<style scoped>
.nav{
display: flex;
flex-direction: row;
}
.nav_item{
flex:1;
}
</style>
Provide和Inject
App.vue
<template>
<div class="app">
<home-component></home-component>
<h2>app:{{ message }}</h2>
<button @click="message = 'hello world'">改变message</button>
</div>
</template>
<script>
import { computed } from 'vue'
import HomeComponent from './HomeComponent.vue'
export default {
components:{
HomeComponent
},
data(){
return {
message:"Hello App"
}
},
provide(){
return {
name:"why",
age:18,
message:computed(()=>{
return this.message
})
}
}
}
</script>
<style scoped>
</style>
子组件 HomeBanner.vue
<template>
<div>
<h2>HomeBanner:{{ name }} - {{ age }} - {{ message }}</h2>
</div>
</template>
<script>
export default {
inject:["name","age","message"]
}
</script>
<style scoped>
</style>
孙组件 HomeComponent.vue
<template>
<div>
<home-banner></home-banner>
</div>
</template>
<script>
import HomeBanner from './HomeBanner.vue';
export default {
components:{
HomeBanner
},
data(){
return {
}
}
}
</script>
<style scoped>
</style>
事件总线的使用
App.vue
<template>
<div class="app">
<!-- 安装状态管理库 npm install hy-event-store -->
<!-- Mitt事件状态(略) -->
<HomeCon></HomeCon>
<h2>{{ message }}</h2>
<button @click="show_cate_gory = !show_cate_gory">是否显示cate_gory</button>
<cate-gory v-if="show_cate_gory"></cate-gory>
</div>
</template>
<script>
import eventBus from './utils/event.bus';
import HomeCon from './HomeCon.vue'
import CateGory from './CateGory.vue'
export default {
components:{
HomeCon,
CateGory
},
data(){
return {
message:'hello vue',
show_cate_gory:true
}
},
created(){
// 事件监听
eventBus.on("whyEvent",(name,age,height)=>{
console.log("whyEvent事件在app中的监听",name,age,height)
this.message = `name:${name},age:${age},height:${height}`
})
}
}
</script>
<style scoped>
</style>
子组件 CateGory.vue
<template>
<div>
<h2>Category</h2>
</div>
</template>
<script>
import eventBus from './utils/event.bus.js'
export default {
methods:{
whyEventHandler(){
console.log("whyEvent在category中监听")
}
},
created(){
eventBus.on("whyEvent",this.whyEventHandler)
},
unmounted(){
console.log("category umounted")
eventBus.off("whyEvent",this.whyEventHandler)
}
}
</script>
<style scoped>
</style>
子组件 HomeBanner.vue
<template>
<div>
<button @click="bannerBtnClick">banner按钮</button>
</div>
</template>
<script>
import eventBus from './utils/event.bus';
export default {
methods:{
bannerBtnClick(){
console.log("bannerBtnClick")
eventBus.emit("whyEvent","why",18,1.88)
}
}
}
</script>
<style scoped>
</style>
子组件 HomeCon.vue
<template>
<div>
<home-banner></home-banner>
</div>
</template>
<script>
import HomeBanner from './HomeBanner.vue';
export default {
components:{
HomeBanner
},
data(){
return {
}
}
}
</script>
<style scoped>
</style>
事件总线 event.bus.js
import { HYEventBus } from "hy-event-store"
const eventBus = new HYEventBus()
export default eventBus
生命周期函数演练
App.vue
<template>
<div>
<h2>{{ message }}---{{ counter }}</h2>
<button @click="message = 'hello world'" >修改message</button>
<button @click="counter++">+1</button>
<button @click="showHome = !showHome">隐藏home</button>
<home-com v-if="showHome"></home-com>
</div>
</template>
<script>
import HomeCom from "./HomeCom.vue"
export default {
components:{
HomeCom
},
data(){
return {
message:'hello vue',
counter:1,
showHome:true
}
},
// 1.组件创建之前
beforeCreate(){
console.log("beforeCreate")
},
// 2.组件被创建完成
created(){
console.log("组件被创建完成:created")
console.log("1.发送网络请求,请求数据")
console.log("2.监听eventbus事件")
console.log("3.监听watch数据")
},
// 3. 组件template准备被挂在
beforeMount(){
console.log("beforeMount")
},
// 4.组件template被挂载,虚拟dom=》真实dom
mounted(){
console.log("mounted")
console.log("1.获取DOM")
console.log("2.使用DOM")
},
// 5.数据发生改变
// 5.1 准备更新DOM
beforeUpdate(){
console.log("beforeUpdate")
},
// 5.2 更新DOM
updated(){
console.log("updated")
},
// 6.卸载Vnode-》DOM原生
// 6.1 卸载之前
beforeUnmount(){
console.log("beforeUnmount")
},
// 6.2 DOM元素被卸载完成
unmounted(){
console.log("unmounted")
}
}
</script>
<style scoped>
</style>
子组件 HomeCom.vue
<template>
<div>
Home
</div>
</template>
<script>
export default {
unmounted(){
console.log("unmounted home")
},
beforeUnmount(){
console.log("beforeUnmout home")
}
}
</script>
<style scoped>
</style>
ref获取元素组件
App.vue
<template>
<div>
<h2 ref="title" class="title" :style="{color:titleColor}">{{ message }}</h2>
<button ref="btn" @click="changeTitle">修改title</button>
<BannerCom ref="banner"></BannerCom>
<BannerCom ref="banner"></BannerCom>
<BannerCom ref="banner"></BannerCom>
<BannerCom ref="banner"></BannerCom>
<BannerCom ref="banner"></BannerCom>
</div>
</template>
<script>
import BannerCom from './BannerCom.vue';
export default {
components:{
BannerCom
},
data(){
return {
message:"hello world",
titleColor:"red"
}
},
methods:{
changeTitle(){
// 1.不要主动去获取DOM,并且修改DOM内容
this.message = "你好啊,世界"
this.titleColor = "blue"
// 2.获取h2/button元素
console.log(this.$refs.title)
console.log(this.$refs.btn)
// 3.获取banner组件:组件实例
console.log(this.$refs.banner)
// 3.1在父组件中可以主动的调用子组件的方法
this.$refs.banner.bannerClick()
// 3.2 获取banner组件实例,获取banner中的元素
console.log(this.$refs.banner.$el)
// 3.3 如果banner template是多个根,拿到的是一个node节点
console.log(this.$refs.banner.$el.nextElementSibling)
// 第二个node节点
console.log(this.$refs.banner.$el.previousElementSibling)
// 4.组件实例了解
console.log(this.$parent); // 父组件
console.log(this.$root); //根组件
}
}
}
</script>
<style scoped>
</style>
子组件 BannerCom.vue
<template>
<div class="banner">
Banner
</div>
<div class="banner2"></div>
</template>
<script>
export default {
methods:{
bannerClick(){
console.log('bannerClick')
}
}
}
</script>
<style scoped>
</style>
内置组件的使用
App.vue
<template>
<div>
<button v-for="(item,index) in tab_arr" :style="default_tab == item ? 'color:red;' : ''" :key="index" @click="show_tab(item)">
{{ item }}
</button>
<!-- 第一中方法 -->
<!-- <div v-if="default_tab=='home'">
<HomeCom></HomeCom>
</div>
<div v-if="default_tab=='about'">
<AboutCom></AboutCom>
</div>
<div v-if="default_tab=='category'">
<CategoryCom></CategoryCom>
</div> -->
<!-- 第二种方式 -->
<component name="kb" age="20" @homebtn="homebtn" :is="default_tab"></component>
</div>
</template>
<script>
import HomeCom from './views/HomeCom.vue'
import AboutCom from './views/AboutCom.vue'
import CategoryCom from './views/CategoryCom.vue'
export default {
components:{
HomeCom,
AboutCom,
CategoryCom
},
data(){
return {
tab_arr:['HomeCom','AboutCom','CategoryCom'],
default_tab:'HomeCom'
}
},
methods:{
show_tab(item){
this.default_tab = item
},
homebtn(eve){
console.log('homebtn',eve)
}
}
}
</script>
<style scoped>
</style>
子组件 AboutCom.vue
<template>
<div>
about组件
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
子组件 CategoryCom.vue
<template>
<div>
category组件
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
子组件 HomeCom.vue
<template>
<div>
home组件{{ name }} --- {{ age }}
<button @click="homebtn">homebtn</button>
</div>
</template>
<script>
export default {
data(){
return {
}
},
props:{
name:{
type:String,
default:'why'
},
age:{
type:String,
default:'18'
}
},
emits:['homebtn'],
methods:{
homebtn(){
this.$emit('homebtn','home')
}
}
}
</script>
<style scoped>
</style>
Keep-Alive的使用-分包
App.vue
<template>
<div>
<button v-for="(item,index) in tab_arr" :style="default_tab == item ? 'color:red;' : ''" :key="index" @click="show_tab(item)">
{{ item }}
</button>
<!-- 第一中方法 -->
<!-- <div v-if="default_tab=='home'">
<HomeCom></HomeCom>
</div>
<div v-if="default_tab=='about'">
<AboutCom></AboutCom>
</div>
<div v-if="default_tab=='category'">
<CategoryCom></CategoryCom>
</div> -->
<!-- 第二种方式 -->
<KeepAlive include="HomeCom,AboutCom">
<component :is="default_tab"></component>
</KeepAlive>
</div>
</template>
<script>
import { defineAsyncComponent } from 'vue'
import HomeCom from './views/HomeCom.vue'
import AboutCom from './views/AboutCom.vue'
// import CategoryCom from './views/CategoryCom.vue'
const AsyncCategory = defineAsyncComponent(()=>import("./views/CategoryCom.vue"))
export default {
components:{
HomeCom,
AboutCom,
CategoryCom:AsyncCategory
},
data(){
return {
tab_arr:['HomeCom','AboutCom','CategoryCom'],
default_tab:'HomeCom'
}
},
methods:{
show_tab(item){
this.default_tab = item
},
homebtn(eve){
console.log('homebtn',eve)
}
}
}
</script>
<style scoped>
</style>
子组件 AboutCom.vue
<template>
<div>
about组件
</div>
</template>
<script>
export default {
name:"AboutCom",
created(){
console.log('about created')
},
unmounted(){
console.log('about unmounted')
},
}
</script>
<style scoped>
</style>
子组件 CategoryCom.vue
<template>
<div>
category组件
</div>
</template>
<script>
export default {
created(){
console.log('category created')
},
unmounted(){
console.log('category unmounted')
},
}
</script>
<style scoped>
</style>
子组件 HomeCom.vue
<template>
<div>
home组件
<div>
计数:{{ counter }}
</div>
<button @click="jia">加1</button>
</div>
</template>
<script>
export default {
name:"HomeCom",
data(){
return {
counter:0
}
},
created(){
console.log('home created')
},
unmounted(){
console.log('home unmounted')
},
// 对于保持keep-alive组件,监听有没有进行切换
// keep-alive组件进入活跃状态
activated(){
console.log('home activated')
},
deactivated(){
console.log('home deactivated')
},
methods:{
jia:function(){
this.counter++;
}
}
}
</script>
<style scoped>
</style>
组件的v-model
App.vue
<template>
<div class="app">
<!-- 1.input v-model -->
<!-- <input type="text" v-model="message"> -->
<!-- <input type="text" :value="message" @input="message = $event.target.value"> -->
<!-- <CounterCom :modelValue="appCounter" @update:modelValue="appCounter = $event"></CounterCom> -->
<!-- <CounterCom v-model="appCounter"></CounterCom> -->
<CounterCom2 v-model:counter="appCounter" v-model:why="why"></CounterCom2>
</div>
</template>
<script>
import CounterCom2 from './CounterCom2.vue'
export default {
components:{
CounterCom2
},
data(){
return {
message:'hello',
appCounter:"100",
why:'coderwhy'
}
}
}
</script>
<style scoped>
</style>
子组件 CounterCom.vue
<template>
<div>
counter
<div>
modelValue:{{ modelValue }}
</div>
<button @click="changeCounter">修改counter</button>
</div>
</template>
<script>
export default {
props:{
modelValue:{
type:String,
default:''
}
},
emits:["update:modelValue"],
methods:{
changeCounter(){
this.$emit("update:modelValue",'999999')
}
}
}
</script>
<style scoped>
</style>
子组件 CounterCom2.vue
<template>
<div>
<div>
counter:{{ counter }}
</div>
<button @click="changeCounter">修改counter</button>
<div>
why:{{ why }}
</div>
<button @click="changeWhy">修改why的值</button>
</div>
</template>
<script>
export default {
props:{
counter:{
type:String,
default:''
},
why:{
type:String,
default:''
}
},
emits:["update:counter","update:why"],
methods:{
changeCounter(){
this.$emit("update:counter",'999999')
},
changeWhy(){
this.$emit("update:why",'kobi')
}
}
}
</script>
<style scoped>
</style>
组件的混入Mixin
message-mixin.js
export default {
data(){
return {
message:"Hello World"
}
},
created(){
console.log("message:",this.message)
}
}
App.vue
<template>
<div>
<HomeCon></HomeCon>
<AboutCon></AboutCon>
<CatetoryCon></CatetoryCon>
</div>
</template>
<script>
import HomeCon from './views/HomeCon.vue'
import AboutCon from './views/AboutCon.vue'
import CatetoryCon from './views/CatetoryCon.vue'
export default {
components:{
HomeCon,
AboutCon,
CatetoryCon
}
}
</script>
<style scoped>
</style>
子组件 AboutCon.vue
<template>
<div>
<h2>AboutCon组件</h2>
</div>
</template>
<script>
import messageMixin from '../mixins/message-mixin'
export default {
mixins:[messageMixin]
}
</script>
<style scoped>
</style>
子组件 CatetoryCon.vue
<template>
<div>
<h2>CatetoryCon组件</h2>
</div>
</template>
<script>
import messageMixin from '../mixins/message-mixin'
export default {
mixins:[messageMixin]
}
</script>
<style scoped>
</style>
子组件 HomeCon.vue
<template>
<div>
<h2>HomeCon组件</h2>
</div>
</template>
<script>
import messageMixin from '../mixins/message-mixin'
export default {
mixins:[messageMixin]
}
</script>
<style scoped>
</style>
感谢观看,我们下次再见