laydate组件 无法传值_Vue 组件通讯实践

前言

组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。 组件一般有以下几种关系:

796ad89954941cc5a63a8786bb7e48b1.png

如上图所示,grandfather 和 parent、parent 和 childA、parent 和 childB 都是父子关系,childA 和 childB 是兄弟关系,grandfather 和 childA、childB 是隔代关系(可能隔多代)。  所以组件通讯是 vue.js 的核心之一,接下来结合代码,来了解各个组件是怎么通讯的。

1

props 和 $emit

子组件(Child.vue)的 props 属性能够接收来自父组件(Parent.vue)数据。没错,仅仅只能接收,props是单向绑定的,即只能父组件向子组件传递,不能反向。
// 父组件(Parent.vue)<template>    <div id="parent">        <Child :msg="msg" />    div>template><script>import Child from './Child'export default {    name: 'parent',    data() {        return {            msg: '这是来自父组件来的数据~~'        }    },    components: {        Child    }}script>// 子组件(Child.vue)<template>    <div id="child">        <div>{{ msg }}div>    div>template><script>export default {    name: 'child',    data() {        return {        }    },        props: {        msg: {            type: String        }    },    methods: {    }}script>
$emit 实现子组件向父组件传值(通过事件形式),子组件通过 $emit 事件向父组件发送消息,将自己的数据传递给父组件。
// 父组件<template>    <div id="parent">        <div>{{ msg }}div>        <Child2 @changeMsg="parentMsg" />    div>template><script>import Child2 from './Child2'export default {    name: 'parent',    data() {        return {            msg: ''        }    },    methods: {        parentMsg( msg ) {            this.msg = msg;        }    },    components: {        Child2    }}script>// 子组件<template>    <div id="child">        <button @click="childMsg">传递数据给父组件button>    div>template><script>export default {    name: 'child',    data() {        return {        }    },    methods: {        childMsg() {            this.$emit( 'changeMsg', '传递数据给粑粑组件' );        }    }}script>

总结:开发组件常用的数据传输方式,父子间传递。

2

$emit 和 $on

实现方式是通过创建一个空的 vue 实例,当做 $emit 事件的处理中心(事件总线),通过它来触发以及监听事件,来实现任意组件间的通信,包含父子,兄弟,隔代组件。
// 父组件<template>    <div id="parent">        <Child1 :Event="Event" />        <Child2 :Event="Event" />        <Child3 :Event="Event" />    div>template><script>import Vue from 'Vue';import Child1 from './Child1';import Child2 from './Child2';import Child3 from './Child3';// 公共的实例const Event = new Vue();export default {    name: 'parent',    data() {        return {            Event        }    },    components: {        Child1,        Child2,        Child3    }}script>// 子组件1<template>    <div id="child1">        1、她的名字叫:{{ name }}        <button @click="send">传递数据给Child3button>    div>template><script>export default {    name: 'child1',    data() {        return {            name: '柯基慧'        }    },    props: {        Event: Object    },    methods: {        send() {            this.Event.$emit( 'msgA', this.name );        }    }}script>// 子组件2<template>    <div id="child2">        1、她的身高:{{ height }}        <button @click="send">传递数据给Child3button>    div>template><script>export default {    name: 'child2',    data() {        return {            height: '149.9cm'        }    },    props: {        Event: Object    },    methods: {        send() {            this.Event.$emit( 'msgB', this.height );        }    }}script>// 子组件3<template>    <div id="child3">        <h3>她的名字叫:{{ name }},身高{{ height }}。h3>    div>template><script>export default {    name: 'child3',    data() {        return {            name: '',            height: ''        }    },    props: {        Event: Object    },    mounted() {        this.Event.$on( 'msgA', name => {            this.name = name;        } );        this.Event.$on( 'msgB', height => {            this.height = height;        } );    }}script>

总结:在父子、兄弟、隔代组件中都可以互相数据通信,重要的是 $emit 和 $on 事件必须是在一个公共的实例上才能触发。

3

$attrs 和 $listeners

Vue 组件间传输数据在 Vue2.4 版本后增加了新方法 $attrs 和 $listeners 。

1)$attrs

$attrs - 包含了父作用域中不作为 props 被识别 (且获取) 的特性绑定 ( class 和 style 除外)。 当一个组件没有声明任何 props 时,这里会包含所有父作用域的绑定 ( class 和style 除外),并且可以通过 v-bind="$attrs" 传入内部组件 - 在创建高级别的组件时非常有用。 简单点讲就是包含了所以父组件在子组件上设置的属性(除了 props 传递的属性、class 和 style )。 想象一下,你打算封装一个自定义input组件 - MyInput,需要从父组件传入 type,placeholder,title 等多个html元素的原生属性。 此时你的 MyInput 组件 props 如下:
props:['type', 'placeholder', 'title', ...]
如果它的属性越多,那子组件就要定义更多的属性,会很影响阅读,所以,$attrs 专门为了解决这种问题而诞生,这个属性允许你在使用自定义组件时更像是使用原生 html 元素。 比如:
// 父组件<template>    <div id="parentAttrs">        <MyInput placeholder="请输入你的姓名" type="text" title="姓名" v-model="name" />    div>template><script>import MyInput from './MyInput';export default {    name: 'parent',    data() {        return {            name: ''        }    },    components: {        MyInput    }}script>// 子组件<template>    <div>        <label>姓名:label>        <input v-bind="$attrsAll" @input="$emit( 'input', $event.target.value )" />    div>template><script>export default {    name: 'myinput',    data() {        return {}    },    inheritAttrs: false,    computed: {        $attrsAll() {            return {                value: this.$vnode.data.model.value,                ...this.$attrs            }        }    }}script>

2)$listener

$listeners - 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。 它可以通过 v-on="$listeners" 传入内部组件 - 在创建更高层次的组件时非常有用。 简单点讲它是一个对象,里面包含了作用在这个组件上所有的监听器(监听事件),可以通过 v-on="$listeners" 将事件监听指向这个组件内的子元素(包括内部的子组件)。 同上面 $attrs 属性一样,这个属性也是为了在自定义组件中使用原生事件而产生的。 比如要让前面的 MyInput 组件实现 focus 事件,直接这么写是没用的。
<template>    <div id="parentListener">        <MyInput @focus="focus" placeholder="请输入你的姓名" type="text" title="姓名" v-model="name" />    div>template><script>import MyInput from './MyInput';export default {    name: 'parent',    data() {        return {            name: ''        }    },    methods: {        focus() {            console.log( 'test' );        }    },    components: {        MyInput    }}script>

必须要让 focus 事件作用于 MyInput 组件的 input 元素上。

<template>    <div>        <label>姓名:label>        <input v-bind="$attrsAll" v-on="$listenserAll" />        <button @click="handlerF">操作testbutton>    div>template><script>export default {    name: 'myinput',    data() {        return {}    },    inheritAttrs: false,    props: ['value'],    methods: {        handlerF() {            this.$emit( 'focus' );        }    },    computed:{         $attrsAll() {            return {                value: this.value,                ...this.$attrs            }        },        $listenserAll() {            return Object.assign(                {},                this.$listeners,                {input: event => this.$emit( 'input', event.target.value )})        }    }}script>
$attrs 里存放的是父组件中绑定的非 props 属性,$listeners 里面存放的是父组件中绑定的非原生事件。 组件可以通过在自己的子组件上使用 v-on=”$listeners”,进一步把值传给自己的子组件。 如果子组件已经绑定 $listener 中同名的监听器,则两个监听器函数会以冒泡的方式先后执行。

总结:用在父组件传递数据给子组件或者孙组件。

4

provide 和 inject

Vue2.2 版本以后新增了这两个 API, 这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。 使用方法: provide 在父组件中返回要传给下级的数据; inject 在需要使用这个数据的子辈组件或者孙辈等下级组件中注入数据。 使用场景: 由于 vue 有 $parent 属性可以让子组件访问父组件。 但孙组件想要访问祖先组件就比较困难。 通过 provide/inject 可以轻松实现跨级访问父组件的数据。 注意: provide 和 inject 绑定并不是可响应的。 这是刻意为之的。 然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
// 父组件<template>    <div class="parentProvide">        <button @click="changeSth">我要干嘛好呢~button>        <p>要干嘛:{{ sth }}p>        <ChildA />    div>template><script>import ChildA from './ChildA';export default {    name: 'parent-pro',    data() {        return {            sth: '吃饭~'        }    },    // 在父组件传入变量    provide() {        return {            obj: this        }    },    methods: {        changeSth() {            this.sth = '睡觉~';        }    },    components: {        ChildA    }}script>// 子组件A<template>    <div>        <div class="childA">            <p>子组件A该干嘛呢:{{ this.obj.sth }}p>        div>        <ChildB />    div>template><script>import ChildB from "./ChildB";export default {    name: "child-a",    data() {        return {};    },    props: {},    // 在子组件拿到变量    inject: {        obj: {            default: () => {                return {}            }        }    },    components: {        ChildB    }}script>// 子组件B<template>    <div>        <div class="childB">            <p>子组件B该干嘛呢:{{ this.obj.sth }}p>        div>    div>template><script>export default {    name: "child-b",    data() {        return {};    },    props: {},    // 在子组件拿到变量    inject: {        obj: {            default: () => {                return {}            }        }    }}</script

总结:传输数据父级一次注入,子孙组件一起共享的方式。

5

$parent 和 $children & $refs

$parent 和 $children : 指定已创建的实例之父实例,在两者之间建立父子关系。 子实例可以用 this.$parent 访问父实例,子实例被推入父实例的 $children 数组中。 $refs : 一个对象,持有注册过 ref 特性的所有 DOM 元素和组件实例。 ref 被用来给元素或子组件注册引用信息。 引用信息将会注册在父组件的 $refs 对象上。 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素; 如果用在子组件上,引用就指向组件。
// 父组件    <div class="parentPC">        <p>我的名字:{{ name }}p>        <p>我的标题:{{ title }}p>        <ChildA ref="comp1" />        <ChildB ref="comp2" />    div>template><script>import ChildA from "./ChildA.vue";import ChildB from "./ChildB.vue";export default {    name: 'parent-pc',    data() {        return {            name: '',            title: '',            contentToA: 'parent-pc-to-A',            contentToB: 'parent-pc-to-B'        }    },    mounted() {        const comp1 = this.$refs.comp1;        this.title = comp1.title;        comp1.sayHi();        this.name = this.$children[1].title;    },    components: {        ChildA,        ChildB    }}script>// 子组件A - ref方式<template>    <div>        <p>(ChildA)我的父组件是谁:{{ content }}p>    div>template><script>export default {    name: 'child-a',    data() {        return {            title: '我是子组件child-a',            content: ''        }    },    methods: {        sayHi() {            console.log( 'Hi, girl~' );        }    },    mounted() {        this.content = this.$parent.contentToA;    }}script>// 子组件B - children方式<template>    <div>        <p>(ChildB)我的父组件是谁:{{ content }}p>    div>template><script>export default {    name: 'child-b',    data() {        return {            title: '我是子组件child-b',            content: ''        }    },    mounted() {        this.content = this.$parent.contentToB;    }}script>
从上面例子可以看到这两种方式都可以父子间通信,而缺点就是都不能跨级以及兄弟间通信。

总结:父子组件间共享数据以及方法的便捷实践之一。

6

Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。 它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

83bc87d4a0be440c995bc7ad692cc270.png

Vuex 实现了一个单项数据流,通过创建一个全局的 State 数据,组件想要修改 State 数据只能通过 Mutation 来进行,例如页面上的操作想要修改 State 数据时,需要通过 Dispatch (触发 Action ),而 Action 也不能直接操作数据,还需要通过 Mutation 来修改 State 中数据,最后根据 State 中数据的变化,来渲染页面。

1、State (index.js)

State 用来存状态。 在根实例中注册了 store 后,用 this.$store.state 来访问。
Vue.use(Vuex);const state = {    userInfo: {},           // 用户信息};export default new Vuex.Store({    state,    getters,    mutations,    actions});

2、Getters

Getters 从 State 上派生出来的状态。 可以理解为基于 State 的计算属性。 很多时候,不需要 Getters,直接用 State 即可。
export default {    /**    @description    获取用户信息    */    getUserInfo( states ) {        return states.userInfo;    }}

3、Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 Mutation。

Mutation 用来改变状态。需要注意的是,Mutation 里的修改状态的操作必须是同步的。在根实例中注册了 store 后, 可以用 this.$store.commit('xxx', data) 来通知 Mutation 来改状态。

export const UPDATE_USERINFO = "UPDATE_USERINFO";export default {    [type.UPDATE_USERINFO]( states, obj ) {        states.userInfo = obj;    }}

4、Action

  • Action 提交的是 Mutation,而不是直接变更状态。

  • Action 可以包含任意异步操作。

在根实例中注册了 store 后, 可以用 this.$store.dispatch('xxx', data) 来存触发 Action。

export default {    update_userinfo({        commit    }, param) {        commit( "UPDATE_USERINFO", param );    }}
乍一眼看上去感觉多此一举,我们直接分发 Mutation 岂不更方便? 实际上并非如此,还记得 Mutation 必须同步执行这个限制么? Action 就不受约束! 我们可以在 Action 内部执行异步操作:
actions: {  incrementAsync ({ commit }) {    setTimeout(() => {      commit('increment')    }, 1000)  }}

总结:对 Vue 应用中多个组件的共享状态进行集中式的管理(读/写),统一的维护了一份共同的 State 数据,方便组件间共同调用。

7

slot-scope 和 v-slot

从 vue@2.6.x 开始,Vue 为具名和范围插槽引入了一个全新的语法,v-slot 指令。 一个假设的 组件的模板如下:
<template>    <div class="container">        <header>            <slot name="header">slot>        header>        <main>            <slot>slot>        main>        <footer>            <slot name="footer">slot>        footer>    div>template><script>export default {    name: "base-layout",    data() {        return {}    }}script>
在向具名插槽提供内容的时候,我们可以在一个父组件的 元素上使用 v-slot 特性:
// 父组件<template>    <base-layout>        <template v-slot:header>            <h1>Here might be a page titleh1>        template>        <p>A paragraph for the main content.p>        <p>And another one.p>        <template v-slot:footer>            <p>Here's some contact infop>        template>    base-layout>template><script>import BaseLayout from "./BaseLayout";export default {    name: "parent-slot",    data() {        return {        }    },    components: {        BaseLayout    }}script>

    插槽的名字现在通过 v-slot:slotName 这种形式来使用,没有名字的 隐含有一个 "default" 名称:

<template v-slot:default>    <p>A paragraph for the main content.p>    <p>And another one.p>template>

8

scopedSlots 属性

scopedSlots 是编程式语法,在 render() 函数中使用 scopedSlots。

// baseLayout.vueexport default {    data() {        return {            headerText: "child header text",            defaultText: "child default text",            footerText: "child footer text"        }    },    render( h ) {        return h("div", { class: "child-node" }, [            this.$scopedSlots.header({ text: this.headerText }),            this.$scopedSlots.default(this.defaultText),            this.$scopedSlots.footer({ text: this.footerText })        ]);    }}import BaseLayout from "./baseLayout";export default {    name: "ScopedSlots",    components: {        BaseLayout    },    render(h) {        return h("div", { class: "parent-node" }, [            this.$slots.default,            h("base-layout", {                scopedSlots: {                    header: props => {                        return h("p", { style: { color: "red" } }, [                            props.text                        ]);                    },                    default: props => {                        return h("p", { style: { color: "deeppink" } }, [                            props                        ]);                    },                    footer: props => {                        return h("p", { style: { color: "orange" } }, [                            props.text                        ]);                    }                }            })        ]);    }}

总结

组件间不同的使用场景可以分为 3 类,对应的通信方式如下: 父子通信: props 和 $emit,$emit 和 $on,Vuex,$attrs 和 $listeners,provide 和 inject,$parent 和 $children&$refs 兄弟通信: $emit 和 $on,V uex 隔代(跨级)通信: $emit 和 $on,Vuex,provide 和 inject,$attrs 和 $listeners 排 版 | chuanrui 如下技术文章,你可能也感兴趣: 从实践中理解IO模型前端动效库搭建之路定时任务管理之批量管理从删库到不跑路(下)-之变更回滚

b6212c4acc067e897605f39bd727ee5a.gif

f4c1668d3a7748a8212c88f804a4b1b4.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值