# Props & Emits

# 父子组件传值

# 父传子

  • 父组件传值
<template>
  <child :data="data"></child>
</template>

<script>
export default {
  // ...
  data() {
    return {
      data: 'Hello Vue'
    }
  }
}
</script>
  • 子组件通过props属性接受
<template>
  <span>{{ data }}</span>
</template>

<script>
export default {
  // ...
  props: ['data']
}
</script>

# 子传父

原理

父组件在编译模板后将自定义事件evName及其回调函数cb通过vm.$on(evName, cb)添加到子组件的事件中心Events

子组件通过vm.$emit(evName, ...args)触发自身的事件中心Events上的对应的自定义事件

  • 子组件通过$emit触发自定义事件
<template>
  <button @click="send"></button>
</template>

<script>
export default {
  // ...
  methods: {
    send() {
      this.$emit('update', 'hello world')
    }
  }
}
</script>
  • 父组件
<template>
    <child @update="message"></child>
</template>
<script>
export default {
  // ...
  data() {
    return {
      data: ''
    }
  }
  methods: {
    message(msg) {
      this.data = msg
    }
  }
}
</script>

# v-model 原理

单向数据流

Vue 实现的双向绑定 其实就是:value@input语法糖

  • 父组件
<template>
  <child v-model="data"></child>
</template>

<script>
export default {
  data() {
    return {
      data: ''
    }
  }
}
</script>

子组件

<template>
  <input type='text' :value="value" @input="update">
</template>

<script>
export default {
  // ...
  props: ['value'],
  methods: {
    update(event) {
      this.$emit('input', event.target.value)
    }
  }
}
</script>
model 属性
<template>
  <input type='text' :value="propName" @input="update">
</template>
<script>
export default {
  // ...
  props: ['propName'],
  model: {
    porp: 'propName', // 名字随便
    event: 'eventName' // 可以是自定义事件也可以是input,click事件
  }
  methods: {
    update(event) {
      this.$emit('eventName', event.target.value)
    }
  }
}
</script>
如果希望将一个对象的所有属性作为prop传入
<template>
  <child v-bind="props"><children>
</template>
<script>

export default {
  // ...
  data () {
    return {
      props: {
        total
      }
    }
  }
}
</script>

# Props 验证

  • 概述:props可以使用对象形式接受一下参数实现相应的验证
  • type:接受参数的数据类型,有以下类型
    • String
    • Nmuber
    • Boolean(如果Boolean类型,则父组件传值没有参数默认为false
    • Object
    • Array
    • Map
    • Regexp
    • Date
    • Function
    • Symbol
  • default:接受参数的默认值。如果父组件没有传入值则使用默认值,引用类型需要使用方法返回一个默认值
  • required:必须要传的值
  • validator:可以使用此函数达到枚举的效果
export default {
  props: {
    a: {
      type: String, // 类型要是字符串
      default: 'hello', // 默认值,一般不与 required 放在一起使用
      validator(value) {
        // 使用验证可以达到枚举的效果
        // 这个值必须是下列字符串中的一个
        return ['succescc', 'warning', 'danger'].includes(value); //includes()检查值是否在数组中存在
      }
    },
    b: {
      type: [String, Number], // 可以是多种类型
      required: true, // 必传,一般不与 default 放在一起使用
    },
    c: {          
      type: Array,
      default() {
        return [1, 2, 3]
      }
    }
  }
}

# .sync 修饰符

背景

在有些情况下,我们可能需要对一个prop进行双向绑定。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以修改父组件,且在父组件和子组件都没有明显的改动来源。

  • 父组件
<template>
  <child :text.sync="text"></child>
</template>

<script>
export default {
  data() {
    return {
      text: 'hello vue'
    }
  }
}
</script>
  • 子组件
<template>
  <div @click="update">{{ text }}</div>
</template>

<script>
export default {
  props: ['text'],
  methods: {
    update() {
      // 使用了.sync 修饰符,可以直接使用 update:propsName 更新
      this.$emit('update:text', 'hello')
    }
  }
}
</script>
.sync语法糖

.sync修饰符在模板编译以后会变成以下

<template>
  <child :value="value" @update:value="update"></child>
</template>

<script>
export default {
  data() {
    return {
      value: 'hello vue'
    }
  },
  methods: {
    update(value) {
      this.value = value
      // 子组件 this.$emit('update:value', value)
    }
  }
}
</script>