美文网首页react & vue & angular
Vite+vue3+Ts+pinia开发(三:父子通讯、兄弟通讯

Vite+vue3+Ts+pinia开发(三:父子通讯、兄弟通讯

作者: 邪七 | 来源:发表于2022-09-21 16:58 被阅读0次

    首先你完成了如下:
    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的初步使用。

    相关文章

      网友评论

        本文标题:Vite+vue3+Ts+pinia开发(三:父子通讯、兄弟通讯

        本文链接:https://www.haomeiwen.com/subject/tziwortx.html