您的当前位置:首页>全部文章>文章详情

Vue3的手脚架使用和组件父子间通信-插槽(Options API)学习笔记

2019ab/CSDN发表于:2024-05-12 07:26:02浏览:96次TAG: #Vue

Vue CLI安装和使用

  1. 全局安装最新vue3
npm install @vue/cli -g
  1. 升级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>

感谢观看,我们下次再见

腾讯云采购季云服务器一折促销