🎉小白也能3步写出WeUI Uploader!从“懵圈”到“会写”的保姆级教程

94 阅读6分钟

💡 引言:面试官问我上传组件怎么写?我直接掏出这篇博客!

你是不是也曾在面试中被问到“怎么优雅地写上传组件”?别慌!今天咱们就用 WeUI Uploader 教你搞定它!
通过这篇博客,你不仅能写出代码,还能像老司机一样解释“为什么这么写”。

⚠️ 提前剧透:你会看到“BEM西装打领带”、“浮动布局排队买奶茶”、“伪元素隐身人”三大神比喻!

image.png

🚀 第一步:看懂WeUI的“高富帅”命名法(BEM)

image.png

技术原理

BEM(Block-Element-Modifier)就像给代码穿西装打领带——结构清晰、层次分明

<!-- 父容器(Block) -->
<div class="weui-uploader">
  <!-- 子元素(Element) -->
  <div class="weui-uploader__hd">...</div>
  <ul class="weui-uploader__files">
    <!-- 状态修饰符(Modifier) -->
    <li class="weui-uploader__file weui-uploader__file_selected">...</li>
  </ul>
</div>

幽默类比

  • Block:就像餐厅的“主菜”,是组件的核心(.weui-uploader)。
  • Element:类似“配菜”,是子元素(__hd__files)。
  • Modifier:像“加辣”指令,表示状态(_selected)。

代码注释

<!-- Block: 上传组件 -->
<div class="weui-uploader">
  <!-- Element: 头部标题 -->
  <div class="weui-uploader__hd">图片上传</div>
  <!-- Element: 文件列表 -->
  <ul class="weui-uploader__files">
    <!-- Modifier: 选中状态 -->
    <li class="weui-uploader__file weui-uploader__file_selected"></li>
  </ul>
</div>

💡 为什么选BEM?:避免类名冲突,让代码像“菜单分类”一样清晰!


🌟 第二步:浮动布局 vs 弹性布局(Flex)的“奶茶店吵架”

image.png

技术原理

WeUI Uploader用 浮动布局 实现文件列表排列:

.weui-uploader__file {
  float: left; /* 左浮!像排队买奶茶一样排成一列 */
  margin-right: 8px; /* 给“奶茶”之间留点距离 */
}

代码对比

布局方式优点缺点
浮动布局适合多列排列(如文件列表)需要清除浮动,否则“奶茶洒一地”😅
弹性布局自动换行、响应式强移动端兼容性稍弱

代码注释

/* 浮动布局:左浮!自动换行 */
.weui-uploader__file {
  float: left;
  width: 96px; /* 每杯奶茶的宽度 */
  margin-right: 8px; /* 右边留空,避免“贴脸杀” */
}

💡 为什么选浮动?:移动端屏幕小,浮动布局能快速排满一列,自动换行超方便!


🔧 第三步:Stylus变量+伪元素=“CSS魔法”

image.png

技术原理

WeUI用 Stylus变量 定义主题色,通过 伪元素 画出表单边框:

$weui-bg-0 = #ededed  // 背景色
$weui-fg-3 = rgba(0,0,0,0.1)  // 边框色

.weui-cells::before {
  content: "";  // 伪元素就像CSS的“隐身人”
  height: 1px;  // 画一条1px的边框线
  background-color: $weui-fg-3;
}

幽默类比

  • Stylus变量:像CSS的“快捷键”,改一个值全站换色!
  • 伪元素:CSS界的“隐身人”,默默帮你画线、加图标。

代码注释

// 主题色变量(改这里,全站颜色变魔术!✨)
$weui-bg-0 = #ededed

// 伪元素画边框(别怕!它不会占用空间)
.weui-cells::before {
  content: "";  // 必须有content才能显示
  height: 1px;  // 用background-color画线
}

💡 为什么选伪元素?:避免额外HTML标签,用CSS实现“无痕”边框!


📱 移动端黑科技:滚动更丝滑的“魔法咒语”

image.png

技术原理

WeUI用 -webkit-overflow-scrolling: touch 优化移动端滚动:

.page {
  -webkit-overflow-scrolling: touch;  // 移动端滚动更敏感
  overflow: scroll;  // 桌面端正常滚动
}

幽默类比

  • -webkit:像安卓和iOS的“通用语言”,让滚动像“奶茶滑入杯中”一样顺滑。
  • touch:告诉手机:“我要用手滑动,别用鼠标!”

代码注释

/* 移动端滚动优化:手指滑动更丝滑! */
.page {
  -webkit-overflow-scrolling: touch;  // WebKit内核(安卓/iOS)专属
  overflow: scroll;  // 桌面端正常滚动
}

💡 为什么选这个方案?:移动端没有鼠标,必须用触摸滚动!而 -webkit 是安卓/iOS的“通用语言”。


🧩 语义化标签 & 模块化设计

image.png

技术原理

WeUI用 .weui-cells 实现表单统一风格:

<!-- 语义化标签:表单容器 -->
<div class="weui-cells">
  <!-- 表单项 -->
  <div class="weui-cell">
    <div class="weui-cell__bd">上传图片</div>
  </div>
</div>

幽默类比

  • .weui-cells:像“表格”一样整齐排列表单项。
  • 模块化设计:每个组件独立成模块,像“乐高积木”一样自由组合!

代码注释

<!-- 表单容器 -->
<div class="weui-cells">
  <!-- 表单项 -->
  <div class="weui-cell">
    <div class="weui-cell__bd">上传图片</div>
  </div>
</div>

💡 为什么选语义化标签?:让代码更易读,搜索引擎也能看懂!


🧩 上传组件的“魔术师袖口”——负边距技巧

image.png

技术原理

WeUI通过 负边距(Negative Margin) 消除布局间隙:

.weui-uploader__bd {
  margin-bottom: -8px; /* 消除最后一行的底部空隙 */
  margin-right: -8px; /* 消除最后一列的右侧空隙 */
}

幽默类比

  • 负边距:像魔术师的“袖口”,偷偷把多余的空间“藏起来”。
  • 为什么需要?:如果不用,文件列表的最后几项会像“奶茶店排队时有人突然插队”,导致布局错乱。

代码注释

/* 魔术师的袖口:把多余的空间藏起来! */
.weui-uploader__bd {
  margin-bottom: -8px; /* 把最后一行的空白“剪掉” */
  margin-right: -8px; /* 把最后一列的空白“剪掉” */
}

💡 为什么选负边距?:用CSS的“小聪明”解决布局问题,比加额外标签更优雅!


🧩 图片上传的“视觉魔术”——背景图设计

image.png

技术原理

WeUI用 背景图 实现上传文件的“预览效果”:

.weui-uploader__file {
  background: url("https://weui.io/images/pic_160.png") no-repeat 50%;
  background-size: cover;
}

幽默类比

  • background-size: cover:像“拉伸照片贴墙”,无论图片宽高比如何,都能填满96x96的“奶茶杯”。
  • no-repeat:防止图片重复,像“奶茶店只卖一杯,不搞拼盘”😅。

代码注释

/* 背景图魔术:让图片自动填满奶茶杯! */
.weui-uploader__file {
  background: url("https://weui.io/images/pic_160.png") no-repeat 50%;
  background-size: cover; /* 自动拉伸填满 */
}

💡 为什么选背景图?:避免用<img>标签,减少DOM节点,性能更优!


🧾 总结:3个必知技巧 + 1个常见坑点

✅ 3个必知技巧

  1. BEM命名:像“菜单分类”一样清晰!
  2. 浮动布局:适合多列排列,记得清除浮动!
  3. 负边距技巧:用“魔术师袖口”消除布局间隙!

❌ 1个常见坑点

  • 浮动布局忘清除:会导致“奶茶洒一地”(布局错乱)!
    /* 清除浮动的“魔法咒语” */
    .clearfix::after {
      content: "";
      display: block;
      clear: both;
    }
    

🚀 动手实践!

  1. 打开 common.styl,把 $weui-bg-0 改成粉色,看看页面颜色变魔术!
  2. 尝试给 .weui-uploader__file 加个边框,变成“彩色奶茶杯”!

🧩 所有源码大公开!复制粘贴即用!

💡 为什么要展示源码?

“纸上得来终觉浅”,真正的学习是动手写代码!以下是你需要的所有源码,直接复制粘贴即可运行:

📄 index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <!-- 设置视口,确保在移动设备上正确显示 -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WEUI uploader 源码学习</title>
    <!-- 引入样式文件 -->
    <link rel="stylesheet" href="./common.css">
</head>
<body>
    <!-- 页面主容器 -->
    <div class="page">
        <!-- 页面头部区域 -->
        <header class="page__hd">
            <!-- 页面标题 -->
            <h1 class="page__title">Uploader</h1>
            <!-- 页面描述 -->
            <p class="page__desc">
                上传组件,支持图片上传、文件上传、视频上传等
            </p>
        </header>
        <!-- 页面主体内容区域 -->
        <main class="page__bd">
            <!-- WEUI表单单元格容器 -->
            <div class="weui-cells weui-cells_form">
                <!-- 上传器单元格 -->
                <div class="weui-cell weui-cell_uploader">
                    <!-- 单元格主体内容区域 -->
                    <div class="weui-cell__bd">
                        <!-- 上传器主容器 -->
                        <div class="weui-uploader">
                            <!-- 上传器头部:标题和计数信息 -->
                            <div class="weui-uploader__hd">
                                <!-- 上传器标题 -->
                                <p class="weui-uploader__title">图片上传</p>
                                <!-- 上传进度信息:已上传数量/总数量 -->
                                <div class="weui-uploader__info">
                                    <span>0</span> /
                                    <span>9</span>
                                </div>
                            </div>
                            <!-- 上传器主体:文件列表区域 -->
                            <div class="weui-uploader__bd">
                                <!-- 文件列表容器 -->
                                <ul class="weui-uploader__files">
                                    <!-- 文件项:这里展示了9个空的文件占位符 -->
                                    <li class="weui-uploader__file">
                                    </li>
                                    <li class="weui-uploader__file">
                                    </li>
                                    <li class="weui-uploader__file">
                                    </li>
                                    <li class="weui-uploader__file">
                                    </li>
                                    <li class="weui-uploader__file">
                                    </li>
                                    <li class="weui-uploader__file">
                                    </li>
                                    <li class="weui-uploader__file">
                                    </li>
                                    <li class="weui-uploader__file">
                                    </li>
                                    <li class="weui-uploader__file">
                                    </li>
                                </ul>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </main>
    </div>
</body>
</html>

📄 common.css

/* 全局重置样式:清除所有元素的默认边距和内边距 */
* {
  margin: 0;
  padding: 0;
}

/* 页面主容器样式 */
.page {
  /* 绝对定位,占满整个视口 */
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  /* 设置背景色为浅灰色 */
  background-color: #ededed;
  /* 允许内容滚动 */
  overflow: scroll;
  /* iOS设备滚动优化 */
  -webkit-overflow-scrolling: touch;
  /* 盒模型设置 */
  box-sizing: border-box;
  /* 层级设置 */
  z-index: 1;
}

/* 页面头部样式 */
.page .page__hd {
  /* 头部内边距 */
  padding: 40px;
}

/* 页面标题样式 */
.page .page__hd .page__title {
  /* 左对齐 */
  text-align: left;
  /* 字体大小 */
  font-size: 20px;
  /* 字体粗细 */
  font-weight: 400;
}

/* 页面描述样式 */
.page .page__hd .page__desc {
  /* 顶部外边距 */
  margin-top: 4px;
  /* 半透明黑色文字 */
  color: rgba(0,0,0,0.55);
  /* 左对齐 */
  text-align: left;
  /* 字体大小 */
  font-size: 14px;
}

/* WEUI单元格容器样式 */
.weui-cells {
  /* 顶部外边距 */
  margin-top: 8px;
  /* 白色背景 */
  background: #fff;
  /* 相对定位,为伪元素定位提供参考 */
  position: relative;
  /* 隐藏溢出内容 */
  overflow: hidden;
}

/* 单元格容器顶部边框线(使用伪元素实现) */
.weui-cells::before {
  content: "";
  /* 绝对定位 */
  position: absolute;
  left: 0;
  right: 0;
  /* 边框线高度 */
  height: 1px;
  /* 半透明黑色边框 */
  background-color: rgba(0,0,0,0.1);
  /* 层级设置 */
  z-index: 2;
}

/* 单个单元格样式 */
.weui-cell {
  /* 内边距 */
  padding: 16px;
  /* 相对定位 */
  position: relative;
  /* 弹性布局 */
  display: flex;
  /* 垂直居中对齐 */
  align-items: center;
  /* 行高设置 */
  line-height: 1.41176471;
}

/* 单元格主体内容区域 */
.weui-cell .weui-cell__bd {
  /* 弹性增长,占据剩余空间 */
  flex: 1;
}

/* 上传器单元格特殊样式 */
.weui-cell__uploader {
  /* 底部增加内边距,为上传器内容留出更多空间 */
  padding-bottom: 24px;
}

/* 上传器头部样式 */
.weui-uploader .weui-uploader__hd {
  /* 弹性布局 */
  display: flex;
  /* 底部内边距 */
  padding-bottom: 12px;
  /* 垂直居中对齐 */
  align-items: center;
}

/* 上传器标题样式 */
.weui-uploader .weui-uploader__hd .weui-uploader__title {
  /* 弹性增长,占据剩余空间 */
  flex: 1;
}

/* 上传器信息(计数)样式 */
.weui-uploader .weui-uploader__hd .weui-uploader__info {
  /* 半透明灰色文字 */
  color: rgba(0,0,0,0.3);
}

/* 上传器主体样式 */
.weui-uploader .weui-uploader__bd {
  /* 负边距,用于抵消文件项的外边距,实现紧凑布局 */
  margin-bottom: -8px;
  margin-right: -8px;
  /* 隐藏溢出内容 */
  overflow: hidden;
}

/* 文件列表容器样式 */
.weui-uploader .weui-uploader__bd .weui-uploader__files {
  /* 移除列表默认样式 */
  list-style: none;
}

/* 文件项样式 */
.weui-uploader .weui-uploader__bd .weui-uploader__files .weui-uploader__file {
  /* 左浮动,实现网格布局 */
  float: left;
  /* 右边距和下边距,创建文件项之间的间距 */
  margin-right: 8px;
  margin-bottom: 8px;
  /* 文件项尺寸:96x96像素的正方形 */
  width: 96px;
  height: 96px;
  /* 背景图片:使用WEUI提供的默认图片占位符 */
  background: url("https://weui.io/images/pic_160.png") no-repeat 50%;
  /* 背景图片覆盖整个区域 */
  background-size: cover;
}

📄 common.styl

$weui-bg-0 = #ededed
$weui-bg-2 = #fff
$weui-fg-1 = rgba(0,0,0,0.55)
$weui-fg-2 = rgba(0,0,0,0.3)
$weui-fg-3 = rgba(0,0,0,0.1)

*
    margin 0
    padding 0

.page
    position absolute
    top 0
    left 0
    right 0
    bottom 0
    background-color $weui-bg-0
    overflow scroll
    -webkit-overflow-scrolling touch
    box-sizing border-box
    z-index 1
    .page__hd
        padding 40px
        .page__title
            text-align left
            font-size 20px
            font-weight 400
        .page__desc
            margin-top 4px
            color $weui-fg-1
            text-align left
            font-size 14px

.weui-cells
    margin-top 8px
    background $weui-bg-2
    position relative
    overflow hidden
    &::before
        content ""
        position absolute
        left 0
        right 0
        height 1px
        background-color $weui-fg-3
        z-index 2

.weui-cell
    padding 16px
    position relative
    display flex
    align-items center
    line-height 1.41176471
    .weui-cell__bd
        flex 1


.weui-cell__uploader
    padding-bottom 24px
.weui-uploader
    .weui-uploader__hd
        display flex
        padding-bottom 12px
        align-items center
        .weui-uploader__title
            flex 1
        .weui-uploader__info
            color $weui-fg-2
    .weui-uploader__bd
        margin-bottom -8px
        margin-right -8px
        overflow hidden
        .weui-uploader__files
            list-style none
            .weui-uploader__file
                float left
                margin-right 8px
                margin-bottom 8px
                width 96px
                height 96px
                background url(https://weui.io/images/pic_160.png) no-repeat 50%
                background-size cover

🚀 动手试试看!

  1. 把以上代码分别保存为 index.htmlcommon.csscommon.styl
  2. 在浏览器中打开 index.html,你会看到一个完整的 WeUI Uploader!
  3. 修改 common.styl 中的变量,比如把 $weui-bg-0 改成粉色,看看页面颜色变魔术!

🌈 结语:前端思维养成记

写代码不只是“堆砖头”,而是理解为什么这么写
通过这篇博客,你不仅学会了 WeUI Uploader 的写法,还掌握了:

  • BEM命名的“西装打领带”哲学
  • 浮动布局的“奶茶店排队”逻辑
  • Stylus变量的“快捷键”思维
  • 负边距的“魔术师袖口”技巧
  • 背景图的“视觉魔术”设计

🎉 现在,去试试写一个属于自己的上传组件吧!如果遇到问题,欢迎留言区“求救”~