vue属性透传

157 阅读1分钟

属性透传与实传

实传:prop指定传递的属性; 透传:未指定prop传递的属性传递给子组件嵌套组件

使用场景: 以二次封装ui组件场景为例;保持第三方组件原有功能不变,实现一些定制效果

使用的api: $attrs, $listeners, inheritAttrs, 渲染函数

// 父组件
<MyButton name="测试透传" :disabled="false" @click="clickBtn" />

// 子组件, MyButton
const MyButton = {
  // 将透传的属性继续传递给ui-Button组件
  template: `<div class="box">
    <Button v-bind:type="type" v-bind="$attrs" v-on="$listeners" />
  </div>`,
  components: {
    Button,
  },
  inheritAttrs: false,  // 禁止未被props指定的属性应用到组件根元素上
  props: {
    type: {
      type: String,
      default: 'primary',  // 设置默认值为primary
    },
  },
  created() {
    console.log(this.$attrs);  // {name: '测试透传', disabled: false}
    console.log(this.$listeners);  // {click: f}
  },
};

// ui组件Button, 直接使用如下, 默认type为'default'
<Button type="primary" name="测试文本" icon="xxx" disabled @click="xxx" />

.native绑定事件到子组件根元素; $listeners可用于将事件监听器指向到具体的某个子元素,使用更灵活

引申使用:动态组件透传实现

在上面案例的基础上实现动态生成表单内容

// template
<div v-for="(config, index) in configArr" :key="config.type + index">
  <!-- 动态组件,根据配置中的 Type 来决定渲染的是 Input 还是 Select -->
  <component :is="config.type" :configProps="config.props"></component>
</div>

// configArr
[{
  type: 'Input',
  props: {
    placeholder: '我是默认值',
    clearable: true
  }
}, {
  type: 'Button',
  props: {
    type: 'default'
  }
}]

// 注册新的组件, 使用渲染函数以替代动态组件
// 使用渲染函数解构props,获取attribute绑定
const CompFormItem = {
  components: {
    Input, Select
  },
  name: 'FormItem',
  props: {
    config: {
      required: true
    }
  },
  render (h) {
    // 第一个参数就是配置中的 type,也就是我们的组件名称
    return h(`${this.config.type}`, {
      props: {
        ...this.config.props || {}
      },
      attrs: {
        ...this.config.props || {}
      }
    })
  }
}
// 使用组件
<FormItem :config="config"></FormItem>

-----关于vue3.x的变更-----

  1. $listeners被移除,事件监听器整合到$attrs
  2. $attrs包含style、class属性
  3. .native修饰符移除,因为子组件未定义触发的事件,将被作为子组件的根元素的原生事件监听,此时无需使用.native绑定原生事件到子组件根元素; 使用emits选项定义组件触发的事件

-----关于向父组件透传原生事件的问题-----

<template>
  <button @click="$emit('click', $event)">OK</button>
</template>
<script>
export default {
  emits: [] // 不声明事件
}
</script>

// 父组件
<my-button @click="handleClick"></my-button>

该事件触发两次:1. $emit;2. 应用到子组件根元素上的事件
解决方案:1. emits选项正确声明事件;2. 移除透传的事件