vue进阶学习之路1-组件通信

352 阅读3分钟

目标:

  • 深刻理解vue的组件化机制
  • 掌握vue组件化常用技术
  • 掌握通用组件设计和实现思路
  • 加深对vue原理理解
vue组件化是vue的核心思想,极大提高代码复用性、可维护性和可测试性。

资源环境:

  • vue-cli 3.x
  • node.10.x
  • vuejs 2.x

文件目录

App.vue

main.js

parent.vue

child.vue

组件通信

一、父子通信:

1.通过属性传值

<div id="father">
    <child v-bind:msg= "message" class="child"> 
    <!-- 通过 v-bind 动态赋值 -->
    </child>
</div>
<script>
    let father = new Vue({
        el:'#father',
        components: { child }, // 注册子组件
        data: {
            message: 'message from father'
        }
    })
</script>

子组件通过props接收属性

<script>
    let child = {
        template: `<div>
        <p>下面是来自爸爸的信息:</p>
        <p>{{ msg }}</p>
        </div>`,
        props: ['msg']
    }
</script>

关于props

  • 可以使用v-bind动态绑定父组件来的内容
  • 在组件中使用props来从父组件接收参数,注意,在props中定义的属性,都可以在子组件中直接使用
  • props来自父级,而组件中data->return的数据就是组件自己的数据,两种情况作用域就是组件本身,可以在template,computed,methods中直接使用
  • props的值有两种,一种是字符串数组,一种是对象
  • 对于数组或对象类型的 prop来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态(尽可能将父子组件解耦,避免子组件无意中修改了父组件的状态),不介意直接修改prop的数据。
除去props传递,对于标签的原生API可以使用 $attrs 传递

$attrs--继承所有的父组件属性(除了prop传递的属性、class 和 style )

inheritAttrs:默认值true,继承所有的父组件属性(除props的特定绑定)作为普通的HTML特性应用在子组件的根元素上,如果你不希望组件的根元素继承特性设置inheritAttrs: false,但是class属性会继承 代码示例:

<div id="app">
    <base-input label="姓名" class="username-input" placeholder="username" data-date-picker="activated"></base-input>
</div>
<script>
Vue.component("base-input", {
    inheritAttrs: false, //此处设置禁用继承特性
    props: ["label"],
    template: `
    <label>
      {{label}}
      {{$attrs.placeholder}}
      {{$attrs["data-date-picker"]}}
      <input v-bind="$attrs"/>
    </label>
    `,
    mounted: function() {
        console.log(this.$attrs);
    }
})
const app = new Vue({
    el: '#app',
    data: {}
});
</script>

如果把上面例子中的inheritAttrs: false去掉或者改为inheritAttrs: true,最终渲染为:

2.通过ref 引用

<child v-bind:msg="message" class="child" ref="parent"> 
<script>
     let father = new Vue({
        el:'#father',
        components: { child }, // 注册子组件
        data: {
            message: 'message from father'
        },
        mounted () {
            console.log(this.$refs.parent.msg)
        }
    })
</script>

3.通过children:不保证children中的属性顺序

<child v-bind:msg="message" class="child" ref="parent"> 
<script>
    let father = new Vue({
        el:'#father',
        components: { child }, // 注册子组件
        data: {
            message: 'message from father'
        },
        mounted () {
            console.log(this.$children[0].msg)
        }
    })
</script>

二、子父通信:

1.通过事件派发:

<script>
    let child = {
        template: `<div>
        <p>下面是来自爸爸的信息:</p>
        <p>{{ msg }}</p>
        <h1 @click="$emit('foo', 'hello123')">123</h1>
        </div>`,
        props: ['msg']
    }
</script>

父接收:

<child v-bind:msg= "message" class="child"   @foo="onfoo"> </child>
<script>
    let father = new Vue({
        el:'#father',
        components: { child }, // 注册子组件
        data: {
            message: 'message from father'
        },          
        methods: {
            onfoo (data) {
                console.log('子给父' + data)
            }
        }
    })
</script> 

三、兄弟通信:

1.中间人桥接(parent,root)

<button @click="message">兄弟组件</button>
mounted () {
    this.$parent.$on('foo', () => {
      console.log('兄弟组件测试')
    })
},
methods: {
    message () {
      this.$parent.$emit('foo')
    }
}
现象: 点击一个button,却触发两次

缺点:耦合度高,监听需要做过滤处理

2.总线模式bus

main.js或公共方法js文件中:

class Bus {
  constructor () {
    this.callbacks = {}
  }
  $on (name, fn) {
    this.callbacks[name] = this.callbacks[name] || []
    this.callbacks[name].push(fn)
  }
  $emit (name, args) {
    if (this.callbacks[name]) {
      this.callbacks[name].forEach(cb => cb(args))
    }
  }
}
Vue.prototype.$bus = new Bus()
// Vue.prototype.$bus = new Vue()

可以用Vue()原因:Vue本身就有on和emit

mounted () {
    this.$bus.$on('foo', () => {
        console.log('兄弟组件bus测试')
    })
},
methods: {
    message () {
      this.$bus.$emit('foo')
    }
}

3.vuex(后期单独补充说明)

四、祖代通信:(跨层级传参)

祖代传参通过provide:(provide的参数不建议修改)

let father = new Vue({
    el:'#father',
    components: { child }, // 注册子组件
    provide () {
        return {
            'foo': 'provide testData'
        }
    },
    data: {
        message: 'message from father'
    },          
    methods: {
        onfoo (data) {
            console.log('子给父' + data)
        }
    }
})

子代接收通过inject:

let child = {
    template: `<div>
    <p>下面是来自爸爸的信息:</p>
    <p>{{ msg }}</p>
    <p>下面是来自祖代的信息:</p>
    <p>{{foo}}</p>
    </div>`,
    props: ['msg'],
    inject: ['foo']
}

五、毫无关系

vuex(后期单独补充说明)