ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

uni-app微信小程序登录页全流程:视觉交互、input坑与授权登录

uni-app微信小程序登录页全流程:视觉交互、input坑与授权登录 做 uni-app 微信小程序这几年登录页面是我见过最容易看起来简单、做起来翻车的页面。它结构小、元素少但偏偏要同时扛住视觉观感、输入交互、键盘适配、授权流程、登录态管理这几件事。这篇接着上一篇的思路往下走不再讲怎么把两个输入框摆上去而是把登录页面拆到能直接抄的程度从配色和圆角体系到 input 组件的隐藏坑再到微信授权登录的取舍和真机上的样式偏差。适合已经能用 uni-app 跑通一个页面、但对细节把控还没底的同学也适合做了一两年小程序、想把登录页从能用打磨到好看又好用的人。1. 好看的登录页本质是三层功夫的叠加很多同学一上来就问有没有现成的模板我一般不建议直接拿模板改。模板能解决 60 分的问题但从 60 分到 90 分那段恰恰是最费时间也最能体现水平的地方。而这段差距拆开看其实就三层视觉层、交互层、结构层。任何一层缺了页面都会给人一种说不上哪里不对就是不够精致的感觉。1.1 视觉层配色、圆角、阴影、留白四件套视觉层最容易犯的错是元素太多。登录页的面积本来就有限再堆三四种颜色、两套圆角规则视觉上立刻就散了。我的做法是先把规则定死再往里填内容。配色上一个登录页最多用三种颜色一个主色按钮、链接、选中态、一个中性色系文字、边框、背景通常黑白灰的 5 到 6 个层级、一个点缀色错误提示、警告。主色我用得最多的是#3B6FFF这一类偏蓝的色调原因是蓝色在浅色背景上对比度稳定不会像紫色那样在不同屏幕色域下偏色明显。圆角是最容易被忽略的细节。我的习惯是建一套三级圆角体系大卡片32rpx、输入框和次级容器24rpx、按钮44rpx接近胶囊形但不完全是。为什么不干脆全部用胶囊因为输入框如果是全胶囊和内部左对齐的文字配合起来会显得文字浮在框里留白不好控制。统一体系比统一数值更重要读者眼睛会自动捕捉这种规律性。阴影要克制。很多人喜欢用很重的阴影来突出卡片结果在小程序的深色背景或者低端安卓屏幕上阴影会变成一团脏兮兮的灰。我的参数是.form-card { background: #ffffff; border-radius: 32rpx; box-shadow: 0 8rpx 40rpx rgba(17, 24, 39, 0.06); }0.06这个透明度是关键超过0.1就会显得脏。另外阴影的y偏移要大于blur的四分之一否则会像发光而不是投影。留白用 8 的倍数体系也就是8 / 16 / 24 / 32 / 48 / 64 rpx不要出现17rpx、23rpx这种数字。这一条听着像强迫症但实际效果非常明显页面上所有间距都落在同一个节奏上时人眼会觉得很顺。1.2 交互层焦点态、按压态、加载态一个都不能少视觉做完了页面是静态好看。真正让人感觉精致的是手指碰上去之后的反馈。这里有三个状态必须处理。焦点态。用户点进输入框时边框颜色要变、可以配合一点点缩放。但注意微信小程序里的:focus伪类支持并不完整正确做法是绑定focus和blur事件用一个数据字段控制类名view classfield :class{ field--focus: focusField phone } input v-modelform.phone typenumber maxlength11 placeholder请输入手机号 placeholder-classfield__placeholder focusfocusField phone blurfocusField / /view按压态。小程序的view支持hover-class这是最稳的方案。不建议依赖 CSS 的:active因为它在不同基础库版本上表现不一致有的版本根本不触发。给按钮加一个hover-classbtn--hover在对应类里把背景色压暗 8% 到 10%再加一个transform: scale(0.98)手感就出来了。加载态。点击登录之后如果只弹一个全屏的加载提示用户会觉得卡住了。我倾向于把 loading 内联到按钮里按钮文字变淡、左侧出现一个旋转的小圈、按钮进入禁用状态。这样用户能明确看到系统在动而且视线不用离开按钮。1.3 结构层装饰区、表单区、底栏区三段式结构上我把登录页固定切成三块顶部的装饰区、中间的表单区、底部的协议与切换登录方式区。装饰区用绝对定位 pointer-events: none这样它就不会拦截点击事件。表单区用 flex 布局垂直居中但如果内容高度超过视口要允许滚动而不是硬撑。底栏区贴底并且必须带安全区 padding否则在全面屏机型上协议文字会被底部横条挡住一半。这三块的边界一定要清晰。我见过不少人把装饰元素和表单元素混在同一个 flex 容器里结果键盘弹起、内容伸缩时装饰元素跟着一起动整个页面就乱了。2. 骨架搭建rpx 换算、背景实现与安全区处理骨架搭得好不好决定了后面改样式时是微调还是推倒重来。这一节讲三个最基础但最容易出问题的点。2.1 rpx 的换算逻辑以及什么时候不该用它rpx的规则很简单不管屏幕多宽都按 750 份等分750rpx永远等于视口宽度。所以如果设计稿是 750px 宽标注多少就写多少1:1 映射不需要换算。这一条是 uni-app 在小程序端比原生开发舒服的地方。但rpx有个陷阱它会跟着屏幕宽度等比放大。在手机上没问题在平板、折叠屏展开态、甚至某些小程序的分屏模式下750rpx会变成非常夸张的物理尺寸一个600rpx宽的登录卡片在平板上能占满半个屏幕字大得像在放大镜里看。我的处理方式是给登录卡片同时设一个max-width.form-card { width: 640rpx; max-width: 640px; margin: 0 auto; }max-width用px因为它是上限保护不应该跟着屏幕放大。这样在手机上按rpx走在超宽屏上被px卡住两边都不难看。还有一类不该用rpx的地方图标和需要精确像素级的元素比如细边框。1rpx在 2 倍屏上其实是 0.5 物理像素渲染出来会有时有时无的现象。边框我一般直接写1px。2.2 背景装饰能用 CSS 就别用图片登录页的背景装饰通常有两种做法用图片或者用 CSS。我强烈建议用 CSS原因有三个。第一是体积。一张 750x1334 的背景图即使压成 webp 也要 40KB 到 80KB而小程序主包只有 2MB 的额度登录页作为启动必经之路必须在主包每一 KB 都要省。第二是清晰度。背景图在不同分辨率、不同宽高比的机型上一定会拉伸或裁切渐变的过渡带容易出现色阶断层看起来像马赛克。第三是可控性。CSS 写的背景可以跟着主题色改可以做缓慢的位移动画图片则要重新切。具体实现上我用的是径向渐变叠加.login-page { position: relative; min-height: 100vh; background: linear-gradient(180deg, #f5f8ff 0%, #ffffff 60%); overflow: hidden; } .blob { position: absolute; border-radius: 50%; pointer-events: none; } .blob-1 { width: 520rpx; height: 520rpx; top: -180rpx; right: -140rpx; background: radial-gradient(circle, rgba(59, 111, 255, 0.18) 0%, rgba(59, 111, 255, 0) 70%); } .blob-2 { width: 420rpx; height: 420rpx; bottom: -120rpx; left: -160rpx; background: radial-gradient(circle, rgba(255, 138, 101, 0.16) 0%, rgba(255, 138, 101, 0) 70%); }提示径向渐变本身已经带了柔和的边缘过渡不需要再叠加filter: blur()。很多人为了追求毛玻璃效果加上blur(80rpx)在中低端安卓上会直接掉帧因为模糊是 GPU 逐像素计算面积越大越吃性能。如果用linear-gradient做整页背景注意色标至少要给三段两段渐变在大屏上会有明显的色带。三段以上、并且相邻色差控制在 5% 以内看起来才是奶油感而不是塑料感。2.3 安全区底部协议区必须处理底部协议文字被全面屏的横条挡住是登录页最经典的低级错误。处理方式是给底部容器加安全区 padding.footer { position: fixed; left: 0; right: 0; bottom: 0; padding: 24rpx 48rpx; padding-bottom: calc(24rpx constant(safe-area-inset-bottom)); padding-bottom: calc(24rpx env(safe-area-inset-bottom)); }写两行padding-bottom是为了兼容老版本 iOSconstant()是旧语法新版本用env()后者不生效时前者兜底顺序不能反。另外要注意如果登录页用了自定义导航栏navigationStyle: custom顶部的状态栏高度也要自己处理。获取方式是用uni.getSystemInfoSync().statusBarHeight拿到之后作为 padding-top。这个值在开发者工具里是模拟值真机上才会拿到真实数值这一点后面还会再提。3. 表单区域input 组件那些不写在文档里的坑表单是登录页的核心也是坑最密集的地方。微信小程序的input组件和浏览器的input完全是两套东西很多在 H5 上理所当然的写法搬过来就不生效。3.1 placeholder 样式、光标、键盘类型第一个坑是placeholder-style。这个属性文档里写着可以直接写内联样式但实测在部分安卓机型上不生效稳妥做法是改用placeholder-class在 CSS 里定义类.field__placeholder { color: #9aa4b8; font-size: 28rpx; font-weight: 400; }第二个坑是键盘类型。typenumber会调起纯数字键盘但它的行为是可以输入小数点、减号也就是说用户能输入1.5或者-3。如果你要限制为纯整数需要在input里做正则过滤。手机号场景我一般用typenumber配合maxlength11再加一层正则校验因为maxlength对number类型在某些安卓版本上会失效这个得靠代码兜底。第三个坑是密码框。input有个password属性布尔值比typepassword更推荐因为前者在小程序里对小眼睛图标的支持更好。切换明文与密文的实现是绑一个布尔值input v-modelform.password :password!showPwd placeholder请输入密码 placeholder-classfield__placeholder / view classfield__eye clickshowPwd !showPwd text classiconfont{{ showPwd ? \ue63e : \ue63f }}/text /view注意切换password属性会导致输入框重新渲染在 iOS 上会让光标位置回到开头。如果你的密码框允许用户编辑中间内容这个体验就很糟。实际的规避方式是在切换时不改变绑定的值或者干脆只在输入为空时才允许切换。第四个是cursor-spacing。这个属性控制光标与键盘的距离默认值在某些机型上偏小输入框紧贴键盘显得很挤。我一般设cursor-spacing20单位是 px 不是 rpx这一点要注意。confirm-type也值得配一下手机号框设next密码框设done用户输完可以直接在键盘上点下一项和完成减少手指移动距离。这个细节用的人不多但对体验的提升很直观。3.2 校验策略什么时候校验比校验什么更重要校验规则本身不复杂手机号/^1[3-9]\d{9}$/密码长度 8 到 20 位且必须包含字母和数字。真正需要设计的是校验时机。常见的三种策略对比策略触发时机优点缺点实时校验每次输入都校验反馈最快输入到一半就报错用户烦躁失焦校验离开输入框时校验打扰少准确反馈稍慢提交校验点登录时全量校验实现简单错误一次性全冒出来体验差我的做法是混合式输入时只清错不在输入过程中报新错失焦时校验该字段提交时全量校验并把第一个错误字段滚动到可视区。具体实现上表单数据里存两份form放值errors放错误信息。const form reactive({ phone: , password: }); const errors reactive({ phone: , password: }); const rules { phone: [ { test: v !!v, msg: 请输入手机号 }, { test: v /^1[3-9]\d{9}$/.test(v), msg: 手机号格式不正确 } ], password: [ { test: v !!v, msg: 请输入密码 }, { test: v /^(?.*[a-zA-Z])(?.*\d)[\w!#$%^*]{8,20}$/.test(v), msg: 密码需8-20位且包含字母和数字 } ] }; function validateField(key) { const rule rules[key].find(r !r.test(form[key])); errors[key] rule ? rule.msg : ; return !rule; }有个细节一定要做错误提示的区域要预留固定高度。如果错误文案是动态插入的输入框会被挤下去整个卡片抖一下观感非常差。做法是给提示文案一个min-height没错误时占位但内容为空.field__error { min-height: 36rpx; line-height: 36rpx; font-size: 24rpx; color: #f04a4a; padding-left: 8rpx; }3.3 验证码倒计时与协议勾选的状态管理如果登录方式里有短信验证码倒计时逻辑几乎是必写。这段代码看着简单但有三个坑。第一个坑是setInterval没有清理。用户点完获取验证码然后直接返回上一页定时器还在后台跑反复进入几次就会同时存在多个定时器倒计时数字乱跳。必须在onUnload里清掉let timer null; function startCountdown() { countdown.value 60; timer setInterval(() { countdown.value - 1; if (countdown.value 0) { clearInterval(timer); timer null; } }, 1000); } onUnload(() { if (timer) { clearInterval(timer); timer null; } });第二个坑是后台切换导致计时不准。小程序切到后台后定时器会被降频甚至暂停用户切回来一看还是 60 秒。更稳的做法是记录结束时间戳每次 tick 用Date.now()反算剩余秒数const endAt Date.now() 60000; timer setInterval(() { const rest Math.max(0, Math.ceil((endAt - Date.now()) / 1000)); countdown.value rest; if (rest 0) { clearInterval(timer); timer null; } }, 500);第三个是协议勾选。这里我不建议用小程序自带的checkbox组件因为它的样式几乎改不动选中态的圆角、颜色、大小都受系统控制。自己用view画一个更省事view classagree clickagreed !agreed view classagree__box :class{ agree__box--on: agreed } text v-ifagreed classagree__tick✓/text /view text classagree__text我已阅读并同意/text text classagree__link click.stopopenProtocol(user)《用户协议》/text /view注意click.stop不加的话点协议链接会同时触发勾选和跳转用户会莫名其妙地看到勾选框被选中。如果用户没勾协议就点登录我的处理是给协议那一行加一个横向抖动动画并弹一个轻提示。抖动比单纯弹 toast 更有效因为用户视线通常在按钮区域抖动能把注意力引到协议上。keyframes shake { 0%, 100% { transform: translateX(0); } 20% { transform: translateX(-12rpx); } 40% { transform: translateX(12rpx); } 60% { transform: translateX(-8rpx); } 80% { transform: translateX(8rpx); } } .agree--shake { animation: shake 0.4s ease-in-out; }4. 动效设计让页面活起来但别活过头登录页加动效这件事做对了是加分项做过了就是减分项。我见过有人登录页上又是粒子又是打字机又是 3D 翻转结果页面加载慢、掉帧、还显得很廉价。我的原则是动效只服务于引导注意力和提供反馈两个目的不为炫技。4.1 入场动画CSS 优于 JS 动画uni-app 里做动画有两条路CSS 的keyframes和uni.createAnimation。我基本上不用后者原因是createAnimation是通过修改数据驱动视图的每一次动画帧都要走一遍setData通信在页面上同时跑三四个动画时就会明显卡顿。CSS 动画直接跑在渲染层不占逻辑层的通信带宽这是本质区别。入场动画我的方案是分层延迟logo 先出现标题跟进表单卡片最后滑入。keyframes fadeUp { from { opacity: 0; transform: translateY(24rpx); } to { opacity: 1; transform: translateY(0); } } .header__logo { animation: fadeUp 0.5s ease-out both; } .header__title { animation: fadeUp 0.5s ease-out 0.08s both; } .header__sub { animation: fadeUp 0.5s ease-out 0.16s both; } .form-card { animation: fadeUp 0.6s ease-out 0.24s both; }both是关键它让元素在动画开始前就保持from状态避免元素先闪一下再开始动画。延迟用0.08s这种小间隔就够了总时长控制在 0.8 秒以内超过 1 秒用户会觉得这个页面怎么加载这么慢。要动就只动transform和opacity。这两个属性可以由合成层直接处理不触发重排。动width、height、top、left、margin都会引发布局计算在低端设备上就是掉帧的根源。4.2 按钮反馈与内联 loading登录按钮是页面上最重要的元素它的反馈设计直接决定用户的操作信心。我给它做三层状态默认态、按压态、加载态。按压态用hover-class加载态用数据驱动view classbtn :class{ btn--disabled: loading } hover-classbtn--hover :hover-stay-time80 clickhandleLogin view v-ifloading classbtn__spinner/view text classbtn__text{{ loading ? 登录中 : 登 录 }}/text /view.btn { height: 96rpx; border-radius: 48rpx; display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #3b6fff 0%, #5a8bff 100%); transition: transform 0.15s ease, opacity 0.15s ease; } .btn--hover { opacity: 0.92; transform: scale(0.985); } .btn--disabled { opacity: 0.6; } .btn__spinner { width: 32rpx; height: 32rpx; margin-right: 12rpx; border: 4rpx solid rgba(255, 255, 255, 0.35); border-top-color: #ffffff; border-radius: 50%; animation: spin 0.7s linear infinite; } keyframes spin { to { transform: rotate(360deg); } }transition用transform和opacity同样是为了避开重排。hover-stay-time设小一点80ms 左右按钮的按压感会更脆设大了会有种粘滞感。4.3 键盘弹起适配adjust-position的两难这是登录页最麻烦的一个问题。小程序的input有个adjust-position属性默认true意思是键盘弹起时页面自动上推保证输入框可见。这个默认行为在普通文档流页面里很好用但如果你的登录页用了position: fixed的布局上推机制会失效或者错位——因为在fixed定位下页面的滚动容器高度没有变化系统不知道往哪儿推。我的处理方式分两种情况如果页面是普通流式布局保留adjust-positiontrue同时给输入框设cursor-spacing让输入框和键盘之间留出呼吸空间。如果页面用了固定布局就把adjust-position关掉手动监听键盘高度const keyboardHeight ref(0); onMounted(() { uni.onKeyboardHeightChange(res { keyboardHeight.value res.height; }); }); onUnload(() { uni.offKeyboardHeightChange(); });拿到高度之后用translateY把整个表单卡片往上推推的距离是键盘高度 - 卡片底部到屏幕底部的距离 一点余量。这个计算必须在真机上调因为不同机型的键盘高度差异很大iOS 的候选词栏高度也不一样。提示uni.offKeyboardHeightChange()一定要在onUnload里调用。这个监听是全局的不取消的话切到其他页面还会继续触发回调在一些低内存机型上会引发奇怪的渲染问题。5. 从点击按钮到拿到 token登录流程的完整链路UI 做完了不代表登录页做完了。真正的登录流程涉及校验、防重复提交、授权方式选择、token 存储和页面跳转这一段出问题往往是页面好看但用户登不进去。5.1 防重复提交一个标志位不够用户手快连点两下登录按钮会产生两个请求。第一个请求成功拿到 token第二个请求可能因为验证码已失效而返回错误结果用户看到一个登录失败的提示实际已经登录成功了。这种问题很难复现但用户投诉里经常出现。最基础的方案是加一个loading标志位async function handleLogin() { if (loading.value) return; if (!validateAll()) return; loading.value true; try { const res await loginApi({ ...form }); saveToken(res.token); uni.reLaunch({ url: /pages/home/index }); } catch (e) { uni.showToast({ title: e.message || 登录失败, icon: none }); } finally { loading.value false; } }finally里重置loading很重要。如果只写在try里请求异常时按钮会永远卡在加载态用户只能杀掉小程序。但标志位有个漏洞如果请求很快返回但页面跳转有延迟用户在这段间隙里还是能点到。我的补充做法是加一个时间戳节流两次点击间隔小于 800ms 直接忽略。另外在reLaunch之前把按钮保持在禁用状态不要提前解锁。请求本身也要设超时。小程序的uni.request默认超时 60 秒对登录接口来说太长了用户会以为小程序死了。我在登录接口上单独设timeout: 1000010 秒没响应就给个明确提示。5.2 三种授权方式的取舍微信小程序里做登录大致有三条路各有各的适用场景。方式适用主体用户体验主要限制账号密码登录不限需要输入、注册成本高依赖自建账号体系微信登录code 换 openid不限一键完成、最轻只能拿到 openid拿不到手机号手机号快捷登录需企业主体一键授权、拿到手机号个人主体用不了账号密码登录适合已有用户体系的业务比如从 App 迁移过来的项目。它的实现最直接但要做密码加密传输、登录失败次数限制、找回密码入口配套工作不少。微信登录是绝大多数小程序的首选。流程是调用uni.login()拿到code把code发给后端后端用code去换openid和session_key然后后端生成本业务的 token 返回给前端。这里有两个必须记住的点。第一code是一次性的用过就失效绝不能缓存在前端重复使用。第二code有效期只有 5 分钟而且用户每次调用uni.login()都可能拿到新的code所以一定要即时获取、即时使用。async function wxLogin() { const { code } await uni.login({ provider: weixin }); if (!code) throw new Error(获取登录凭证失败); const res await uni.request({ url: ${BASE_URL}/auth/wx-login, method: POST, data: { code }, timeout: 10000 }); return res.data; }session_key绝对不能下发给前端它是解密用户敏感数据的密钥只能留在服务端。手机号快捷登录需要用到button的open-typegetPhoneNumber。注意这个能力的门槛需要小程序主体为企业或符合条件的组织个人主体用不了。另外这个组件是按次计费的个人练手项目上要留个心。button classbtn btn--wx open-typegetPhoneNumber getphonenumberonGetPhoneNumber 微信手机号一键登录 /button回调里拿到的是加密的encryptedData和iv新版可能是code必须发给后端解密前端解不了也不该解。还有一个容易踩的坑早年的getUserProfile接口现在已经被回收了拿不到真实的头像和昵称。现在的做法是用「头像昵称填写能力」也就是button open-typechooseAvatar配合input typenickname。如果你的登录页还写着授权获取头像昵称然后调getUserProfile用户看到的头像会是灰色的默认图。5.3 token 存储与登录后的跳转策略拿到 token 之后存哪里我的答案是uni.setStorageSync不用全局变量。原因是小程序随时可能被系统回收全局变量一关页面就没了用户下次进来又要重新登录。Storage是持久化的除非用户手动清缓存或者卸载。当然敏感度高的 token 建议加一层过期时间在拦截器里判断有效期过期就跳回登录页。const TOKEN_KEY app_token; export function saveToken(token) { uni.setStorageSync(TOKEN_KEY, { value: token, expireAt: Date.now() 7 * 24 * 3600 * 1000 }); } export function getToken() { const data uni.getStorageSync(TOKEN_KEY); if (!data || !data.value) return ; if (Date.now() data.expireAt) { uni.removeStorageSync(TOKEN_KEY); return ; } return data.value; }跳转用uni.reLaunch而不是uni.navigateTo。原因很直接navigateTo会把登录页留在页面栈里用户在新页面点返回键就回到了登录页而这时他已经登录了页面状态很尴尬。reLaunch会清空整个页面栈是最干净的做法。另外登录页的onLoad里应该加一个判断如果已经有有效 token直接reLaunch到首页不要等用户再点一次登录。这个逻辑在登录态失效被踢回登录页的场景下尤其有用。onLoad(() { if (getToken()) { uni.reLaunch({ url: /pages/home/index }); } });但要注意一点不能无条件跳走。如果用户是主动点了退出登录回来的那就应该正常展示登录页。我的做法是退出登录时先清 token再跳登录页同时带一个logout1的参数登录页看到这个参数就跳过自动跳转判断。6. 踩坑记录跑通了但真机上会出问题的那些细节开发者工具里显示完美真机上打开是另一回事。这是我做小程序这几年感受最深的一点。下面几个问题我几乎每个项目都会遇到。6.1 工具与真机的差异到底差在哪差异主要来自三个地方渲染引擎、字体、以及设备的 GPU 能力。阴影和渐变。开发者工具用的是简化渲染阴影的参数看起来会更实一些。真机上尤其是安卓box-shadow的模糊半径超过 40rpx 后会有明显的带状分层。解决办法是把模糊半径压到 32rpx 以内或者改用一层极浅的边框色来模拟。字体。工具里用的是系统默认字体iOS 上是苹方安卓上各家都不一样。同一个字号在安卓上会显得比 iOS 大一圈行高也会不同。所以我的建议是不要用line-height: normal全部显式指定行高字号不要用奇数28rpx比29rpx稳。圆角溢出。iOS 上有个老问题父元素设了border-radius和overflow: hidden子元素还是可能溢出圆角。加一句transform: translateZ(0)触发合成层就能解决。但要注意这会创建新的层叠上下文可能会让内部绝对定位元素的层级表现发生变化。滚动条和回弹。iOS 的页面回弹如果你不想要得在pages.json里配置安卓的scroll-view滚动条样式在真机和工具里完全不同几乎没法统一我的做法是直接隐藏它。6.2 iOS 上的聚焦与光标问题autofocus这个属性在小程序里非常不可靠。iOS 出于安全策略不允许页面加载时自动聚焦输入框并唤起键盘因为这会打断用户。所以你会看到工具里autofocus生效、真机上没反应。正确的做法是延后聚焦页面加载后延迟一小段时间再通过focus属性触发。但即使这样iOS 上第一次也可能不弹键盘需要用户先有一次页面交互。所以我的建议是登录页不要执着于自动聚焦把它当成一个锦上添花的效果不要依赖它。光标颜色用cursor-color属性设置默认是系统蓝和你的主题色不搭时很突兀。这个属性在安卓上支持良好iOS 上偶尔不生效属于可接受的降级。还有就是密码框的password属性切换导致光标跳到开头的问题前面提过。如果你要做眼睛图标切换明文我的建议是加一个判断只有当光标在输入框末尾时才允许切换否则给一个提示。6.3 包体积与首屏登录页必须放在主包小程序的规则是主包最大 2MB总包含所有分包最大 20MB。登录页是小程序启动后用户看到的第一个或者第二个页面绝对不能放在分包里否则要等分包下载完才能显示白屏时间会拉长。省体积的几个实操点背景装饰全部用 CSS不要图片。这一条前面讲过收益最大。logo 如果一定要用图片优先考虑 SVG小程序支持 base64 形式的 svg 通过image组件渲染或者压到 10KB 以内的 webp。base64 内联到 CSS 里能减少一次请求但会增大包体积超过 4KB 就不划算了。不要为了一个登录按钮引入完整的 UI 组件库。有些组件库单个组件也要几百 KB 的运行时依赖如果项目里只有登录页用可以直接手写。首屏动画不要等数据。页面的入场动画应该是纯 CSS 的不依赖接口返回这样即使在弱网下用户也能立刻看到内容感知上的卡顿会小很多。另外提一句骨架屏。我自己在登录页是不用骨架屏的因为登录页的结构是静态的没有数据要等直接渲染比骨架屏更快。骨架屏适合那些首屏要拉列表数据的页面。提示如果你用uni.getSystemInfoSync()拿状态栏高度来算自定义导航栏记得在真机上验证。这个接口在开发者工具返回的是模拟值通常是 20而在 iPhone 12 以后的机型上真实值是 44 或 47。用模拟值调样式真机上一定错位。我在实际项目里踩过最深的一个坑是登录页的错误提示文案直接用了后端返回的message结果某次后端返回了一段技术性的英文报错用户看到一屏乱码。后来我改成前端统一映射错误码后端文案只写进日志。这个习惯后来在很多页面都救了我——面向用户的文案永远不要直接透传底层信息。
返回列表