uniapp + Vue 自定义组件封装:自定义样式从入门到实战

今天沉浸式学习了 uniapp 中 Vue 自定义组件的封装,重点突破了「自定义样式」这个核心难点——很多新手封装组件时,要么样式冲突、要么无法灵活适配不同场景,其实掌握关键技巧后,自定义样式可以做到既规范又灵活。这篇笔记就把今天的学习成果整理出来,从基础封装到样式自定义,一步步拆解,适合和我一样正在入门的小伙伴参考~

先明确核心目标:封装的自定义组件,不仅要实现复用性,还要支持外部灵活修改样式,同时避免样式污染全局,兼顾易用性和规范性。下面从「组件基础封装」→「自定义样式实现」→「避坑实战」三个维度,结合具体代码讲解,全程可复制实操。

一、基础铺垫:自定义组件的基本封装流程

在 uniapp 中封装 Vue 自定义组件,和纯 Vue 项目思路一致,但要适配 uniapp 的页面结构和语法规范,核心步骤就3步,先搭好基础框架:

1. 新建组件文件

在项目的 components 目录下,新建组件文件夹(如 my-custom-btn),创建 my-custom-btn.vue 文件,这是组件的核心文件。

2. 编写组件基础结构

组件由 template(结构)、script(逻辑)、style(样式)三部分组成,先写一个简单的按钮组件作为示例,后续逐步完善样式自定义:

<template>
  <!-- 组件基础结构 -->
  <view class="custom-btn" @click="handleClick"&gt;
    &lt;slot&gt;默认按钮&lt;/slot&gt; <!-- 插槽支持外部传入按钮文本 -->
  </view>
</template>

<script>
export default {
  name: 'MyCustomBtn', // 组件名称,必填(便于注册和识别)
  props: {
    // 先定义基础props,后续添加样式相关props
    type: {
      type: String,
      default: 'primary' // 按钮默认类型
    }
  },
  methods: {
    handleClick() {
      // 组件点击事件,通过$emit向父组件传值
      this.$emit('click', '按钮被点击啦')
    }
  }
}
</script>

<style scoped>
/* 基础样式,先写固定样式,后续改为可自定义 */
.custom-btn {
  width: 120rpx;
  height: 60rpx;
  line-height: 60rpx;
  text-align: center;
  border-radius: 30rpx;
  background-color: #007aff; /* 默认蓝色 */
  color: #fff;
  font-size: 28rpx;
}
</style>

3. 注册并使用组件

组件封装好后,需要在页面中注册才能使用,有两种注册方式,根据复用频率选择:

方式1:局部注册(仅当前页面使用)
<template>
  &lt;view&gt;
    <!-- 使用自定义组件 -->
    <my-custom-btn @click="handleBtnClick">点击我</my-custom-btn>
  </view>
</template>

<script>
// 引入组件
import MyCustomBtn from '@/components/my-custom-btn/my-custom-btn.vue'
export default {
  components: {
    MyCustomBtn // 注册组件
  },
  methods: {
    handleBtnClick(msg) {
      console.log(msg) // 接收组件传过来的事件
    }
  }
}
</script>
方式2:全局注册(所有页面可使用)

main.js 中注册,无需在每个页面单独引入:

import Vue from 'vue'
import MyCustomBtn from '@/components/my-custom-btn/my-custom-btn.vue'
// 全局注册组件
Vue.component('MyCustomBtn', MyCustomBtn)

二、核心重点:自定义样式的3种实现方式

这是今天学习的核心!封装组件时,固定样式无法满足不同页面的需求(比如有的页面需要红色按钮,有的需要圆角更大),因此需要支持「外部传入样式」,同时避免样式污染。推荐3种实用方式,从简单到灵活,按需选择。

方式1:通过 props 传值控制样式(最基础、最常用)

核心思路:在组件中定义样式相关的 props(如背景色、字体大小、圆角等),外部使用组件时,通过传入 props 覆盖默认样式,适合样式修改场景较少的情况。

修改上面的按钮组件,添加样式相关 props:

<template>
  <view 
    class="custom-btn" 
    @click="handleClick"
    :style="{
      backgroundColor: bgColor,
      color: textColor,
      borderRadius: borderRadius,
      fontSize: fontSize + 'rpx'
    }"
  >
    <slot>默认按钮</slot>
  </view>
</template>

<script>
export default {
  name: 'MyCustomBtn',
  props: {
    type: {
      type: String,
      default: 'primary'
    },
    // 样式相关props,都设置默认值,保证外部不传入时也能正常显示
    bgColor: {
      type: String,
      default: '#007aff' // 默认蓝色
    },
    textColor: {
      type: String,
      default: '#fff' // 默认白色文本
    },
    borderRadius: {
      type: String,
      default: '30rpx' // 默认圆角
    },
    fontSize: {
      type: Number,
      default: 28 // 默认字体大小(单位rpx,外部传入数字即可)
    }
  },
  methods: {
    handleClick() {
      this.$emit('click', '按钮被点击啦')
    }
  }
}
</script>

<style scoped>
/* 保留基础样式,动态样式通过:style绑定 */
.custom-btn {
  width: 120rpx;
  height: 60rpx;
  line-height: 60rpx;
  text-align: center;
}
</style>

外部使用时,传入需要修改的样式 props 即可,未传入的会使用默认值:

<!-- 自定义背景色和文本色 -->
<my-custom-btn 
  bgColor="#ff3333" 
  textColor="#fff"
  @click="handleBtnClick"
>
  红色按钮
</my-custom-btn>

<!-- 自定义圆角和字体大小 -->
<my-custom-btn 
  borderRadius="10rpx" 
  fontSize="32"
  @click="handleBtnClick"
>
  小字体按钮
</my-custom-btn>

方式2:通过 style 传入自定义类(灵活度更高)

核心思路:组件支持外部传入自定义 class,通过 :class 绑定,实现更复杂的样式自定义(比如渐变、阴影、hover效果),适合样式差异较大的场景。

修改组件,添加 customClass props,用于接收外部传入的类名:

<template>
  <view 
    class="custom-btn" 
    :class="customClass" // 绑定外部传入的类
    @click="handleClick"
  >
    <slot>默认按钮</slot>
  </view>
</template>

<script>
export default {
  name: 'MyCustomBtn',
  props: {
    // 新增:接收外部自定义类名
    customClass: {
      type: String,
      default: ''
    },
    // 保留之前的基础props
    bgColor: {
      type: String,
      default: '#007aff'
    }
  },
  // ... 其他代码不变
}
</script>

<style scoped>
.custom-btn {
  width: 120rpx;
  height: 60rpx;
  line-height: 60rpx;
  text-align: center;
  border-radius: 30rpx;
  background-color: v-bind(bgColor); // 也可以用v-bind绑定props中的样式
  color: #fff;
  font-size: 28rpx;
}
</style>

外部页面中,先定义自定义样式类,再传入组件:

<template>
  <view>
    <my-custom-btn 
      customClass="gradient-btn" 
      @click="handleBtnClick"
    >
      渐变按钮
    </my-custom-btn>
  </view>
</template>

<style scoped>
/* 外部自定义样式类 */
.gradient-btn {
  background: linear-gradient(to right, #ff3366, #ff9900); /* 渐变背景 */
  box-shadow: 0 2rpx 10rpx rgba(255, 51, 102, 0.3); /* 阴影效果 */
}
.gradient-btn:hover {
  opacity: 0.9; /*  hover效果 */
}
</style>

注意:如果组件样式用了 scoped(避免样式污染),外部传入的类名可能无法生效,此时有两种解决方案:

  • 方案1:外部样式类不使用 scoped(不推荐,可能污染全局);
  • 方案2:组件中使用深度选择器 ::v-deep(推荐),修改组件样式如下:
<style scoped>
.custom-btn {
  /* 基础样式不变 */
}
/* 深度选择器:穿透scoped,让外部传入的类生效 */
::v-deep .gradient-btn {
  background: linear-gradient(to right, #ff3366, #ff9900);
  box-shadow: 0 2rpx 10rpx rgba(255, 51, 102, 0.3);
}
</style>

方式3:通过 slot 插入样式(极致灵活)

核心思路:如果组件的样式差异极大,甚至结构也有变化,可通过 slot 插入自定义样式(或整个结构),适合复杂场景,比如组件内部部分区域需要完全自定义。

修改组件,添加样式插槽(或结构插槽):

<template>
  &lt;view class="custom-btn" @click="handleClick"&gt;
    <!-- 插槽:支持外部传入整个内容(包括样式) -->
    <slot name="content">
      <view class="default-content">默认按钮</view>
    </slot>
  </view>
</template>

<script>
// ... 逻辑不变
</script>

<style scoped>
.custom-btn {
  width: 120rpx;
  height: 60rpx;
  line-height: 60rpx;
  text-align: center;
  border-radius: 30rpx;
  background-color: #007aff;
}
.default-content {
  color: #fff;
  font-size: 28rpx;
}
</style>

外部使用时,通过插槽插入自定义内容和样式,完全覆盖默认内容:

<my-custom-btn @click="handleBtnClick">
  <template #content>
    <view class="custom-content">
      <image src="/static/btn-icon.png" mode="widthFix" class="btn-icon"></image>
      <text class="btn-text">带图按钮</text>
    </view>
  </template>
</my-custom-btn>

<style scoped>
.custom-content {
  display: flex;
  align-items: center;
  justify-content: center;
  color: #333;
  font-weight: bold;
}
.btn-icon {
  width: 30rpx;
  height: 30rpx;
  margin-right: 8rpx;
}
</style>

三、避坑指南:今天踩过的3个小坑

学习过程中遇到了几个常见问题,整理出来,帮大家少走弯路:

  1. 样式污染问题:忘记给组件样式加 scoped,导致组件样式影响全局页面,解决:给组件的 style 标签加上 scoped,如需穿透,用 ::v-deep
  2. props 传值类型错误:传入字体大小时,误传字符串(如 fontSize="32"),导致样式不生效,解决:props 中定义 fontSize 为 Number 类型,外部传入数字(如 fontSize="32" 改为 :fontSize="32",绑定数字)。
  3. uniapp 样式单位问题:习惯用 px 单位,导致不同设备适配异常,解决:uniapp 中推荐用 rpx 单位,自动适配不同屏幕,组件样式统一用 rpx。

四、学习总结

今天通过实操掌握了 uniapp 中 Vue 自定义组件封装的核心,尤其是自定义样式的3种实现方式,总结下来:

  • 简单样式修改:用 props 传值绑定 inline-style,高效快捷;
  • 复杂样式修改:用 props 传自定义类名 + 深度选择器,灵活度高;
  • 极致灵活场景:用 slot 插入自定义内容和样式,适配各种复杂需求。

其实自定义组件封装的核心就是「复用性」和「灵活性」,样式自定义更是如此——既要保证组件本身的规范性,又要支持外部按需修改。后续还要继续学习组件的生命周期、props 校验、事件传值等进阶内容,慢慢打磨组件封装能力~

如果小伙伴们有更好的样式自定义技巧,欢迎在评论区交流,一起进步!

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com