vue父、子、孫組件間數據傳遞、事件傳遞

怎麼實現父子組件的數據傳遞、事件傳遞,有哪幾種方式

1、父獲取子組件數據事件能夠經過下面幾種方式:
css

一、經過給子組件綁定ref屬性,獲取子組件實例

二、經過this.$children獲取子組件實例

三、經過vuex完成父子組件數據事件通訊
複製代碼

2、子獲取父組件數據事件能夠經過下面幾種方式:
html

一、經過props傳遞父組件數據、事件, 或者經過$emit和$on實現事件傳遞

二、經過ref屬性,調用子組件方法,傳遞數據;經過props傳遞父組件數據、事件, 或者經過$emit和$on實現事件傳遞

三、經過this.$parent.$data或者this.$parevent._data獲取父組件數據;經過this.$parent執行父組件方法

四、經過vuex完成父子組件數據事件通訊
複製代碼
<div class="demo-block">
    我是父組件內容,我有一個子組件名字叫{{childName1}},這是經過ref獲取的內容; <br>
    我是父組件內容,我有一個子組件名字叫{{childName2}},這是經過children獲取的內容; <br>
    <component-a ref="childA" :message1="msg1" :message2="msg2" :fn="setFn"></component-a><br>
    <button @click="getName1()">refs獲取子組件名稱</button>
    <button @click="getName2()">children獲取子組件名稱</button>
    <button @click="setChild">觸發子組件事件寫入數據</button>
</div>
複製代碼
<script>
    var componentA = {
        name: 'child1',
        template: `<div><div>我是子組件child:{{message1}}-{{message2}},寫入數據{{con}}</div><button @click="fn1">經過props調用父組件方法</button> <button @click="fn2">經過$parent調用父組件方法</button></div>`,
        data() {
            return {
                child: true,
                name: 'child1',
                con:''
            }
        },
        props: ['message1', 'message2', 'fn'],
        methods: {
            fn1() {
                typeof this.fn === 'function' && this.fn();
            },
            fn2() {
                this.$parent && this.$parent.setFn();
            },
            init(params) {
                this.con = params.msg1 + params.msg2;
                alert('我是子組件')
            }
        }
    };
    export default {
        components: {
            componentA
        },
        data() {
            return {
                msg1: 'hello',
                msg2: 'world',
                prevent: true,
                childName1: '',
                childName2: ''
            };
        },
        methods: {
            getName1() {
                this.childName1 = this.$refs.childA.name;
            },
            getName2() {
                this.childName2 = this.$children[0].name;
            },
            setChild() {
                this.$children[0].init({
                    msg1: 'world',
                    msg2: 'hello',
                });
            },
            setFn() {
                alert('我是父組件')
            }
        }
    }
</script>
複製代碼


怎麼實現兄弟組件之間的數據傳遞、事件傳遞,有哪幾種方式

一、利用Vuex存儲數據, 配合父組件watch進行事件監聽

二、利用bus中央開關總線,掛載全局事件

三、利用$parent進去數據傳遞, $parent.$children調用兄弟組件事件
複製代碼
<style> .tab-list { border-bottom: 1px solid #ccc; padding-bottom: 10px; } .tab-item { display: inline-block; width: 100px; text-align: center; border-right: 1px solid #ccc; } .tab-item.active { color: red; } </style>
<div class="demo-block">
    <component-b ref="childB" :listType.sync='transitType'></component-b><br>
    <component-c ref="childC"></component-c>
</div>
複製代碼
<script>
    var componentB = {
        template: `<div>
                      <div class="tab-list">
                        <span class="tab-item" v-for="(item, index) in list" @click="changeType(item.type)" :class="{'active': item.type === type}">{{item.value}}</span>
                      </div>
                   </div>`,
        data() {
            return {
                type: 0,
                list: [{value: '新聞', type: 0}, {value: '汽車', type: 1}, {value: '娛樂', type: 2}]
            }
        },
        props: ['listType'],
        methods: {
            changeType(type) {
                this.type = type;
                if(type === 0) { // 使用watch開關通知
                    this.$emit('update:listType', type)
                } else if(type === 1) { // 使用中央總線;
                    /*掛載全局觸發事件*/
                    window.Vue.$emit('switchType', type);
                    this.$emit('update:listType', type)
                } else { // 使用$parent.$children
                    // this.$parent.$children[1].$options.name 能夠獲取組件名稱
                    // this.$parent.$children[1].$options._componentTag 能夠獲取局部註冊的組件標籤名
                    this.$parent.$children[2].switchType && this.$parent.$children[2].switchType(type)
                    this.$emit('update:listType', type)
                }
            }
        }
    };
    var componentC = {
        name: 'childC',
        template: `<div>
                        <div v-for="(item, index) in listInfo[listType]">{{item}}</div>
                   </div>`,
        data() {
            return {
                listType: 0,
                listInfo: {
                    0: ['新聞1', '新聞2', '新聞3'],
                    1: ['汽車1', '汽車2', '汽車3'],
                    2: ['娛樂1', '娛樂2', '娛樂3']
                }
            }
        },
        props: [],
        methods: {
            switchType(type) {
                this.listType = type;
            }
        },
        mounted() {
            /*掛載全局觸發事件*/
            window.Vue.$on('switchType', (type) => {
                console.log(type, 'type');
                this.switchType(type);
            });
        }
    };
    export default {
        components: {
            componentB,
            componentC
        },
        data() {
            return {
                transitType: 0, // 這是一箇中轉控制屬性
            };
        },
        methods: {
        },
        mounted() {
            //
            this.transitType = this.$refs.childB.type;
            console.log(this.transitType)
        },
        watch: {
            transitType(newType, oldType) {
                if(newType === 0) {
                    this.$refs.childC.switchType && this.$refs.childC.switchType(newType);
                }
            }
        }
    }
</script>
複製代碼


怎麼實現祖孫組件之間的數據傳遞、事件傳遞,有哪幾種方式

一、利用$attrs實現祖孫組件間的數據傳遞,$listeners實現祖孫組件間的事件監聽
 $attrs包含了父做用域中不做爲 prop 被識別 (且獲取) 的特性綁定 (class 和 style 除外)。當一個組件沒有聲明任何 prop 時,這裏會包含全部父做用域的綁定 (class 和 style 除外),而且能夠經過 v-bind="$attrs" 傳入內部組件——在建立高級別的組件時很是有用。
$listeners包含了父做用域中的 (不含 .native 修飾器的) v-on 事件監聽器。它能夠經過 v-on="$listeners" 傳入內部組件——在建立更高層次的組件時很是有用。

二、利用provide/inject 實現祖孫組件間的數據傳遞和事件監聽
provide 和 inject 主要爲高階插件/組件庫提供用例,容許一個祖先組件向其全部子孫後代注入一個依賴,不論組件層次有多深,並在起上下游關係成立的時間裏始終生效。

三、利用vuex傳遞存儲數據,利用$parent、$children封裝一套可向下廣播事件,向上派發事件的方法;
複製代碼

第一種實現方式

<style> .mask-bg { position: fixed; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, .3); } .alert { position: fixed; left: 50%; top: 50%; width: 300px; height: 200px; border-radius: 10px; text-align: center; background-color: #fff; transform: translate(-50%, -50%); } </style>
<div class="demo-block">
    <div>
        <div @click="isShow = true" style="margin-bottom: 20px">顯示彈層(第一種方式)</div>
        <my-mask :show="isShow" :title="title" :con="con" @on-submit="submit"></my-mask><br>
    </div>
</div>
複製代碼
// 彈層組件(子組件的子組件)
var alert = {
    template: ` <div class="alert"> <h2>{{title}}</h2> <div class="alert-con"> {{con}} </div> <button class="submit" @click="submit">肯定</button> </div>`,
    data() {
        return {
        }
    },
    props: ['title', 'con'],
    methods: {
        submit() {
            this.$emit('on-submit')
        }
    }
};
// mask組件(子組件)
var myMask = {
    template: `<div class="mask" v-if="show"> <div class="mask-bg"></div> <alert v-bind="$attrs" v-on="$listeners"></alert> </div>`,
    components: {
        alert
    },
    inheritAttrs: false,
    data() {
        return {
        }
    },
    props: ['show'],
    methods: {
    },
    watch: {
    }
};
export default {
    name: 'my-page',
    components: {
        myMask,
    },
    data() {
        return {}
            },
            isShow: false,
            title: '我是標題',
            con:  '我是內容',
        };
    },
    methods: {
        submit() {
            window.alert('關閉彈層');
            this.isShow = false;
        },
    }
}
複製代碼

祖組件在傳遞數據給子組件的時候,若是子組件沒有經過props接收祖組件傳遞的參數,那麼這個數據會表如今子組件的根標籤的屬性上;經過設置 inheritAttrs 到 false,這些默認行爲將會被去掉; 而後咱們能夠在子組件綁定v-bind="attrs",將這些特性傳遞給孫組件,孫組件經過props接收數據; 同理咱們也能夠經過listeners實現祖孫之間的事件監聽器傳遞vue

第二種實現方式

<style> .tab-list { border-bottom: 1px solid #ccc; padding-bottom: 10px; } .tab-item { display: inline-block; width: 100px; text-align: center; border-right: 1px solid #ccc; } .tab-item.active { color: red; } </style>
<div class="demo-block">
    <div>
        <div @click="changeCon()" style="margin-bottom: 20px">切換顯示內容(第二種方式)</div>
        <my-con></my-con><br>
    </div>
</div>
複製代碼
var myTable = {
    template: `<div> <div class="tab-list"> <span class="tab-item" v-for="(item, index) in list" @click="switchType(item.type)" :class="{'active': item.type === store.state.type}">{{item.value}}</span> </div> </div>`,
    data() {
        return {
            tableType: 0,
            list: [{value: '新聞', type: 0}, {value: '汽車', type: 1}, {value: '娛樂', type: 2}]
        }
    },
    inject: ['store'],
    methods: {
        switchType(value) {
            this.store.commit('type', value)
        }
    }
};
var myList = {
    template: `<div> <div v-for="(item, index) in listInfo[store.state.type]">{{item}}</div> </div>`,
    data() {
        return {
            listType: 0,
            listInfo: {
                0: ['新聞1', '新聞2', '新聞3'],
                1: ['汽車1', '汽車2', '汽車3'],
                2: ['娛樂1', '娛樂2', '娛樂3']
            }
        }
    },
    inject: ['store'],
    props: [],
    methods: {
    },
    mounted() {
    }
};
var myCon = {
    template: `<div> <my-table></my-table> <my-list></my-list> </div>`,
    data() {
        return {
        }
    },
    components: {
        myTable,
        myList,
        mySearch
    },
    props: [],
    methods: {
    },
    mounted() {
    }
};

export default {
    name: 'my-page',
    components: {
        myCon,
    },
    provide() {
        return {
            store: this.store
        }
    },
    data() {
        return {
            store: {
                state: {
                    type: 0
                },
                commit (type, value) {
                    this.state[type] = value
                }
            },
        };
    },
    methods: {
        changeCon() {
            this.store.state.type ++;
            if(this.store.state.type > 2) this.store.state.type = 0;
        },
    },
    mounted() {
    }
}
複製代碼

第三種實現方式(來自餓了麼UI以前源碼)

<div class="demo-block">
    <div>
        <input type="text" placeholder="搜索(第三種方式)" class="input" @input="changInput"><span v-if="!hasSearch" style="display:inline-block; margin-bottom: 20px">請輸入內容</span>
        <my-con></my-con><br>
    </div>
</div>
複製代碼
var myCon = {
    template: `<div> <my-table></my-table> <my-list></my-list> <my-search></my-search> </div>`,
    data() {
        return {
        }
    },
    components: {
        myTable,
        myList,
        mySearch
    },
    props: [],
    methods: {
    },
    mounted() {
    }
};
var mySearch = {
    name: 'mySearch',
    template: `<div> <p style="margin: 20px auto">{{content}}</p> </div>`,
    data() {
        return {
            content: '沒有搜索內容'
        }
    },
    props: [],
    methods: {
        /*對多級父組件進行事件派發*/
        dispatch(componentName, eventName, params) {
            /*獲取父組件,若是以及是根組件,則是$root*/
            var parent = this.$parent || this.$root;
            /*獲取父節點的組件名*/
            var name = parent.$options.name;
            while (parent && (!name || name !== componentName)) {
                /*當父組件不是所需組件時繼續向上尋找*/
                parent = parent.$parent;
                if (parent) {
                    name = parent.$options.name;
                }
            }
            /*找到所需組件後調用$emit觸發當前事件*/
            if (parent) {
                parent.$emit.apply(parent, [eventName].concat(params));
            }
        },
    },
    mounted() {
        this.$on('on-input', (value) => {
            this.content = value;
            if(value == '') {
                this.content = '沒有搜索內容';
                this.dispatch('my-page', 'no-data', false);
            }
        })
    }
};
function broadcast(componentName, eventName, params) {
  /*遍歷當前節點下的全部子組件*/
  this.$children.forEach(child => {
    /*獲取子組件名稱*/
    var name = child.$options.name;

    if (name === componentName) {
      /*若是是咱們須要廣播到的子組件的時候調用$emit觸發所需事件,在子組件中用$on監聽*/
      child.$emit.apply(child, [eventName].concat(params));
    } else {
      /*非所需子組件則遞歸遍歷深層次子組件*/
      broadcast.apply(child, [componentName, eventName].concat([params]));
    }
  });
}
export default {
    name: 'my-page',
    components: {
        myMask,
        myCon,
    },
    provide() {
        return {
            store: this.store
        }
    },
    data() {
        return {
            store: {
                state: {
                    type: 0
                },
                commit (type, value) {
                    this.state[type] = value
                }
            },
            isShow: false,
            title: '我是標題',
            con:  '我是內容',
            hasSearch: false
        };
    },
    methods: {
        submit() {
            window.alert('關閉彈層');
            this.isShow = false;
        },
        changeCon() {
            this.store.state.type ++;
            if(this.store.state.type > 2) this.store.state.type = 0;
        },
        changInput(e) {
            if(e.target.value) {
                this.hasSearch = true;
            }
            this.broadcast('mySearch', 'on-input', e.target.value)
        },
        /*對多級父組件進行事件派發*/
        dispatch(componentName, eventName, params) {
            /*獲取父組件,若是以及是根組件,則是$root*/
            var parent = this.$parent || this.$root;
            /*獲取父節點的組件名*/
            var name = parent.$options.name;
            while (parent && (!name || name !== componentName)) {
                /*當父組件不是所需組件時繼續向上尋找*/
                parent = parent.$parent;
                if (parent) {
                    name = parent.$options.name;
                }
            }
            /*找到所需組件後調用$emit觸發當前事件*/
            if (parent) {
                parent.$emit.apply(parent, [eventName].concat(params));
            }
        },
        broadcast(componentName, eventName, params) {
            broadcast.call(this, componentName, eventName, params);
        }
    },
    mounted() {
        this.$on('no-data', (value) => {
            this.hasSearch = value;
        })
    }
}
複製代碼

broadcast經過遞歸遍歷子組件找到所需組件廣播事件,而dispatch則逐級向上查找對應父組件派發事件。 broadcast(componentName:(組件名),eventName:(事件名稱)以及params:(數據))。根據componentName深度遍歷子組件找到對應組件emit事件eventName。 dispatch(componentName:(組件名),eventName:(事件名稱)以及params:(數據))。根據componentName向上級一直尋找對應父組件,找到之後emit事件eventName。
經過這種方式進行數據、事件傳遞,必須給組件命名name;vuex

相關文章
相關標籤/搜索