首先你完成了如下:
Vite+vue3+Ts+pinia实战(一:初始、基础安装、踩坑)
Vite+vue3+Ts+pinia开发(二:路由、pinia、UI库安装)
今天就讲讲父子通讯、兄弟通讯,以及ref、reactive的简单使用吧。
父传子Ref基础的HelloWorle.vue就有就不多说了。
初始目录:
image.png一、在components下建一个组件,我这里叫header吧
// components/Header/index.vue
<template>
<div class="header">
<div class="tabs-list">
<div
class="tab-item"
v-for="(item,inx) of tabs"
:key="inx"
:class="{'active':cutTab == item.code}"
@click="tabChange(item)"
>{{ item.label }}</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
const tabs = reactive([{
label: "物品查询",
code: "item"
},
{
label: "怪物查询",
code: "mon"
},
{
label: "地图查询",
code: "map"
},
{
label: "Npc查询",
code: "npc"
}])
const cutTab = ref('item')
const tabChange = (item: any) => {
if (item.code == cutTab.value) {
return
}
cutTab.value = item.code;
}
</script>
<style scoped lang="scss">
.header {
height: 60px;
background: #20a0ff;
padding-left: 30px;
padding-right: 20px;
.tabs-list {
height: 60px;
float: left;
.tab-item {
height: 60px;
line-height: 60px;
float: left;
box-sizing: border-box;
color: #fff;
padding: 0 20px;
font-size: 15px;
cursor: pointer;
border-radius: 4px;
position: relative;
transition: all 0.3s ease;
&.active::after {
display: block;
}
&::after {
// color: #409eff;
content: "";
position: absolute;
height: 4px;
width: 90%;
left: 5%;
bottom: 0;
border-radius: 4px 4px 0 0;
background: #ff4500;
display: none;
}
&:hover {
&::after {
display: block;
}
}
}
}
}
</style>
从上面可以看到,Vue 3.0和2.0开发的不同。
3.0支持从上到下一路开发。或者说分段式开发。而不再像2.0需要先定义data什么的,然后再methods里写方法。
3.0其实也是有自己的生命周期
的,如有需要可以使用。
beforeCreate ===>setup()
created =======>setup()
beforeMount ===> onBeforeMount
mounted=======> onMounted
beforeUpdate ===> onBeforeUpdate
updated =======> onUpdated
beforeUnmount ==>onBeforeUnmount
unmounted=====> onUnmounted
Demo.vue代码:
<template>
<h2>当前求和为:{{sum}}</h2>
<button @click="sum++">点我+1</button>
</template>
<script>
import { ref,onBeforeMount,onMounted,onBeforeUpdate,onBeforeUnmount,onUnmounted } from 'vue'
export default {
name:'Demo',
setup(){
console.log('--setup--')
//数据
let sum = ref(0)
//通过组合式API的形式去使用生命周期钩子
onBeforeMount(()=>{
console.log('--onBeforeMount--')
})
onMounted(()=>{
console.log('--onMounted--')
})
onBeforeUpdate(()=>{
console.log('--onBeforeUpdate--')
})
onBeforeUnmount(()=>{
console.log('--onBeforeUnmount--')
})
onUnmounted(()=>{
console.log('--onUnmounted--')
})
//返回一个对象(常用)
return { sum }
},
//通过配置项的形式使用生命周期钩子
beforeCreate(){
console.log('---beforeCreate---')
},
created(){
console.log('--created--')
},
beforeMount(){
console.log('--beforeMount--')
},
mounted(){
console.log('--mounted--')
} ,
beforeUpdate(){
console.log('--beforeUpdate--')
},
updated(){
console.log('--updated--')
},
beforeUnmount(){
console.log('--beforeUnmount--')
},
unmounted(){
console.log('--unmounted--')
},
}
</script>
二 ref、reactive的使用
这里说下大概意思吧,定义字符串、布尔、数字类型用ref,定义对象、数组、数组对象用reactive
使用说明:
· ref定义的数据:操作数据需要.value,读取数据时模版中直接读取不需要.value
· reactive定义的数据:操作数据与读取数据:均不需要.value
let num = ref(0)
let string = ref('测试')
let loading = ref(false);
let user = reactive({
name:"zs",
age:18
})
// 使用场景
const changeValue = ()=>{
num.value = 1;
string.value = '新值';
loading.value = true;
user.age = 19
}
// 这里需要注意的是,如果定义的是数组,要清空或者说重新赋值场景如下
let tableData = reactive([]);
const getList = () => {
axios....(res => {
tableData.length = 0;
Object.assign(tableData, res.data.data.list);
})
}
三、回归正题,新建一个兄弟组件,Content
// components/Content/index.vue
<template>
<div class="content">content组件</div>
</template>
<script setup lang="ts">
</script>
<style scoped lang="scss">
.content {
box-sizing: border-box;
height: calc(100% - 60px);
min-height: 399px;
padding: 20px 20px 10px;
}
</style>
四、 修改App.vue 并且引入该组件
<script setup lang="ts">
// This starter template is using Vue 3 <script setup> SFCs
// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup
import Header from './components/Header/index.vue';
import Content from './components/Content/index.vue';
</script>
<template>
<Header />
<Content />
</template>
<style scoped>
</style>
下面看我们看效果
GIF.gif
五、兄弟通讯
5.1 这里我们希望顶部组件切换,内容组件可以获取到。并且根据不同的Code做不同的操作。
修改Header/index.vue
,引入onMounted
生命周期,以及emit
<script setup lang="ts">
import { ref, reactive, onMounted, defineEmits } from 'vue';
const tabs = reactive([{
label: "物品查询",
code: "item"
},
{
label: "怪物查询",
code: "mon"
},
{
label: "地图查询",
code: "map"
},
{
label: "Npc查询",
code: "npc"
}])
const cutTab = ref('');
const emit = defineEmits(['tabChange'])
onMounted(() => {
tabChange(tabs[0]);
})
const tabChange = (item: any) => {
if (item.code == cutTab.value) {
return
}
cutTab.value = item.code;
emit('tabChange', cutTab.value)
}
</script>
5.2 从上面我可以看到他初始化了切换到第一个页签了,并且把当前的code导出去了。
下面我就修改我们的app.vue
用于接收它
<template>
<Header @tabChange="tabChange" />
<Content />
</template>
<script setup lang="ts">
import Header from './components/Header/index.vue';
import Content from './components/Content/index.vue';
const tabChange = (v: any) => {
console.log(v)
}
</script>
<style scoped>
</style>
5.3 到这里,我们仅是父组件知道了header的变化。下面怎么告诉Content组件呢?
其实这时候是有2种方法,第一是将code传给Content,Content去监听这个值的变化做对应的处。
方法2就是直接调用Content的方法进行操作。由于是教程,我这里就演示第2种。
修改Content/index.vue
<script setup lang="ts">
import { ref } from 'vue';
let cutTab = ref("");
// 接收到参数后进行你想要的处理
const tabChange = (code: string) => {
console.log("content:" + code);
cutTab.value = code;
}
// 导出这个方法
defineExpose({
tabChange
})
</script>
<style scoped lang="scss">
修改App.vue
<template>
<Header @tabChange="tabChange" />
<Content ref="RefContent" />
</template>
<script setup lang="ts">
// 1.0 引入ref
import { ref } from 'vue'
import Header from './components/Header/index.vue';
import Content from './components/Content/index.vue';
// 2.0 要使用子组件就要定义一个变量跟你上面定义的一样的ref
const RefContent: any = ref(null);
const tabChange = (v: any) => {
// console.log(RefContent)
RefContent.value.tabChange(v);
}
</script>
看结果:
image.png
结语:
今天这一篇,学习了兄弟通讯,A组件 通过 defineEmits(2.0的$emit)导出方法。B组件通过 defineExpose 将它自身的方法暴露出来供 父组件(Ref) 调用,来达到父子通讯、兄弟通讯。
我们还了解了生命周期,以及ref、reactive的初步使用。
网友评论