vue组件封装备忘录

307 阅读1分钟

1.属性透传

利用 v-bind + $attr 实现属性方法继承传递

具体原理参考 juejin.cn/post/745685…

父组件

<template>
   <ChildComponent id="child" class="custom-class" data-custom="123"   />  
  </template>
  
  <script>
  import ChildComponent from './ChildComponent.vue'; 
  
  export default {
    components: {
        ChildComponent, 
    }
  };
  </script> 

ChildComponent子组件

<template>
  <div v-bind="$attrs"   > 
  </div>
</template> 

2.v-model透传

<template>
   <MidComponent v-model="parentData" /> 
   <!--等价于 -->
   <!-- <MidComponent :modelValue="parentData" @update:modelValue="$event => parentData = $event" />  -->
  </template>
  
  <script>
  import MidComponent from './MidComponent.vue';
  import ChildComponent from './ChildComponent.vue';
  
  export default {
    components: {
        MidComponent,
        ChildComponent,
    },
    data() {
        return {
            parentData:  'xxxx'
        };
        }
  };
  </script> 

中间组件需要显示声明回调的事件

<!-- 中间组件 -->
<template>
  <ChildComponent v-bind="$attrs"  />
</template>

<script>
import ChildComponent from './ChildComponent.vue';
export default {
  components: {
    ChildComponent,
  },
  inheritAttrs: false,
};
</script>

子组件需要 定义操作的属性+ 声明回调的事件

<!-- 子组件 --> 
<template>
  <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template>

<script>
export default {
  props: ['modelValue'],
};
</script>

el-input处理

只需要在子组件 对应修改 el-input的事件 model-value 和@input的参数 ,实现数据绑定即可

<!-- 子组件 --> 
<template>
  <el-input :model-value="modelValue" @input="$emit('update:modelValue', $event)" > 
   </el-input> 
</template>

<script>
export default {
  props: ['modelValue'],
};
</script>

3.v-model透传(createVNode)

使用createVNode创建的组件,事件的定义需要加上onXXX(以便于与浏览器原生事件做区分)

 createVNode(ChildSubComponent, { 
        modelValue: props.modelValue, 
        'onUpdate:modelValue': onValueUpdate, 
      })

完整代码

ParentComponent.vue

<template> 
    <MidComponent v-model="parentData"  />   
   </template>
   
   <script>
   import MidComponent from './MidComponent.vue'; 
   
   export default {
     components: {
         MidComponent,  
     },
     data() {
         return {
             parentData:  'xxxx', 
         };
         }
     }
   };
   </script> 

MidComponent.vue

<script lang="ts">
import { createVNode, defineComponent } from 'vue' 
import ChildComponent from './ChildComponent.vue'; 
export default defineComponent({ 
  props: {
    modelValue: {
      required: true,
    }, 
  },
  emits: ['update:modelValue'], 
  setup(props, { emit }) {
    const onValueUpdate = (value: any) => {
      emit('update:modelValue', value)
    } 

      return () =>
       createVNode(ChildComponent, { 
        modelValue: props.modelValue, 
        'onUpdate:modelValue': onValueUpdate, 
      })
  },
})
</script>

ChildComponent.vue

<template>
  <el-input :model-value="modelValue" @input="$emit('update:modelValue', $event)" > 
   </el-input> 
</template>

<script>
export default {
  props: ['modelValue'],
};
</script>

4.插槽透传

通过$slots 获取父组件所有声明的插槽,然后遍历输出,中间组件和子组件都要遍历

插槽穿透原理可以参考 juejin.cn/post/744884…

父组件

<template>
    <MidComponent v-model="parentData"> 
        <template #prefix>
            111
        </template>
        <template #suffix>
            222
        </template> 
        <template #prepend>
            333
        </template>
    </MidComponent>
</template>

<script>
import MidComponent from './MidComponent.vue'; 
export default {
    components: {
        MidComponent, 
    },
    data() {
        return {
            parentData: 'xxxx'
        };
    }
};
</script>

中间组件

<!-- 中间组件 -->
<template>
  <ChildComponent v-bind="$attrs"  >
    <template v-for="(_, name) in $slots" #[name]="scopedData">
    <slot :name="name" v-bind="scopedData"></slot>
    </template>
  </ChildComponent>

</template>

<script>
import ChildComponent from './ChildComponent.vue';
export default {
  components: {
    ChildComponent,
  },
  inheritAttrs: false,
};
</script>

子组件

<!-- 子组件 --> 
<template>
  <el-input :model-value="modelValue" @input="$emit('update:modelValue', $event)" >
    <!--(_, name)  注意第一个是内容,第二个才是key 也就是插槽的名称  -->
    <template v-for="(_, name) in $slots" #[name]="scopedData">
      <slot :name="name" v-bind="scopedData"></slot>
    </template>
  </el-input> 
</template>

<script>
export default {
  props: ['modelValue'],
  mounted() {
    console.log(this.$slots);
  }
}; 
</script>

5.获取ref以及暴露的方法

通过遍历ref的所有属性方法,然后直接绑定在this上,暂时只支持一层调用

父组件ParentComponent.vue

<template>
   <ChildComponent ref="testRef" v-model="parentData" />  
   <input type="button" value="test" @click="test">
  </template>
  
  <script>
  import ChildComponent from './ChildComponent.vue'; 
  
  export default {
    components: {
        ChildComponent,  
    },
    data() {
        return {
            parentData:  'xxxx'
        };
    },
    methods: {
        test() {
            this.$refs.testRef.focus2()
            console.log(this.$refs.testRef.val)
        }
    }
  };
  </script> 

子组件ChildComponent.vue

<!-- 子组件 --> 
<template>
  <el-input ref="myRef" :model-value="modelValue" @input="$emit('update:modelValue', $event)" > 
   </el-input> 
</template>

<script>
export default {
  props: ['modelValue'],
  data() {
    return {
      val:111
    } 
  },
  mounted() { 
    for (const key in this.$refs.myRef) {
        this[key] = this.$refs.myRef[key]
    } 
  },
  methods: {
    focus2() {
      this.$refs.myRef.focus()
    }
  }

};
</script>

源码

github.com/mjsong07/co…