首页 / 资讯中心 / 文章详情

ReactPy 界面构建入门:用 Python 创建 HTML 布局、组件与数据渲染

ReactPy 界面构建入门:用 Python 创建 HTML 布局、组件与数据渲染 ★ FEATURED ARTICLE
前端UI组件【免费下载链接】reactpyIts React, but in Python项目地址https://gitcode.com/gh_mirrors/re/reactpy点击查看免费下载ReactPy 是一款用 Python 写 React的 UI 框架它允许开发者完全在 Python 一侧定义视图结构及其背后的数据与逻辑。本文以 Creating Interfaces 指南 为主线从零讲解如何用reactpy.html在 Python 中构建 HTML 布局、如何用component装饰器定义可复用组件以及如何基于集合数据动态渲染并借助key正确组织元素——读完即可动手写出第一个可运行的 ReactPy 界面。背景为什么要在 Python 中构建界面在典型的 Python Web 应用中定义视图与承载数据与逻辑这两项职责通常被拆分在客户端与服务器两侧后端输出模板或 API前端负责渲染与交互。ReactPy 将这两件事集中到同一个地方——直接在 Python 里用函数调用构造 HTML 界面。这一设计的基础就是允许开发者用 Python 语法而非模板字符串或 JSX描述 HTML 结构。以文档开篇的待办清单为例标准 HTML 写作如下h1My Todo List/h1 ul liBuild a cool new app/li liShare it with the world!/li /ul在 ReactPy 中同样的布局用reactpy.html模块内的函数复现from reactpy import html html.h1(My Todo List) html.ul( html.li(Build a cool new app), html.li(Share it with the world!), )可以看出reactpy.html中的每个函数都与对应的 HTML 标签同名h1/对应html.h1ul对应html.ulli对应html.li。函数嵌套的调用结构天然映射了标签的父子层级。注意只调用这些函数还不够。上面的代码没有把返回值保存下来因此无法被渲染。要把整块布局变成一个可用的 UI需要用一个html.div包裹并赋值给变量layout html.div( html.h1(My Todo List), html.ul( html.li(Build a cool new app), html.li(Share it with the world!), ), )给元素添加 HTML 属性AttributesHTML 不只是文本元素往往还需要属性。以img图片元素为例标准 HTML 会这样指定图片来源、样式、替代文本等img srchttps://picsum.photos/id/237/500/300 classimg-fluid stylewidth: 50%; margin-left: 25%; altBillie Holiday tabindex0 /ReactPy 中这些属性通过一个字典传给元素函数html.img( { src: https://picsum.photos/id/237/500/300, class_name: img-fluid, style: {width: 50%, margin_left: 25%}, alt: Billie Holiday, } )两者之间有三点关键差异这也是 HTML 与 ReactPy 章节 强调的核心规则命名风格ReactPy 中的所有属性名使用snake_case而不是 HTML 的连字符分隔写法。例如tabindex写作tab_indexmargin-left写作margin_left。样式用字典style属性不再使用字符串而是用一个字典描述 CSS 属性如{width: 50%, margin_left: 25%}。这样避免了在字符串中转义引号和其他特殊字符的麻烦。class 改名HTML 的class属性在 ReactPy 中改为class_name以避免与 Python 的class关键字冲突。完整的受支持属性列表及与 HTML 的差异参见 HTML 属性参考。关于 ReactPy 内部如何用数据结构表示 HTML可进一步阅读 表示 HTML。组件把 UI 元素组合成可复用积木随着文档结构变大变复杂直接操作这些细粒度 UI 元素会变得难以维护。ReactPy 允许把这些元素分组为组件components组件可以在整个应用中反复使用。定义组件component装饰器组件本质上就是返回 HTML 的普通 Python 函数。唯一的特殊之处在于定义组件需要给函数加上component装饰器。被装饰的函数被称为渲染函数render function按惯例使用CamelCase命名与类命名一致。以文档中的Photo组件为例from reactpy import component, html, run component def Photo(): return html.img( { src: https://picsum.photos/id/237/500/300, style: {width: 50%}, alt: Puppy, } ) run(Photo)完整示例见 simple_photo.py。末尾的run(Photo)会在本地启动服务器并渲染该组件相关运行方式详见 运行 ReactPy。警告如果忘记给Photo的渲染函数加上component装饰器服务器仍能正常启动但一旦访问页面就会是空白服务端日志会报错TypeError: Expected a ComponentType, not dict.组件嵌套与复用组件定义一次后可以被任意嵌套进其他组件。下面定义一个父级Gallery组件它返回多个Photo组件——这正是组件的威力所在一次定义处处复用见 nested_photos.pyfrom reactpy import component, html, run component def Photo(): return html.img( { src: https://picsum.photos/id/274/500/300, style: {width: 30%}, alt: Ray Charles, } ) component def Gallery(): return html.section( html.h1(Famous Musicians), Photo(), Photo(), Photo(), ) run(Gallery)返回单一根元素组件必须返回一个单一根元素。这个根元素可以有自己的子元素但你不能从一个组件里直接返回一个元素列表并指望它被正确渲染。如果需要返回多个元素必须用类似html.div的东西把它们包起来见 wrap_in_div.pycomponent def MyTodoList(): return html.div( html.h1(My Todo List), html.img({src: https://picsum.photos/id/0/500/300}), html.ul(html.li(The first thing I need to do is...)), )如果不想为了包裹而额外引入一个div可以使用片段fragment——即html._函数见 wrap_in_fragment.py。片段可以分组元素但不会在 UI 中留下任何痕迹。component def MyTodoList(): return html._( html.h1(My Todo List), html.img({src: https://picsum.photos/id/0/500/200}), html.ul(html.li(The first thing I need to do is...)), )例如下面的 ReactPy 代码from reactpy import html html.ul( html._( html.li(Group 1 Item 1), html.li(Group 1 Item 2), html.li(Group 1 Item 3), ), html._( html.li(Group 2 Item 1), html.li(Group 2 Item 2), html.li(Group 2 Item 3), ) )渲染后得到的 HTML 中不会出现任何额外的包裹节点ul liGroup 1 Item 1/li liGroup 1 Item 2/li liGroup 1 Item 3/li liGroup 2 Item 1/li liGroup 2 Item 2/li liGroup 2 Item 3/li /ul组件参数化组件既然是普通函数自然可以接收参数让父组件把信息传给子组件。与标准 HTML 元素用字典传参不同组件像普通函数一样接受位置参数和关键字参数。见 parametrized_photos.pyfrom reactpy import component, html, run component def Photo(alt_text, image_id): return html.img( { src: fhttps://picsum.photos/id/{image_id}/500/200, style: {width: 50%}, alt: alt_text, } ) component def Gallery(): return html.section( html.h1(Photo Gallery), Photo(Landscape, image_id830), Photo(City, image_id274), Photo(Puppy, image_id237), ) run(Gallery)条件渲染用if与内联表达式切换 UI组件经常需要依据不同条件展示不同内容。考虑一个待办清单其中只有部分条目已完成。基础实现如下见 todo_list.pyItem组件并没有根据done改变外观component def Item(name, done): return html.li(name) component def TodoList(): return html.section( html.h1(My Todo List), html.ul( Item(Find a cool problem to solve, doneTrue), Item(Build an app to solve it, doneTrue), Item(Share that app with the world!, doneFalse), ), )假如想给doneTrue的条目加上 ✔ 标记一种方式是用if语句条目完成时返回一种li未完成时返回另一种见 bad_conditional_todo_list.pycomponent def Item(name, done): if done: return html.li(name, ✔) else: return html.li(name)这个写法能达成目的但html.li(name, ✔)与html.li(name)高度相似分支重复。更简洁、更易维护的做法是使用内联if表达式三元表达式见 good_conditional_todo_list.pycomponent def Item(name, done): return html.li(name, ✔ if done else )渲染数据从集合生成 UI 元素实际开发中经常需要根据一批数据构造大量相似的元素。文档以可按优先级排序与过滤的待办清单为例展开。目标视图形如ul liMake breakfast (important)/li liFeed the dog (important)/li liDo laundry/li liGo on a run (important)/li liClean the house/li liGo to the grocery store/li liDo some coding/li liRead a book (important)/li /ul从列表渲染元素最直接的做法是把每个li的文本放进一个 Python 列表tasks [ Make breakfast (important), Feed the dog (important), Do laundry, Go on a run (important), Clean the house, Go to the grocery store, Do some coding, Read a book (important), ]然后用列表推导式把这个列表渲染成一串li元素再整体传入父级ulfrom reactpy import html list_item_elements [html.li(text) for text in tasks] list_element html.ul(list_item_elements)最后把结果放进组件返回见 todo_from_list.pyfrom reactpy import component, html, run component def DataList(items): list_item_elements [html.li(text) for text in items] return html.ul(list_item_elements) component def TodoList(): tasks [ Make breakfast (important), Feed the dog (important), Do laundry, Go on a run (important), Clean the house, Go to the grocery store, Do some coding, Read a book (important), ] return html.section( html.h1(My Todo List), DataList(tasks), ) run(TodoList)过滤与排序元素仅用文本字符串列表无法表达优先级这一排序/过滤维度因此需要把数据结构升级为字典列表tasks [ {text: Make breakfast, priority: 0}, {text: Feed the dog, priority: 0}, {text: Do laundry, priority: 2}, {text: Go on a run, priority: 1}, {text: Clean the house, priority: 2}, {text: Go to the grocery store, priority: 2}, {text: Do some coding, priority: 1}, {text: Read a book, priority: 1}, ]接着可以用 Python 内置的filter思路此处用列表推导式和sorted实现过滤与排序只显示priority不大于某个filter_by_priority的条目并按priority排序filter_by_priority 1 sort_by_priority True filtered_tasks tasks if filter_by_priority is not None: filtered_tasks [t for t in filtered_tasks if t[priority] filter_by_priority] if sort_by_priority: filtered_tasks list(sorted(filtered_tasks, keylambda t: t[priority]))filter_by_priority1、sort_by_priorityTrue时过滤排序后的结果应为[ {text: Make breakfast, priority: 0}, {text: Feed the dog, priority: 0}, {text: Go on a run, priority: 1}, {text: Do some coding, priority: 1}, {text: Read a book, priority: 1}, ]把这些逻辑并入DataList组件见 sorted_and_filtered_todo_list.pycomponent def DataList(items, filter_by_priorityNone, sort_by_priorityFalse): if filter_by_priority is not None: items [i for i in items if i[priority] filter_by_priority] if sort_by_priority: items sorted(items, keylambda i: i[priority]) list_item_elements [html.li(i[text]) for i in items] return html.ul(list_item_elements)警告上面的代码会产生大量警告日志原因是列表中的元素缺少key请阅读下一节了解原因与解决办法。用 Key 组织列表项以调试模式运行上面的示例参见 以 Debug 模式运行 ReactPy时服务端会输出类似如下的报错Key not specified for child in list {tagName: li, children: ...}这表明列表中的每个待办条目没有指定唯一的key。要消除警告需要进一步扩展数据结构为每个条目添加唯一 IDtasks [ {id: 0, text: Make breakfast, priority: 0}, {id: 1, text: Feed the dog, priority: 0}, {id: 2, text: Do laundry, priority: 2}, {id: 3, text: Go on a run, priority: 1}, {id: 4, text: Clean the house, priority: 2}, {id: 5, text: Go to the grocery store, priority: 2}, {id: 6, text: Do some coding, priority: 1}, {id: 7, text: Read a book, priority: 1}, ]然后在构造li元素时声明key属性list_item_elements [html.li({key: t[id]}, t[text]) for t in tasks]这个key告诉 ReactPy列表中的哪个li对应tasks数据中的哪一项。当列表的顺序或条目数量可能变化时例如切换filter_by_priority或sort_by_prioritykey就至关重要。完整改造后的组件见 todo_list_with_keys.pycomponent def DataList(items, filter_by_priorityNone, sort_by_priorityFalse): if filter_by_priority is not None: items [i for i in items if i[priority] filter_by_priority] if sort_by_priority: items sorted(items, keylambda i: i[priority]) list_item_elements [html.li({key: i[id]}, i[text]) for i in items] return html.ul(list_item_elements)组件同样需要 Key到目前为止讨论的都是标准 HTML 元素的key但该原则同样适用于组件。所有被component装饰的函数都会自动获得一个key参数其作用与标准 HTML 元素完全一致from reactpy import component component def ListItem(text): return html.li(text) tasks [ {id: 0, text: Make breakfast}, {id: 1, text: Feed the dog}, {id: 2, text: Do laundry}, {id: 3, text: Go on a run}, {id: 4, text: Clean the house}, {id: 5, text: Go to the grocery store}, {id: 6, text: Do some coding}, {id: 7, text: Read a book}, ] list_element [ListItem(t[text], keyt[id]) for t in tasks]警告key参数是保留参数。如果定义的组件渲染函数本身带有名为key的参数会直接报错Traceback (most recent call last): ... TypeError: Component render function ... uses reserved parameter keyKey 的使用规则为避免渲染数据时出现意外行为必须遵守以下几条规则确保每条数据始终与正确的 UI 元素对应1. 非兄弟元素可以使用相同的 key如果两个元素在 UI 中拥有不同的父级它们可以使用相同的 keydata_1 [ {id: 1, text: Something}, {id: 2, text: Something else}, ] data_2 [ {id: 1, text: Another thing}, {id: 2, text: Yet another thing}, ] html.section( html.ul([html.li(data[text], keydata[id]) for data in data_1]), html.ul([html.li(data[text], keydata[id]) for data in data_2]), )2. 兄弟元素之间的 key 必须唯一data [ {id: 1, text: Something}, {id: 2, text: Something else}, {id: 1, text: Another thing}, # BAD: has a duplicated id {id: 2, text: Yet another thing}, # BAD: has a duplicated id ] html.section( html.ul([html.li(data[text], keydata[id]) for data in data]), )3. key 必须与数据绑定固定不要为了消除警告而给 key 生成随机值否则会导致意外行为from random import random data [ {id: random(), text: Something}, {id: random(), text: Something else}, {id: random(), text: Another thing}, {id: random(), text: Yet another thing}, ] html.section( html.ul([html.li(data[text], keydata[id]) for data in data]), )如何挑选 key 的值前面的示例数据量小手动添加id即可。但实际开发中往往要面对已存在的数据此时该如何选 key数据来自数据库直接使用数据库生成的键和 ID它们天然唯一。例如关系数据库记录的主键。数据在本地生成并持久化如笔记应用中的笔记创建条目时使用自增计数器或标准库的uuid模块。想深入了解 key 的必要性可阅读 为什么 ReactPy 需要 key。小结与下一步至此你已经掌握了 ReactPy 构建界面的三大支柱HTML with ReactPy用reactpy.html中与 HTML 标签同名的函数在 Python 中构造布局通过字典设置属性snake_case命名、style用字典、class改为class_nameYour First Components用component装饰普通函数定义可复用组件组件可嵌套、可参数化、必须返回单一根元素必要时用html._片段包裹并可用if/内联表达式做条件渲染Rendering Data用列表推导式从数据集合渲染元素通过filter/sorted逻辑过滤排序并用key将数据与 UI 元素稳定绑定兄弟元素间唯一、与数据固定对应。完成这些基础之后就可以进入下一步——添加交互Adding Interactivity让静态界面响应事件、管理状态。三个子章节的完整路线图见 Creating Interfaces 总览对应参考实现位于 reference/_examples 下的各类可运行示例如 click_count.py 与 todo.py。赞分享前端UI组件【免费下载链接】reactpyIts React, but in Python项目地址https://gitcode.com/gh_mirrors/re/reactpy点击查看免费下载相关推荐faster-whisper 如何上手一条命令装好 4 倍速的语音转文字引擎faster whisper 如何上手一条命令装好 4 倍速的语音转文字引擎 faster whisper 是 OpenAI 语音识别模型 Whisper 的人工智能语音音频本地部署ReactPy中的服务端组件开发创建服务器渲染组件ReactPy中的服务端组件开发创建服务器渲染组件 你是否在寻找一种方式能够用Python编写高效的Web界面同时享受React的组件化开发体验Reac前端UI组件ReactPy用Python构建用户界面的利器ReactPy用Python构建用户界面的利器 ReactPy 是一个开源项目旨在使用 Python 语言来构建用户界面无需依赖 JavaScript。该前端UI组件上一篇Zappa与AWS Translate多语言翻译无服务器应用开发下一篇JTS 距离计算高级技巧从平面距离到 Hausdorff 相似度度量创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站