从Vue组件传值到封装

1,307 阅读3分钟

以element ui为例,封装组件在项目中最为常见,代码的复用,并且可以把代码行数压缩,提升代码的阅读。

vue组件传值

父传子

父组件:通过绑定在子组件的信息,传递给子组件,当父组件data中没有对用的值时,子组件触发对应default

<template>
    <div>
        <Chile :info="msg"></Child>
    </div>
</template>

<script>
    import Child from 'child.vue'

    export default {
        name: 'father',
        components: {
            Child,
        },
        data () {
            return {
                msg: "传给子组件的信息"
            }
        }
    }
</script>

子组件:在子组件中以props进行接收,可以定义传输的类型,当没有传值时执行default

<template>
    <div>{{info}}</div>
</template>

<script>
    export default {
        name: 'child',
        props:{
            info: {
                type: String, //可设置传输数据的类型
                default () { // 当父组件没有数据传递时
                    return '没有传值时显示的值' 
                }
            }
        }
    }
</script>

子传父

父组件

<template>
    <div>
        <Chile @Tofather="fn"></Child>
        <div>{{val}}</div>
    </div>
</template>

<script>
    import Child from 'child.vue'

    export default {
        name: 'father',
        components: {
            Child
        },
        data () {
            return {
                val: ""
            }
        },
        methods: {
            fn (info) {
                this.val = info;
            }
        }
    }
</script>

子组件

<template>
    <div>
        <button @click="toFather">发送信息到父组件</button>
    </div>
</template>

<script>
    export default {
        name: 'child',
        data () {
            return {
                msg: "子组件传给父组件的信息"
            }
        },
        methods: {
            toFather () {
                this.$emit("ToFather", this.msg)
            }
        }
    }
</script>

非父子组件传值

通过创建公共的vue实例,进行传值 创建busEvent.js

import Vue from 'vue';
var bus = new Vue();
export default bus;

在传递值的组件中引入busEvent.js,传值一方使用$emit传值,接受方使用$on进行接收: A,B为非父子组件

// A组件
<template>
    <div>
        <button @click="handleToB">toB</button>
    </div>
</template>

<script>
    import Bus from '../busEvent.js';
    
    export default {
        name: 'A',
        data () {
            return {
                msg: "A传给B的信息"
            }
        },
        methods: {
            handleToB () {
                Bus.$emit("toB", this.msg)
            }
        }
    }
</script>
// B组件
<template>
    <div>
        {{msg}}
    </div>
</template>

<script>
    import Bus from '../busEvent.js';
    
    export default {
        name: "B",
        data () {
            return {
                msg: ""
            }
        },
        created() {
            Bus.$on("toB", (data) => {
                this.msg = data;
            })
        }
    }
</script>

组件方法的调用

组件嵌套级别不升的情况下,可以使用$refs来调用

<template>
    <div>
        <Demo ref="Demo"></Demo>
        <button @click="handleDemo">使用Demo组件中的方法</button>  
    </div>
</template>

<script>
    import Demo from '../demo';
    
    export default {
        components: {
            Demo,
        },
        methods: {
            handleDemo() {
                this.$refs.Demo.getDemo(); // getDemo(),为Demo组件的方法
            }
        }
    }
</script>

当嵌套级别深的时候,比如在APP.vue想使用组件中的方法,可以使用公共的vue实例 :busEvent.js

// demo.vue
<script>
    import Bus from '../busEvent.js';
    
    export default {
        methods: {
            handleAlert() {
                alert('调用成功');    
            }
        },
        created() {
            Bus.$on('alert', this.handleAlert); //绑定方法的时候不要加括号,前面的方法名不可重复,唯一的
        }    
    }
</script>
// APP.vue   举例使用APP.vue,在项目中代表嵌套级别升的组件
<template>
    <div>
        <button @click="handle">使用任意组件中的方法</button>  
    </div>
</template>

<script>
    import Bus from '../busEvent.js';
    
    export default {
        methods: {
            handle() {
                Bus.$emit('alert');    
            }
        },
    }
</script>

vue组件封装

首先要考虑组件的复用性,一个好的组件可以让项目代码变得非常简洁。
就拿目前我在维护的项目为例,基本页面构成是由顶部的搜索和底部的表格组成,如果把两个封装成组件,这样满足复用并且还符合简洁的标准。

组件封装其实就是运用父子组件之间传值,通过在子组件中的props中定义属性来控制对子组件的使用。
以element-ui的table为例:

//table 组件的封装
<template>
    <el-table
        :data="tableData"
        style="width: 100%">
        <el-table-column
            v-for="col in column"
            :key="col.id"
            :prop="col.id"
            :label="col.label"
            :width="col.width">
            //当表格中有些需要设置小数或者千分位的时候设置
            <template slot-scope="scope">
                <div v-if="needId.includes(col.id)">
                    {{setNumber(scope.row[col.id])}}
                </div>
                <div v-else>
                    {{scope.row[col.id]}}
                </div>
            </template>
        </el-table-column>
    </el-table>
</template>

<script>
export default {
    name: childTable,
    data() {
        return {
            tableData: [],
        };
    },
    //父组件传给子组件的属性,用来控制组件的使用
    props: {
        url: {
            type: String,
        },
        //表格信息传入
        column: {
            type: Array,
            default() {
                return [];  
            },
        },
        // 根据 needId 来判断那条数据需要做处理
        needId: {
            type: Array,
            default() {
                return [];  
            },
        },
        refreshBool: {
            type: Boolean,
            default() {
                return true;  
            },
        },
    },
    methods: {
        //请求数据,根据自己项目中的
        getData() {
            this.$ajax({
                url: this.url,
                method: 'get',
            }).then((res) => {
               this.tableData = res.list;
            });
        },
    },
    created: {
        //根据 refreshBool 来判断是否获取数据是否获取
        if(this.refreshBool) {
            this.getData();
        }
    },
}
</script>
//在父组件中调用
<template>
    <div>
        <childTable :column="column" :url="'xx/xx/xx'" :needId="['editNumber']"></childTable>
    </div>
</template>

<script>
import childTable from './childTable'; 

export default {
    components: {
        childTable,
    },
    data() {
        return {
            column: [
                {
                    prop: 'name',
                    lable: '姓名',
                    width: 'auto',
                },
                {
                    prop: 'age',
                    lable: '年龄',
                    width: 'auto',
                },
                {
                    prop: 'editNumber',
                    lable: '处理的数字',
                    width: 'auto',
                },
            ],
        };
    },
};
</script>

只需要在父组件中在子组件标签中添加或者删除属性就可以实现对组件的控制,但有时表格中有固定列,用来操作每行的功能,虽然可以在组件中添加属性来控制,但还是推荐使用 slot;

slot分为匿名和具名两种:

//childTable 中
<template>
    <el-table
        :data="tableData"
        style="width: 100%">
        <el-table-column
            v-for="col in column"
            :key="col.id"
            :prop="col.id"
            :label="col.label"
            :width="col.width">
            //当表格中有些需要设置小数或者千分位的时候设置
            <template slot-scope="scope">
                <div v-if="needId.includes(col.id)">
                    {{setNumber(scope.row[col.id])}}
                </div>
                <div v-else>
                    {{scope.row[col.id]}}
                </div>
            </template>
            //匿名插槽每个组件中只能有一个
            <slot></slot>
            //具名插槽
            <slot name="operation"></slot>
        </el-table-column>
    </el-table>
</template> 
//在父组件中
<template>
    <div>
        <childTable :column="column" :url="'xx/xx/xx'" :needId="['editNumber']">
            <el-table-column
                //使用具名插槽时使用 slot,匿名插槽不需要
                slot="operation"
                fixed="right"
                label="操作"
                width="100">
                <template slot-scope="scope">
                    <el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button>
                    <el-button type="text" size="small">编辑</el-button>
                </template>
            </el-table-column>
        </childTable>
    </div>
</template>

使用 slot,可以方便调用操作的方法,不需要在组件中另外设置方法的调用,当需要重新刷新数据时,可以使用 $ref 获取 dom 调用组件中的 getDate() 方法来刷新数据。