用 SimpleStart 实现用户登录功能:由数据驱动界面自动切换
SimpleStart是一个纯 Python 的 Web 应用框架:你只写 Python 脚本,它负责把界面渲染到浏览器——不需要 HTML、CSS、JavaScript,也没有 npm、webpack 这类前端工具链。首次接触的读者,可以从第 1 篇入门看起。
上篇我们用
ss.state做了自动判分测验:答案是数据,颜色是数据,得分条文案也是数据。这篇再进一步——做一个用户登录系统:登录卡片和欢迎卡片的切换、错误提示的出现与消失,同样不写一行"更新界面"的代码。连"哪张卡片可见"也由数据驱动。全程 37 行。
思路:把"当前该显示什么"也交给状态
传统写法里,"显示登录页还是欢迎页"是一段命令式流程:校验 → 隐藏 A → 显示 B → 填文案,每一步都在操作界面。这篇继续上篇的思路,并把范围再扩大一步——上篇数据驱动的是"值和颜色",这篇把界面本身的显示与隐藏也交给状态:两个状态位各绑一张卡片的visible,登录成功只是翻转两个布尔值,界面自动完成切换——这就是响应式更新的效果。
第一步:账号表 + 一份登录态
USERS={"guest":"123456","test":"12345"}auth=ss.State(logged_in=False,logged_out=True,user="",pwd="",msg="请输入用户名和密码",hello="")演示账号表就一个 dict(真实系统换成数据库查询,界面代码零改动)。auth里 6 个状态位各有分工:user/pwd存输入框的内容,msg存提示文案,hello存欢迎语,logged_in/logged_out分别控制两张卡片的显示与隐藏。
第二步:登录与登出的回调函数,只读写状态
deflogin(event):ifUSERS.get(auth.user.value)==auth.pwd.value:auth.logged_in,auth.logged_out=True,Falseauth.hello=f"欢迎回来,{auth.user.value}!"auth.msg=""else:auth.msg="❌ 用户名或密码错误,请重试"deflogout(event):auth.update(logged_in=False,logged_out=True,user="",pwd="",msg="已退出登录")和上篇的写法完全一致:
- 读状态显式
.value,口诀不变:传给组件用属性本身,写逻辑用.value; USERS.get(...)查不到返回None,和任何密码都不相等——避免了KeyError,空用户名也会直接进入"错误"分支(真实系统记得密码哈希入库,这里只做演示);- 两个回调没有任何组件引用:登录成功只是翻转两个布尔值、填两段文案;登出是一次
update全部还原。至于哪张卡片显示、哪张隐藏,由绑定关系决定。
第三步:两张卡片,显示与隐藏绑定在状态上
withss.row(justify="center",style="margin-top:80px;"):withss.card(width=380,padding=32,visible=auth.logged_out):ss.write("## 登录")ss.space("mb-4")ss.input(value=auth.user,placeholder="用户名",block=True)ss.space("mb-4")ss.input(value=auth.pwd,type="password",placeholder="密码",block=True)ss.space("mb-4")ss.button("登 录",type="primary",block=True,onclick=login)ss.space("mt-2")ss.text(auth.msg,type="error")withss.card(width=380,padding=32,visible=auth.logged_in):ss.text(auth.hello,type="success")ss.space("mb-4")ss.button("退出登录",block=True,onclick=logout)三个看点:
visible=auth.logged_out/visible=auth.logged_in:组件参数接的是 State 属性,显隐就成了对状态的订阅——状态位翻转后,卡片自动显示或隐藏,全程没有"先获取组件再设置属性"这类操作;- 输入框连
onchange都没写。上篇的 radio 需要在onchange里把选择写回 State;这里ss.input(value=auth.user)是双向绑定:输入实时写回auth.user,点击登录时auth.user.value已经是最新值。绑定还是同一个参数,方向由组件性质决定; - 错误提示
ss.text(auth.msg, type="error"):失败时msg更新文案,错误提示显示;成功时msg = "",错误提示隐藏——提示的出现与消失同样由状态驱动。
一次登录过程中,状态的完整流转
| 时机 | logged_in | logged_out | msg | 用户看到 |
|---|---|---|---|---|
| 页面加载 | False | True | 请输入用户名和密码 | 登录卡片 |
| 密码错误 | False | True | ❌ 用户名或密码错误 | 登录卡片 + 错误提示 |
| 登录成功 | True | False | (空) | 欢迎卡片 |
| 退出登录 | False | True | 已退出登录 | 登录卡片 |
四次界面切换,没有一行"更新界面"的代码。命令式写法中最容易出问题的正是这类代码:loginForm.style.display = "none"……写错一个 id、漏一个分支,界面和数据就会不一致;状态驱动的方式从机制上避免了这类不一致。
小结
回顾这个 37 行的登录系统:
- 视图切换同样由数据驱动。上篇驱动的是值和颜色,这篇把卡片的显隐也交给状态——"当前该显示什么"不再是散落在回调里的 if/else,而是两行绑定;
- 双向绑定让输入控件零回调。
value=auth.user一个参数,输入写回状态、点击时读到的就是最新值——表单类需求先想绑定,再想回调; - 逻辑层的结构不变。还是"只读写状态",还是
.value取值——从测验到登录,应用换了一个领域,代码结构原样复用,这正是分层设计带来的可复用性; - 应用易于扩展。连续输错锁定、“记住我”、会话超时,都只是给 State 增加状态位、给绑定增加条件,界面代码不用改。
跑起来
pipinstallsimplestart ss login.py# 四段代码拼起来存成 login.py,或直接用仓库里的 tutorials/login.py试账号guest / 123456。先输入错误密码查看错误提示,再依次测试登录成功与退出登录。
下一步
这个登录系统还有可以工程化的地方,也是后续篇章的引子:用@auth.watch("logged_in")监听状态,登录成功自动写入ss.LocalStorage,刷新页面不掉登录态;用ss.openpage在登录后跳转主应用,就进入多页应用的场景——侧边栏、页面路由、布局结构,留到布局篇展开。
- 在线文档(中文):http://www.simplestart.cc/zh
- Documentation (English):http://www.simplestart.cc/
- GitHub(本文完整代码:tutorials/login.py):https://github.com/readever/simplestart
pip install simplestart,给你手头任何一个 Python 小工具加上用户登录功能试试。做出了有意思的应用,欢迎来 GitHub Issues 交流。
本系列文章
- 00 SimpleStart:用 Python 开发 Web 应用的框架(综述)
- 01 SimpleStart 入门:不写一行前端,用纯 Python 构建 Web 应用
- 02 用 SimpleStart 做个自动判分测验:ss.state 双向数据绑定入门
- 03 用 SimpleStart 实现用户登录功能:由数据驱动界面自动切换(本文)