002-组件

组件介绍

Vue 的核心概念之一,将需求划分为多个小模块,小模块通过组件来实现,各个组件之间耦合度低,复用性高,团队成员分配任务进行各个组件编写,其他开发人员只要知道当前组件需要的入参,即可使用。

全局组件

通过 Vue.component 注册全局组件,传入 template、data、methods 等属性,配置组件名称,即可通过组件名称使用组件。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="app">
        <!-- 全局注册组件 -->
        <component-one></component-one>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

    <script>
        // 全局注册组件
        Vue.component('component-one', {
            template: `<div id="componentOneTpl">
                        <span>{{text}}</span>
                    </div>`,
            data: function () {
                return {
                    text: 'componentOneText'
                }
            }
        });

        var app = new Vue({
            el: '#app',
            data: {},
            methods: {

            }
        });
    </script>
</body>

</html>

父子组件传值

  1. 父组件传值给子组件 – 父组件属性绑定传值,子组件 props 接收

    <div id="app">
        <component-son :father-msg="msg"></component-son>
        <!-- 子节点接受到了父节点的数据:father message -->
    </div>
    
    // 子组件
    Vue.component('component-son', {
        template: `<div id="componentOneTpl">
                    <span>子节点接受到了父节点的数据:{{fatherMsg}}</span>
                </div>`,
        //props: ['fatherMsg'],
        props: {
            "fatherMsg": {
                type: String
            }
        },
    })
    
  2. 子组件传值给父组件 – 子组件 $emit 传值, 父组件 方法绑定,形参接收

    <div id="app">
        {{ sonMsg }}    <!-- i am son message !!! -->
        <component-son @send="accectSonMsg"></component-son>
    </div>
    
    // 子组件
    Vue.component('component-son', {
        template: `<div id="componentOneTpl">
                    <button @click="send()">发送给父节点数据</button>
                </div>`,
        data: function () {
            return {
                sonMsg: 'i am son message !!!'
            }
        },
        methods: {
            send() {
                this.$emit('send', this.sonMsg);
            }
        }
    
    })
    
    var app = new Vue({
        el: '#app',
        data: {
            sonMsg: '',
        },
        methods: {
            accectSonMsg(data) {
                console.log(data);
                this.sonMsg = data;
            }
        }
    });
    

bus 总线进行传值

bus 总线,类似于 angularjs 里面的 $rootScope ,任一处触发 $emit() ,所有 $on 都会响应;

  1. var Bus = new Vue(); 将 Bus 挂载到 根实例的 data 属性上;
  2. 采用 this.$root.Bus.$emit() 进行触发;
  3. 采用 this.$root.Bus.$on() 进行接收;
<div id="app">
   <!-- bus 总线 -->
   父组件数据:{{message}}
   <component-bus></component-bus>
   <component-bus2></component-bus2>
</div>
//  bus
 Vue.component('component-bus', {
     template: `<div>
         <button @click="emitBus()">触发bus事件</button>
         </div>`,
     data: function () {
         return {
             text: 'bus 触发的数据'
         }
     },
     methods: {
         emitBus() {
             this.$root.Bus.$emit('on-message', this.text);
         }
     }
 })

 //  bus 2
 Vue.component('component-bus2', {
     template: `<div>平行组件数据监听:{{text}}</div>`,
     data: function () {
         return {
             text: 'bus2'
         }
     },
     mounted() {
         var _this = this;
         this.$root.Bus.$on('on-message', function (msg) {
             _this.text = msg;
         })
     }
 })

 var Bus = new Vue();

 var app = new Vue({
     el: '#app',
     data: {
         Bus,
         message: '采用bus总线进行监听',
     },
     
     mounted() {
         var _this = this;
         this.$root.Bus.$on('on-message', function (msg) {
             _this.message = msg;
         })
     }
 });

v-model 在组件中的使用

组件通过 props: [‘value’] 接收值;
通过 this.$emit(‘input’, event.target.value) 发送值;

<div id="app">
    <div>v-model 父组件:{{modelInputText}}</div>
    <component-model v-model="modelInputText"></component-model>
</div>
Vue.component('component-model', {
    template: `<div><input type="text" :value="value" @input="updateVal" /><br /><span>v-model的子组件:{{value}}</span></div>`,
    props: ['value'],
    data: function () {
        return {
            text: 'componentOneText'
        }
    },
    methods: {
        updateVal(event) {
            this.$emit('input', event.target.value);
        }
    }
})

var app = new Vue({
    el: '#app',
    data: {
        modelInputText: 'hello v-model !',
    },
});

组件高级用法 slot、 name

slot : 插槽,将引用组件内容放进插槽中;
name: 给组件命名,递归组件使用;

<div id="app">
    <component-high :count="1">
        <span>多个数据</span>
    </component-high>
    <!--  结果展示:
        多个数据
        多个数据
        多个数据
        多个数据
        多个数据
     -->
</div>
Vue.component('component-high', {
    name: 'component-high',
    template: `<div><component-high v-if="count < 5" :count="count + 1"><slot></slot></component-high><slot></slot></div>`,
    props: {
        "count": {
            type: Number,
            default: 2
        }
    }
})

自定义树组件

转载树组件链接:https://blog.csdn.net/u014399368/article/details/87856322
内容简单易懂,很适合组件研究,学习使用。
先上效果图:
在这里插入图片描述
代码及注解如下,对应图片请自行截取,或通过原博客获取。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        h4 {
            font-weight: normal;
            color: #aaa;
            margin-top: 20px;
            line-height: 20px;
            font-size: 14px;
        }

        .line {
            margin: 20px 0;
        }

        #app {
            padding: 20px;
        }

        .cus_vtree_wrap>ul:first-child {
            background-image: none;
        }

        ul.cus_tree_ul {
            padding-left: 16px;
            background-image: url('./images/index.gif');
            background-position: 4px 10px;
            background-repeat: repeat-y;
        }

        li:last-child>ul.cus_tree_ul:last-child {
            background-image: none;
        }

        ul.cus_tree_ul li {
            text-align: left;
            cursor: pointer;
            list-style: none;

        }

        .cus_item_content span.treeExpandBtn {
            display: inline-block;
            width: 18px;
            height: 18px;
            margin-right: 6px;
            background-image: url('./images/zTreeStandard.png');
            margin: 0;

        }

        .cus_vtree_wrap>ul>li:first-child>.cus_item_content>span.treeExpandBtn.butopen {
            background-position: -92px 0px
        }

        li .cus_item_content span.treeExpandBtn.butopen {
            background-position: -92px -18px
        }

        li:last-child .cus_item_content span.treeExpandBtn.butopen {
            background-position: -92px -36px
        }

        .cus_vtree_wrap ul.tree_root_lonely>li>.cus_item_content>span.treeExpandBtn.butopen {
            background-position: -92px -54px
        }

        .cus_vtree_wrap>ul>li:first-child>.cus_item_content>span.treeExpandBtn.btnclose {
            background-position: -74px -0px
        }

        li .cus_item_content span.treeExpandBtn.btnclose {
            background-position: -74px -18px
        }

        li:last-child .cus_item_content span.treeExpandBtn.btnclose {
            background-position: -74px -36px
        }

        .cus_vtree_wrap ul.tree_root_lonely>li>.cus_item_content>span.treeExpandBtn.btnclose {
            background-position: -74px -54px
        }

        .cus_item_content span.treeExpandBtn.lastLine {
            background-position: -56px -36px
        }

        .cus_item_content span.treeExpandBtn.line {
            background-position: -56px -18px
        }

        .cus_item_content .cus_chekcbox {
            display: inline-block;
            width: 14px;
            height: 14px;
            margin-right: 6px;
            background-image: url('./images/zTreeStandard.png');
            background-position: -0px -0px
        }

        .cus_item_content .cus_chekcbox.cus_chekcbox_checked {
            background-position: -14px -0px
        }

        .cus_item_content .cus_chekcbox.cus_chekcbox_part_checked {
            background-position: 0px -42px
        }

        ul.cus_tree_ul li .cus_item_content {
            padding: 4px;
            white-space: nowrap;
            overflow-x: hidden;
        }

        ul.cus_tree_ul li .cus_item_content:hover {
            background: #8f83e1;
            color: white
        }

        ul.cus_tree_ul li .cus_item_content.active {
            background: #7663f8;
            color: white;
        }

        .folder {
            display: inline-block;
            width: 16px;
            height: 16px;
            margin-right: 6px;
            background-image: url('./images/zTreeStandard.png');
            background-position: -110px -0px
        }

        .file {
            display: inline-block;
            width: 18px;
            height: 18px;
            margin-right: 6px;
            background-image: url('./images/zTreeStandard.png');
            background-position: -110px -30px
        }
    </style>
</head>

<body>
    <div id="app">

        <vue-tree :tree-data="treeData" :check-box="checkbox"></vue-tree>

    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

    <script>
        Vue.component('vue-tree', {
            template: `
            <div class='cus_vtree_wrap' @click.capture='clickNodeWrap'>
                    <v-tree-item :treeData='treeData' @clickNodeCom='clickNode' :checkBox='checkBox':class='{tree_root_lonely:treeData.length === 1}' @toggleCheckBox="checkBoxFun"></v-tree-item>
            </div>
            `,
            name: 'vue-tree',
            props: ['treeData', 'checkBox'],
            data() {
                return {
                    //userData:this.treeData
                }
            },
            mounted() {
                this.initData(true, true, true);
            },
            updated() {
                this.initData(true, true, true);
            },
            methods: {
                initData(expandInit, activeInit, checkedInit) {
                    var self = this;
                    var modifyDataFun = function (datas) {
                        if (datas) {
                            datas.forEach((m, index) => {
                                // 给数据添加展开、选中等
                                if (expandInit) {
                                    // 注意对象赋值写法,便于双向数据绑定生效
                                    self.$set(m, 'expand', true);
                                }
                                if (activeInit) {
                                    self.$set(m, 'active', false);
                                }
                                if (checkedInit) {
                                    self.$set(m, 'checked', false);
                                    self.$set(m, 'partchecked', false);
                                }
                                if (index === datas.length - 1) {
                                    self.$set(m, 'last', true);
                                }
                                if (m.children) {
                                    // 递归调用创建
                                    modifyDataFun(m.children);
                                }
                            });
                        }
                    }
                    // 接收外界传递的 tree 数据进行初始化
                    modifyDataFun(this.treeData);
                },
                clickNode(data) {
                    this.$emit('clickNode', data);
                },
                clickNodeWrap() {
                    this.initData(false, true, false);
                },
                checkBoxFun(item) {

                },
                getCheckedNodes() {
                    var resultArr = [];
                    var getCheckedNodesFun = (datas) => {
                        if (datas) {
                            datas.forEach((m) => {
                                if (m.checked === true) {
                                    resultArr.push(m);
                                }
                                if (m.children) {
                                    getCheckedNodesFun(m.children);
                                }
                            })
                        }
                    };
                    getCheckedNodesFun(this.treeData);
                    return resultArr;
                },
                getRoot() {
                    return this.treeData[0];
                },
                findNode(selectedId) {
                    var result;
                    var findNodeFun = (datas) => {
                        if (datas) {
                            try {
                                datas.forEach((m) => {
                                    if (m.id === selectedId) {
                                        result = m;
                                        throw new Error('stop');
                                    }
                                    if (m.children) {
                                        findNodeFun(m.children);
                                    }

                                });
                            } catch (e) {

                            }

                        }
                    };
                    findNodeFun(this.treeData);
                    return result;
                },
                setSelectedNode(node, clickDiv) {
                    var select;
                    var that = this;
                    var setSelectedFun = (datas) => {
                        if (datas) {
                            datas.forEach((m) => {
                                if (m.id === node.id) {
                                    m.active = true;
                                    select = m;
                                    if (clickDiv) {
                                        var dom = document.getElementById(m.id);
                                        if (dom && dom.className.indexOf('cus_item_content') > -1) {
                                            dom.click();
                                        } else {
                                            var doms = document.getElementsByClassName(
                                                'cus_item_content');
                                            for (var y = 0; y < doms.length; y++) {
                                                if (doms[y].id === m.id) {
                                                    doms[y].click();
                                                    break;
                                                }
                                            }
                                        }

                                    }
                                } else {
                                    m.active = false;
                                }
                                if (m.children) {
                                    setSelectedFun(m.children);
                                }
                            });
                        }
                    };
                    setSelectedFun(this.treeData);
                    return select;
                },
                getSelectedNode() {
                    var resultNode = null;
                    var getSelectedFun = (datas) => {
                        if (datas) {
                            datas.forEach((m) => {
                                if (m.active === true) {
                                    resultNode = m;
                                    return;
                                }
                                if (m.children) {
                                    getSelectedFun(m.children)
                                }
                            })
                        }
                    };
                    getSelectedFun(this.treeData);
                    return resultNode;
                },
                getParentNode(node) {
                    var resultNode = null;
                    var getParentNode = (datas, parent) => {
                        if (datas) {
                            try {
                                datas.forEach((m) => {
                                    if (node && m.id === node.id) {
                                        resultNode = parent;
                                        throw new Error("Stop");
                                    }
                                    if (!resultNode && m.children) {
                                        getParentNode(m.children, m);
                                    }
                                });
                            } catch (e) {

                            }

                        }
                    }
                    getParentNode(this.treeData, null);
                    return resultNode;
                },
                getParentNodesArr(node) {
                    var resultArr = [];
                    var parentNode;
                    var getParent = (target) => {
                        parentNode = this.getParentNode(target);
                        if (parentNode) {
                            resultArr.push(parentNode);
                            getParent(parentNode);
                        }
                    };
                    getParent(node);
                    return resultArr;

                }
            },
            components: {
                'v-tree-item': {
                    name: 'v-tree-item',
                    template: '<ul class="cus_tree_ul" :class="{cus_tree_ulLine:(treeData && treeData.length)}"><li v-for="item in treeData"><div class="cus_item_content" :title="item.text" @click="clickNode(item)" :id= item.id   :class="{active:item.active}">' +
                        '<span class="treeExpandBtn" @click.stop="toggleNode(item)" :class="{butopen:item.expand && item.children,btnclose:!item.expand && item.children,line: !item.last && !item.children,lastLine:item.last&&!item.children}"></span><span class="tree-icon" :class="item.icon"></span>' +
                        '<span v-if="checkBox" @click="checkBoxClick(item)" class="cus_chekcbox" :class="{cus_chekcbox_checked:item.checked,cus_chekcbox_part_checked:item.partchecked}"></span>{{item.text}}</div>' +
                        '<v-tree-item :treeData="item.children"  v-if="item.expand" @clickNodeCom="clickNodeCom"  :checkBox="checkBox" :class="{cus_checkbox_allchecked:item.checked}" @toggleCheckBox="checkBoxFun(item)" ></v-tree-item> </li></ul>',

                    methods: {
                        clickNode(item) {
                            item.active = true;
                            this.$emit('clickNodeCom', item);
                        },
                        toggleNode(item) {
                            item.expand = !item.expand;
                            item.active = true;
                        },
                        clickNodeCom(data) {
                            this.$emit('clickNodeCom', data);
                        },
                        checkBoxClick(item) {

                            item.partchecked = false;
                            item.checked = !item.checked;
                            //设置子元素是否勾选
                            var checkChildFun = (childrenDatas) => {
                                childrenDatas.forEach((m) => {
                                    m.checked = item.checked;
                                    if (m.children) {
                                        checkChildFun(m.children);
                                    }
                                })
                            }
                            if (item.children) {
                                checkChildFun(item.children);
                            }


                            this.$emit('toggleCheckBox');
                        },
                        checkBoxFun(item) {
                            var checkedNum = 0;
                            var partCheckedNum = 0;
                            item.children.forEach((li) => {

                                if (li.checked === true) {
                                    checkedNum++;
                                } else if (li.partchecked === true) {
                                    partCheckedNum++;
                                }
                            })
                            if (checkedNum === item.children.length) { //全选
                                item.checked = true;
                                item.partchecked = false;
                            } else if (checkedNum > 0 || partCheckedNum > 0) { //部分勾选
                                item.checked = false;
                                item.partchecked = true;
                            } else { //没有勾选
                                item.checked = false;
                                item.partchecked = false;
                            }

                            this.$emit('toggleCheckBox')
                        },

                    },
                    props: ['treeData', 'checkBox']
                }
            }
        })

        var app = new Vue({
            el: '#app',
            data: {
                treeData: [{
                    text: 'xiaoming',
                    id: '1',
                    icon: 'folder',
                    children: [{
                            text: '1-1',
                            id: '1-1',
                            icon: 'folder',
                            children: [{
                                    text: '1-1-1',
                                    id: '1-1-1',
                                    icon: 'file',
                                },
                                {
                                    text: '1-1-2',
                                    id: '1-1-2',
                                    icon: 'file'
                                }
                            ]
                        },
                        {
                            text: '1-2',
                            id: '1-2',
                            icon: 'folder',
                        }
                    ]
                }],
                checkbox: true
            },
        });
    </script>
</body>

</html>

总结

  1. 组件部分主要包括:组件注册、组件之间传值,组件逻辑编写等;
  2. 组件 component 配合 路由 vue-router 即可实现组件切换,单页面应用程序;
  3. 简单组件传值通过父子组件传值、bus总线传值可满足需求,复杂逻辑组件传值需要使用 vuex ;
  4. 组件设计需要关注点:入参必须明确,扩展性必须强,逻辑代码注解说明,复杂代码抽离等;
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值