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>
感谢观看,我们下次再见