主题
微信小程序基础与组件(三)
本页对应 微信小程序基础与组件,共 7 段视频,约 1 小时 5 分。
学习目标
- 能使用
input组件配置type、password、placeholder、maxlength、confirm-type等属性,并说明type主要用于控制真机键盘类型。 - 能使用
type="nickname"实现微信昵称输入,并在基础库版本不满足时完成组件库版本切换后验证昵称获取效果。 - 能使用
radio-group、radio、checkbox-group、checkbox与label完成单选和多选表单,并通过bindchange事件对象e.detail.value获取选中值。 - 能使用
picker的mode="region"与mode="date"实现省市区和日期选择,配置value、start、end,并通过bindchange更新data完成界面回显。 - 能区分全局样式
app.wxss与页面局部样式的优先级,能将全局样式或字体图标样式抽离后通过@import引入并验证生效。 - 能说明 WXSS 中不能直接引入本地静态资源的限制,并使用网络图片、Base64 或在线字体图标方式完成样式资源引入。
知识点
4.1 navigator:页面跳转
为什么需要:小程序页面之间要跳转,navigator 就相当于网页里的 a 标签。怎么想:先判断目标页面是不是 tabBar 页面;普通页面用默认 navigate,tabBar 页面必须用 switchTab。怎么做:在 navigator 上设置 url,支持相对路径和绝对路径;用 hover-class 控制点击态,值设为 none 可禁用点击效果;用 open-type 指定跳转方式。易错点:url 为空会报错;tabBar 页面如果仍用默认 navigate 会跳不过去,要改成 switchTab。
4.2 image:图片占位与裁剪
为什么需要:页面要显示图片,但小程序里组件名是 image,不是网页的 img。怎么想:image 本身是占位容器,默认有 320*240px 的宽高;当占位容器和图片实际宽高比不一致时,图片可能显示不正常,所以要理解 mode。怎么做:按设计稿设置 image 宽高;需要拉伸铺满用 scaleToFill,需要长边完整显示用 aspectFit(可能留白),需要短边完整显示用 aspectFill(可能裁切)。一般图片比例和容器不一致时,指定 mode="aspectFill"。易错点:把 image 写成 img;只放图片不设宽高,或误以为 aspectFit 一定铺满。
4.3 swiper:轮播容器
为什么需要:轮播图需要可滑动区块,swiper 就是专门做这个的。怎么想:它由容器和滑块单元组成,结构不能乱。怎么做:swiper 内部只能嵌套 swiper-item,swiper-item 内部再放任意内容,比如 image;常用属性有 indicator-dots 显示指示点、autoplay 自动切换、circular 衔接滑动。易错点:swiper 默认高度为 150px,不设高度可能显示不理想;不要在 swiper 里直接塞 image,应放在 swiper-item 里。
4.4 表单相关:input、单选、复选与 picker
为什么需要:移动端登记信息离不开输入、单选、复选、地区和时间选择。怎么想:小程序表单组件名称和网页相似,但属性和事件有差异,不能完全凭网页经验写。怎么做:input 用 type 指定输入类型,placeholder 设占位文字,password 设密码类型,maxlength 控制最大长度,confirm-type 可改键盘右下角按钮文字;type="nickname" 是微信生态里的昵称输入键盘,转写中在模拟器里没反应,是因为基础库版本不够,需在“详情→本地设置”里把调试基础库切到更高版本(如 3.x),之后底部会出现微信昵称。radio-group 包 radio 实现单选,checkbox-group 包 checkbox 实现多选,value 是提交数据,checked 是选中状态;用 bindchange 取 e.detail.value,单选得到一个值,多选得到数组。picker 用 mode 指定类型,region 是省市区三级联动,date 是日期选择;picker 内部要先写文字,否则高度可能为 0 点不到;选完后要通过 bindchange 拿 e.detail.value 并 setData 更新显示。日期选择器可用 start、end 限定范围,格式为 YYYY-MM-DD,end 常动态生成;value 可用于回显。易错点:单选不能靠相同 name 编组,必须用 radio-group;label 包住选项可扩大点击区域;picker 选完不更新数据,页面仍显示“请选择”。
css
/* 提前准备的样式代码 */
page {
padding: 40rpx 0;
box-sizing: border-box;
background-color: #f7f8fa;
}
.legend {
padding-left: 40rpx;
font-size: 36rpx;
color: #333;
font-weight: 500;
}
.form-field {
display: flex;
margin-top: 20rpx;
padding: 0 40rpx;
height: 88rpx;
background-color: #fff;
line-height: 88rpx;
color: #333;
}
.form-field label {
width: 120rpx;
}
.form-field .field {
flex: 1;
}
.form-field input {
height: 100%;
}xml
<!-- 提前准备好的布局结构代码 -->
<view class="register">
<view class="legend">信息登记:</view>
<view class="form-field">
<label for="">姓名:</label>
<view class="field">...</view>
</view>
<view class="form-field">
<label for="">性别:</label>
<view class="field">...</view>
</view>
<view class="form-field">
<label for="">爱好:</label>
<view class="field">...</view>
</view>
<view class="form-field">
<label for="">籍贯:</label>
<view class="field">...</view>
</view>
<view class="form-field">
<label for="">生日:</label>
<view class="field">...</view>
</view>
</view>4.5 scroll-view:可滚动区域
为什么需要:内容超出区域时要能滚动,scroll-view 还能支持下拉刷新等交互。怎么想:它内部嵌套需要滚动的内容,但必须产生溢出;垂直滚动时一定要给 scroll-view 指定高度。怎么做:用 scroll-x 控制水平滚动,用 scroll-y 控制垂直滚动,用 refresher-enable 开启下拉刷新。易错点:没高度、没溢出时不会滚动;把滚动内容放错层级也会失效。
css
/* 提前准备的样式代码 */
page {
background-color: #f2f2f2;
}
.search-bar {
padding: 20rpx 30rpx;
background-color:#fff;
}
.search-bar input {
padding: 14rpx 20rpx 10rpx 40rpx;
border: 1rpx solid #eee;
border-radius: 70rpx;
font-size: 28rpx;
color: #333;
}
.page-body {
height: 700rpx;
display: flex;
margin-top: 20rpx;
}
.page-body .aside {
width: 200rpx;
padding: 20rpx;
background-color: #fff;
}
.page-body .aside .item {
height: 60rpx;
margin-bottom: 20rpx;
border-radius: 10rpx;
background-color: #eee
}
.page-body .aside .active {
background-color: pink;
}
.page-body .content {
flex: 1;
padding: 20rpx;
margin-left: 20rpx;
background-color: #fff;
}
.page-body .content .item {
float: left;
width: 215rpx;
height: 215rpx;
margin: 0 20rpx 20rpx 0;
border-radius: 10rpx;
background-color: #eee;
}
.page-body .content .item:nth-child(even) {
margin-right: 0;
}xml
<!-- 提前准备好的布局结构代码 -->
<!-- 搜索框 -->
<view class="search-bar">
<input type="text" placeholder="输入搜索关键字" />
</view>
<!-- 页面主体 -->
<view class="page-body">
<view class="aside">
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
</view>
<view class="content">
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
</view>
</view>全局样式:先定公共规则,再让页面覆盖
为什么需要
小程序页面很多,如果每个页面都重复写文字颜色、背景、字体图标等公共样式,维护成本高。app.wxss 就是全局样式文件,写在这里的样式会对所有页面生效。
怎么想
先区分“全局”和“局部”:全局样式放在固定的 app.wxss 中;页面自己的 .wxss 是局部样式。页面中还有一个由小程序自动添加的 page 节点,可以把它理解成网页里的 body,所以给 page 写背景、文字颜色,会影响页面整体。
怎么做
在 app.wxss 中写:
css
page {
background-color: pink;
color: red;
}转写稿里的调试过程是:写完全局样式后切到首页,发现文字颜色变了;再切到另一个页面,发现没有变,原因是那个页面内部已经写过局部样式。由此可确认:局部样式存在时优先局部,局部没有覆盖时才读取全局样式。切换页面时可用开发者工具里的“普通编译”快速切路由,类似网页改地址栏。
易错点app.wxss 文件名固定,不能随意改名;不要以为全局样式一定压过页面样式,局部样式会覆盖它;全局样式适合做初始化、公共变量式规则和字体图标引入,不适合把所有页面细节都堆进去。
静态资源:WXSS 不允许本地路径,改用网络地址或 base64
为什么需要
网页里常写 background-image: url(./images/search.png),但小程序为了控制包体和首次加载速度,对 WXSS 中的静态资源有限制:不能直接使用本地图片路径。
怎么想
先判断资源用在哪里:如果是在 .wxss 里做背景图,就必须用网络路径(https:// 或 http:// 开头)或 base64;如果是 <image> 组件,本地图片仍可使用。搜索放大镜这类小图标,也可以优先考虑字体图标。
怎么做
笔记中的示例是把本地路径注释掉,换成网络图片,并用 background-size: contain 控制显示:
css
/* 使用背景图定义放大镜 */
.search-bar .button {
position: absolute;
top: 50%;
right: 60rpx;
width: 50rpx;
height: 50rpx;
transform: translateY(-50%);
/* background-color: pink; */
/* 背景图片 */
/* background-image: url(/static/images/search.png); */
background-image: url(https://lotjol.github.io/static-store/enjoy-plus/images/search.png);
background-size: contain;
}转写稿中的调试过程:先写本地背景图,路径看起来正确但不生效,工具提示“本地资源图片无法通过 WXSS 获取”;换成网络图后正常显示,再补 background-size 让图片占满容器。
易错点
不要把网页经验直接搬过来;本地图片在 WXSS 中报错不是路径写错,而是平台限制;网络图要确保可访问;base64 可用但一般由工具转换,不必手写。
字体图标:用 iconfont 在线链接,不要下载到本地
为什么需要
字体图标比图片更清晰、颜色可控,也适合多个页面复用。但小程序 WXSS 同样不支持本地字体文件,所以不能像网页那样随便下载字体后本地引用。
怎么想
把字体图标当作“全局资源”:在 iconfont 平台把图标加入项目,生成在线链接,把 @font-face 和图标类名复制到样式文件中。因为多个页面都要用,通常放到 app.wxss,或抽到独立文件再 @import 引入。
怎么做
笔记保留的代码如下,核心是 @font-face 中使用在线地址或 base64,再通过 .iconfont 和具体类名显示图标:
css
@font-face {
font-family: "iconfont"; /* Project id 3632516 */
src:
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAATYAAsAAAAACVwAAASMAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDIgqFfIUjATYCJAMUCwwABCAFhGcHRxs0CFGU7k2N7Mdg204e4YSRdMLR9eNemN8PCiyrdGcE0VpZPQtPxIpIv8IQqHchGxsFLsrGxhhAx0bc/7WZn5C0kYUVY0mFNZOTCiVD4qRkau43dTv25XLOxDa2INyuQk54Qj83jTax/7Y8D8SS8H/u9+pPS9C6v8Itu+yBfdEPtOgA2YB35nOfim6k2DXM5CkCtZYVUBxV1DUCfc1jB7CUtOUgbI9rs5EHteyQl7SHaqGse2QO8QZUpFeKq8Br9+fjv5RQg6TIQDvP6eNyA5x8P+b9Q3K8m4PsG3tJwNYJBTLMz8wV98sDN5F+YR6pNk9SaRko5frUa8ztxsXcVzBTOWd/LsM/L0rQDPXA7+nz3VKafIJf9xV5xgewyWB8iKKE82mlFnUXxXkCGmHcHZJMLg35UwFUjBflFd1vxEL6xJ9OSP90CjR+KLzpuzGbFThHUktQW+ZlIdsrWv7QI/S8whctg46TLcOWVYTXrKYrK1xsOrytY0NuNKqWpbXYGi9ajMIGr0se0MwjabII6NAkMH6x5AUWJYTNdDSzTAk4/vv/2azv84s17vqzNoNPi6QR56SANi9l7TMve37qijdO4vQth3ab81kSDQaB8UvYqAktbBAFb6/n6YnltktVYnQe8t51ouQN5okt2t28tNJCpcl06pIWPh9i8XZo9iNTz2bOXC/FsWPr4keXGVJbkhoak5tTLjWnOuzJjSktl1KarY1J0TMbSj/siz52bPqA4Wbtm5eNlTHlUVHl0ZXbDSlJA29l9HaSp1AxuJjvFgPu5wzQM/qh9n5U1PSp7DSousf+up18OnRA8gDCL7Fpwo64weSRwr4xu5mm2otUCDWO9tF70+OoYHB8JIjpXxExR31Y/bT6iFpn6p6Gd51DTPsKfOAc4baukuywEr2PvjgsOrRa8/l/6RdCKw6/BcQLZ3A/ImRBUHnQgnJhnbjz6p1qSb1Lvc7USeH7wMZbCx2iD6EphvXewFQwG7xZJoL7J3EMHcKG0EyAW40AumKtmPC2nhmaYP2MOUuc9/ChN0aXxUTWxJ32CGaDqdM72UY+LxeGA1TOk4fJUJH6glxPcvT35AqSDa0ig76uv7H57l9qj2feP3WdEuDnrSsk4pXzSmjvUn2hA8G3ZOysiZgzGRnzvFnTJzCn80iNhFo9TQW0SKvn/ZjaaucI1TGlBEmVPshUG0Xn+Fko1FmFUrVtqDWn/Pg6bZxIkWuASZM4CM12QNLoPWSaXUDn+AdQ6PQBSs3+Q62zoM5WZzKEUhnxGBm4QSM4syA5RMOM3VXrkXGIjZezss1IdpmR5pITkpLlSuRAchf7uIYaUzAWOVGW7FyFeD1ks0mcU5YsSMAJJoydOYmJYt6nJAiSHUrJEB6GGHAGGYFjJpA4iDYyjv++eojREDY8uSa62Sdz8XJanZMsQVINdKXBUWvuTNZ3GcooBYaJ/HYimcSOU8F8iE0vSDjO/M0sEAGWwNQi55QjkZDEuoaE8U32C6z8kRdqZy9CQZCEklARfaB8gnhZMCmRwYxV7kZIsKpcJl5GAA==') format('woff2'),
url('//at.alicdn.com/t/c/font_3632516_qjs869axt0h.woff?t=1672223363723') format('woff'),
url('//at.alicdn.com/t/c/font_3632516_qjs869axt0h.ttf?t=1672223363723') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-search:before {
content: "\e67d";
}
.icon-edit:before {
content: "\e6d8";
}
.icon-check:before {
content: "\e6d7";
}
.icon-share:before {
content: "\e6d6";
}使用时写两个类名,例如 class="iconfont icon-search"。转写稿还演示了抽离:新建 styles/iconfont.wxss,在 app.wxss 中用 @import "./styles/iconfont.wxss"; 引入。若项目要用两套 iconfont,必须改 font-family 名称,避免重名。
易错点
不要下载字体到本地后直接引用;不要只写 icon-search 而漏掉 iconfont;多个 iconfont 项目不能都用同一个 font-family;@import 路径按项目实际结构调整,不要写成绝对盘符路径。
课件图示








代码示例
常见错误
1. tabBar 页面点击 navigator 毫无反应 现象:<navigator url="/pages/index/index"> 点击后页面不动,控制台也无明显报错。 原因:目标页在 app.json 的 tabBar 中注册过,默认的 open-type="navigate" 无法跳转 tabBar 页面。 修法:改为 <navigator url="/pages/index/index" open-type="switchTab">;同时排查 url 是否漏写或写成空字符串(空路径会直接报错)。
2. 轮播图/图片被拉伸变形或只露出一部分 现象:设计稿 750×400 的图放进 image 后,人脸被压扁;或上下被裁掉一截。 原因:image 是占位容器(默认 320×240),未显式设宽高或宽高比与图片实际比例不一致,默认 scaleToFill 会强行拉伸。 修法:按设计稿给 image 显式设宽高,并加 mode="aspectFill"(要求铺满不留白)或 mode="aspectFit"(要求完整显示)。
3. 单选/多选"全都能选",或选完拿不到值 现象:性别两项同时高亮;或在 bindchange 里 e.detail.value 为 undefined。 原因:① 漏了外层 radio-group / checkbox-group 编组,小程序不像网页端靠 name 分组;② 忘了给 radio/checkbox 本身写 value。 修法:用 radio-group(checkbox-group)包裹并绑定 bindchange,每个子项补齐 value,取值统一走 e.detail.value(多选是数组)。
4. 选择器 picker 点不动 / 选完文字不变 现象:写了 <picker mode="region"></picker>,页面上那一行根本点不到;或能选但界面一直显示"请选择"。 原因:picker 内部没有内容时高度为 0,不可点击;且选中结果不会自动回填,必须自己接管。 修法:内部先放一段占位文字保证可点;再绑定 bindchange,在回调里 this.setData({ region: e.detail.value.join(' ') }),模板中用 {{region || '请选择省市区'}} 兜底。
5. 背景图/字体图标写了本地路径不生效 现象:background-image: url(/static/images/search.png) 编译直接报"本地资源图片无法通过 WXSS 获取";iconfont 下载到本地引入同样报错。 原因:小程序对 WXSS 中的静态资源有限制,只认网络地址或 base64(image 组件的 src 不受此限)。 修法:图片换成 https 网络地址(或转 base64);字体图标改用 iconfont 项目生成的在线 @font-face 链接,app.wxss 里 @import 引入。
6. 换页后全局样式"失效" 现象:app.wxss 里给 page 设了背景色,有的页面生效、有的页面没变。 原因:页面自身 wxss 里有同名同权重的样式覆盖了全局样式,并非全局没生效。 修法:确认页面级样式是否重复定义;需要覆盖时注意选择器权重,不要靠"再写一遍全局"来解决。
7. 数据改了但界面不动 现象:this.data.xxx = ... 后视图毫无变化;或模板里 {{endDate}} 直接渲染不出来。 原因:小程序不会监听 this.data 的直接赋值;且模板中未在 data 里声明的变量不会被响应式渲染。 修法:一律用 this.setData({ xxx: value }),并保证所有参与渲染的变量都在 data 中先声明初始值。
8. 基础库版本过低导致新能力不生效 现象:<input type="nickname"> 在模拟器里毫无反应,也不弹昵称面板。 原因:项目默认基础库(如 2.19.4)低于该特性要求的最低版本。 修法:详情 → 本地设置 → 调试基础库,选择用户占比高的较新版本(如 3.x),重新编译;真机验证键盘类特性(type、confirm-type)必须在手机上测。
练习
练习 1|导航与图片 在首页放三张"活动入口"卡片:第一张跳普通详情页,第二张跳 tabBar 中的"我的"页,第三张跳外部 H5(open-type="navigate" 不行,查文档确认正确取值)。每张卡片内嵌 200×120 的 image,使用网络图片且保证不变形。要求:点击卡片有自定义点击态,长按不出现默认效果。
练习 2|轮播图 用 swiper 做一个 3 张图的轮播:自动播放、循环衔接、显示指示点、不允许用户手动滑动。高度设为 320rpx,图片铺满且不拉伸变形。思考并验证:如果把 swiper 的高度去掉会发生什么?
练习 3|信息登记表单 用笔记里准备好的结构与样式,完成"姓名 / 性别 / 爱好 / 籍贯 / 生日"五项:
- 姓名:最多 10 个字,输入框为空时显示占位文字
- 性别:默认选中"女",切换时把值打印到控制台并回显到页面
- 爱好:多选,把选中的数组用
、拼接后展示在页面下方 - 籍贯:
region选择器,选完回填文字,未选时显示"请选择省市区" - 生日:
date选择器,起始 1900-01-01,结束日期取"今天"(动态生成,不能写死),并支持默认值回显 最后加一个"提交"按钮,点击后把五项数据console.log成完整对象。
练习 4|滚动区域 用笔记里准备好的搜索页结构:左侧分类栏独立垂直滚动,右侧内容区独立垂直滚动,且分类栏滚动时右侧不受影响。再给右侧开启下拉刷新,触发后在控制台输出提示并 1 秒后关闭刷新态。
练习 5|样式限制排查 新建一个页面,故意用本地路径给某个盒子设背景图,观察报错信息并记录;随后分别用"网络图片"和"base64"两种方式修复。再把 iconfont 的图标通过在线链接引入 app.wxss,验证在至少两个不同页面中都能正常显示,并把字体样式抽到单独的 styles/iconfont.wxss 中通过 @import 引入。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 小程序表单组件-input | 09:35 |
| 2 | 小程序表单组件-昵称输入键盘 | 04:01 |
| 3 | 小程序表单组件-单选和多项选择器 | 10:46 |
| 4 | 小程序表单组件-省市区选择器 | 09:59 |
| 5 | 小程序表单组件-日期选择器 | 11:53 |
| 6 | 小程序样式-全局样式 | 08:06 |
| 7 | 小程序样式-资源引入限制 | 10:49 |
小结
- 小程序的表单组件名与网页端大体同名,但属性体系完全不同——
input的type只控制键盘类型,不能变成单选框;遇到不确定的属性一律查官方文档,不要凭 HTML 经验写。 - 表单取值的统一范式只有一条:分组容器 +
bindchange+e.detail.value+setData。单选、多选、picker 全部适用,掌握一个就能类推其余。 - 表单组件普遍不会自动回填,选中结果必须手动写回
data再渲染,这是绝大多数"选了没反应"问题的根因。 - 组件的"能不能点"取决于有没有内容撑开高度:
picker空内容高度为 0、scroll-view垂直滚动必须显式设高、swiper默认仅 150px 高。 - 涉及原生能力(键盘类型、
confirm-type、昵称键盘)的效果只能在真机验证,模拟器不会弹键盘;同时留意基础库版本是否满足最低要求。 - WXSS 对静态资源有限制:不支持本地路径,图片走网络地址或 base64,字体图标走在线链接——这是"小程序要小、首屏要快"的设计取舍,遵守即可。
- 样式作用域遵循"就近优先":
app.wxss是全局兜底,页面 wxss 覆盖它;page等价于网页的body,是设置全局背景/字体的入口。 - 换页面调试别改源码,用工具里的编译模式直接切路由,效率远高于反复改
app.json。