次元图库
36.81M · 2026-03-09
今天沉浸式学习了 uniapp 中 Vue 自定义组件的封装,重点突破了「自定义样式」这个核心难点——很多新手封装组件时,要么样式冲突、要么无法灵活适配不同场景,其实掌握关键技巧后,自定义样式可以做到既规范又灵活。这篇笔记就把今天的学习成果整理出来,从基础封装到样式自定义,一步步拆解,适合和我一样正在入门的小伙伴参考~
先明确核心目标:封装的自定义组件,不仅要实现复用性,还要支持外部灵活修改样式,同时避免样式污染全局,兼顾易用性和规范性。下面从「组件基础封装」→「自定义样式实现」→「避坑实战」三个维度,结合具体代码讲解,全程可复制实操。
在 uniapp 中封装 Vue 自定义组件,和纯 Vue 项目思路一致,但要适配 uniapp 的页面结构和语法规范,核心步骤就3步,先搭好基础框架:
在项目的 components 目录下,新建组件文件夹(如 my-custom-btn),创建 my-custom-btn.vue 文件,这是组件的核心文件。
组件由 template(结构)、script(逻辑)、style(样式)三部分组成,先写一个简单的按钮组件作为示例,后续逐步完善样式自定义:
<template>
<!-- 组件基础结构 -->
<view class="custom-btn" @click="handleClick">
<slot>默认按钮</slot> <!-- 插槽:支持外部传入按钮文本 -->
</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>
组件封装好后,需要在页面中注册才能使用,有两种注册方式,根据复用频率选择:
<template>
<view>
<!-- 使用自定义组件 -->
<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>
在main.js 中注册,无需在每个页面单独引入:
import Vue from 'vue'
import MyCustomBtn from '@/components/my-custom-btn/my-custom-btn.vue'
// 全局注册组件
Vue.component('MyCustomBtn', MyCustomBtn)
这是今天学习的核心!封装组件时,固定样式无法满足不同页面的需求(比如有的页面需要红色按钮,有的需要圆角更大),因此需要支持「外部传入样式」,同时避免样式污染。推荐3种实用方式,从简单到灵活,按需选择。
核心思路:在组件中定义样式相关的 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>
核心思路:组件支持外部传入自定义 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(避免样式污染),外部传入的类名可能无法生效,此时有两种解决方案:
scoped(不推荐,可能污染全局);::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>
核心思路:如果组件的样式差异极大,甚至结构也有变化,可通过 slot 插入自定义样式(或整个结构),适合复杂场景,比如组件内部部分区域需要完全自定义。
修改组件,添加样式插槽(或结构插槽):
<template>
<view class="custom-btn" @click="handleClick">
<!-- 插槽:支持外部传入整个内容(包括样式) -->
<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>
学习过程中遇到了几个常见问题,整理出来,帮大家少走弯路:
scoped,导致组件样式影响全局页面,解决:给组件的 style 标签加上 scoped,如需穿透,用 ::v-deep。fontSize="32"),导致样式不生效,解决:props 中定义 fontSize 为 Number 类型,外部传入数字(如 fontSize="32" 改为 :fontSize="32",绑定数字)。今天通过实操掌握了 uniapp 中 Vue 自定义组件封装的核心,尤其是自定义样式的3种实现方式,总结下来:
其实自定义组件封装的核心就是「复用性」和「灵活性」,样式自定义更是如此——既要保证组件本身的规范性,又要支持外部按需修改。后续还要继续学习组件的生命周期、props 校验、事件传值等进阶内容,慢慢打磨组件封装能力~
如果小伙伴们有更好的样式自定义技巧,欢迎在评论区交流,一起进步!