Streamlit入门指南:快速构建数据科学Web应用的核心原理与实践
1. 项目概述:为什么是Streamlit?
如果你正在数据科学、机器学习或者任何需要快速构建交互式Web应用的领域里摸爬滚打,那你一定经历过这样的场景:花了好几天甚至几周时间训练出一个模型,或者完成了一个复杂的数据分析流程,最后却卡在了“如何展示给别人看”这一步。传统的Web开发,从HTML、CSS、JavaScript到后端框架,学习曲线陡峭,足以让一个专注于算法的数据科学家望而却步。你可能也试过用Flask或Dash,它们确实强大,但配置路由、设计前端组件、处理回调函数依然需要投入不少精力。
这就是Streamlit出现的背景,也是它迅速在数据社区爆火的原因。我第一次接触Streamlit是在2019年底,当时为了给团队内部演示一个实时数据监控面板,用Flask写了两天,各种前后端联调问题搞得焦头烂额。后来偶然看到Streamlit的简介——“The fastest way to build and share data apps”,抱着试试看的心态,结果只用了一个下午,一个功能完整、界面美观的应用就上线了,那种“原来可以这么简单”的震撼感,我至今记忆犹新。
简单来说,Streamlit是一个专为机器学习和数据科学团队设计的开源Python库。它的核心哲学是“将脚本变成可分享的Web应用”。你不需要学习任何前端知识,只需要像写普通的Python脚本一样,用Streamlit提供的API(比如st.write、st.slider、st.plotly_chart)将你的数据、图表和控件“写”出来,Streamlit会自动帮你处理所有的Web服务器、前端渲染和交互逻辑。它彻底改变了数据应用的构建方式,让开发者能够以近乎对话的速度,将想法转化为可交互的工具。
那么,这个教程适合谁呢?如果你是:
- 数据科学家/分析师:想快速将Jupyter Notebook中的分析过程打包成一个可交互的报告或仪表盘。
- 机器学习工程师:需要为模型构建一个简单的推理界面,供产品经理或测试人员试用。
- Python开发者:希望快速搭建一个内部工具,比如配置文件生成器、日志查询界面等。
- 任何想学习快速构建Web应用的初学者:厌倦了复杂的前端生态,想用最熟悉的Python语言快速实现想法。
那么,这个Streamlit入门系列就是为你准备的。在第一篇中,我们将彻底搞懂Streamlit的基本理念,并亲手搭建你的第一个应用,为后续更深入的功能打下坚实基础。
2. 核心设计理念与工作原理解析
在深入代码之前,理解Streamlit独特的设计哲学和工作原理至关重要。这能帮你避免很多初学者的困惑,比如“为什么我的应用运行这么慢?”或者“为什么这个按钮点了没反应?”。Streamlit的运作方式和我们熟悉的传统Web框架有根本性的不同。
2.1 “自上而下”的脚本执行模型
这是Streamlit最核心、也最需要适应的一点。Streamlit应用本质上就是一个Python脚本,它会从上到下、从头到尾地执行。
想象一下,你有一个普通的Python脚本app.py:
import streamlit as st import pandas as pd # 第一行:显示一个标题 st.title(‘我的数据仪表盘’) # 第二行:加载数据 data = pd.read_csv(‘data.csv’) # 第三行:显示数据 st.dataframe(data) # 第四行:创建一个滑块 threshold = st.slider(‘选择阈值:’, 0, 100, 50) # 第五行:根据滑块筛选并显示结果 filtered_data = data[data[‘value’] > threshold] st.write(f’筛选后数据量:{len(filtered_data)}‘) st.dataframe(filtered_data)当你运行streamlit run app.py时,Streamlit会:
- 启动一个本地Web服务器。
- 打开你的浏览器,访问这个服务器。
- 从头开始执行整个
app.py脚本,生成初始的页面内容。 - 当你移动了滑块(第四行的
st.slider),Streamlit会感知到这个交互事件。 - 关键来了:Streamlit会从头开始重新执行整个
app.py脚本。 - 在重新执行时,
st.slider会返回滑块新的位置值(比如从50变成了75)。 - 脚本继续执行第五行,用新的
threshold=75去筛选数据,并更新显示。
注意:这个“从头重新执行”的机制是理解Streamlit状态管理的基础。它意味着你的脚本必须是“幂等”的,即给定相同的输入(用户交互状态),应该产生相同的输出(页面内容)。你不能依赖脚本执行过程中的“记忆”,所有需要持久化的状态都必须通过Streamlit提供的状态管理API(如
st.session_state)来保存。
2.2 组件即函数:声明式UI构建
传统Web开发是“命令式”的:你需要先定义HTML结构(有什么),再用JavaScript去操作DOM元素改变它们(做什么)。Streamlit是“声明式”的:你直接“声明”你希望页面上有什么。
st.button(‘点击我’)不是一个创建按钮然后你需要手动绑定点击事件的指令,而是一个声明:“这里要有一个按钮”。Streamlit负责渲染它,并在用户点击时,让这个函数在脚本重新执行时返回True。
这种模式极大地简化了开发:
- 无需回调函数链:在Dash或Plotly中,你需要用
@app.callback装饰器明确指定哪个输入组件的改变会触发哪个函数去更新哪个输出组件。在Streamlit中,一切更新都通过脚本的重新执行自动发生。 - 布局即代码顺序:你的UI布局就是代码的书写顺序。先写的代码显示在上面,后写的显示在下面。当然,Streamlit也提供了
st.columns、st.container等更灵活的布局组件。
2.3 会话状态(Session State):跨越重新执行的内存
由于脚本每次交互都会重新执行,那么如何记住一些信息呢?比如用户登录名、一个复杂计算的时间中间结果,或者一个累加器的值?这就是st.session_state的用武之地。
你可以把st.session_state想象成一个Python字典,但它与每个用户的浏览器标签页(会话)唯一绑定。在脚本重新执行时,这个字典里的内容会被保留。
import streamlit as st # 初始化计数器 if ‘click_count’ not in st.session_state: st.session_state.click_count = 0 # 按钮 if st.button(‘点击增加’): # 当按钮被点击,脚本重新执行,走到这里时,session_state中的值被保留并可以修改 st.session_state.click_count += 1 # 显示计数器,这个值在多次重新执行中得以保持 st.write(f’按钮被点击了 {st.session_state.click_count} 次‘)理解并熟练运用st.session_state,是构建复杂、有状态Streamlit应用的关键。
3. 从零开始:搭建你的第一个Streamlit应用
理论说得再多,不如亲手跑一遍。我们来一步步创建一个最简单的应用,并理解背后的每一个环节。
3.1 环境准备与安装
首先,确保你有一个Python环境(推荐3.8及以上版本)。使用虚拟环境是一个好习惯,可以避免包依赖冲突。
# 1. 创建并激活虚拟环境(以venv为例) python -m venv streamlit-env # Windows: streamlit-env\Scripts\activate # macOS/Linux: source streamlit-env/bin/activate # 2. 安装Streamlit pip install streamlit # 3. 验证安装 streamlit hello运行streamlit hello命令后,它会启动一个示例应用并在你的默认浏览器中打开。这个示例应用完美展示了Streamlit的各种能力,强烈建议新手花几分钟浏览一下。
3.2 第一个脚本:”Hello, Streamlit!”
创建一个新文件,命名为first_app.py,用任何文本编辑器或IDE打开,输入以下代码:
import streamlit as st # 设置页面配置,这必须是Streamlit命令的第一个调用 st.set_page_config( page_title=“我的第一个应用”, page_icon=“🎈”, # 可以是表情符号或图片路径 layout=“wide”, # “centered” 或 “wide” initial_sidebar_state=“expanded”, # “auto”, “expanded”, “collapsed” ) # 添加一个标题 st.title(‘🎈 欢迎来到我的Streamlit应用!’) # 添加一些文本 st.write(‘这是我的第一个Streamlit应用,它运行在本地。’) # 显示Markdown格式文本 st.markdown(‘’‘ ### 关于Streamlit - **快速**:几分钟内构建应用 - **简单**:无需前端知识 - **强大**:集成所有主流数据科学生态库 ‘’‘) # 创建一个输入框,并获取输入内容 user_name = st.text_input(‘请输入你的名字:’, ‘张三’) # 根据输入内容动态生成问候语 st.write(f’你好,{user_name}!很高兴见到你!‘) # 添加一个按钮 if st.button(‘点我惊喜’): st.balloons() # 释放气球动画 st.success(‘🎉 惊喜就是一堆气球!’)保存文件后,在终端(确保虚拟环境已激活)中,导航到文件所在目录,运行:
streamlit run first_app.py几秒钟后,你的浏览器会自动打开http://localhost:8501,看到你的应用。尝试在输入框里打字,或者点击按钮,感受一下交互的即时性。
实操心得:
st.set_page_config必须是你脚本中第一个Streamlit命令,且只能调用一次。把它放在最开头是个好习惯。layout=“wide”可以让你的应用充分利用屏幕宽度,在展示数据表格或并排图表时特别有用。
3.3 核心显示与数据展示函数详解
Streamlit提供了丰富的函数来输出各种内容。掌握这些基础函数,就掌握了构建应用的砖瓦。
1. 文本输出
st.write():瑞士军刀,几乎可以输出任何对象(字符串、数字、DataFrame、字典、图表对象、Matplotlib图形等)。Streamlit会自动判断类型并正确渲染。在开发初期,用st.write()来调试和快速查看变量内容非常方便。st.text(): 输出纯文本,不解析Markdown。st.markdown(): 输出Markdown格式文本,这是美化文本、添加链接、创建简单列表和表格的主要工具。st.latex(): 渲染LaTeX公式,对于学术或技术展示非常有用。st.title(),st.header(),st.subheader(): 输出不同层级的标题,用于组织页面结构。
2. 数据展示
st.dataframe(): 展示Pandas DataFrame的交互式表格。支持排序、缩放、搜索。这是展示数据的主力。import pandas as pd import numpy as np df = pd.DataFrame(np.random.randn(10, 5), columns=(‘A’, ‘B’, ‘C’, ‘D’, ‘E’)) st.dataframe(df.style.highlight_max(axis=0)) # 甚至可以使用Pandas的Stylerst.table(): 展示静态表格(非交互式)。适合展示小型、固定的数据。st.json(): 美观地展示JSON对象,可折叠展开,适合展示API响应或配置。
3. 图表展示Streamlit本身不绘制图表,但它无缝集成了所有主流绘图库。
- Matplotlib/Seaborn: 直接用
st.pyplot(fig)。 - Plotly: 用
st.plotly_chart(fig)。Plotly的交互性(缩放、平移、悬停提示)会被完整保留。 - Altair: 用
st.altair_chart(chart)。声明式统计图表的绝佳选择。 - Bokeh, Pydeck等: 都有对应的
st.bokeh_chart(),st.pydeck_chart()函数。
import matplotlib.pyplot as plt import plotly.express as px # Matplotlib示例 fig, ax = plt.subplots() ax.plot([1, 2, 3, 4], [1, 4, 2, 3]) st.pyplot(fig) # Plotly示例 df = px.data.iris() fig = px.scatter(df, x=“sepal_width”, y=“sepal_length”, color=“species”) st.plotly_chart(fig, use_container_width=True) # use_container_width让图表自适应宽度注意事项:对于Matplotlib,建议显式创建图形和坐标轴对象(
fig, ax = plt.subplots()),而不是依赖plt.plot()的隐式状态,这在与Streamlit的重新执行模型配合时更可靠。对于Plotly,use_container_width=True参数非常实用,能让图表填充可用空间。
4. 核心交互组件全解析
交互是应用的精髓。Streamlit提供了一系列简洁的组件来捕获用户输入。
4.1 基础输入组件
这些组件会阻塞脚本执行,直到用户与之交互(在脚本重新执行的上下文中理解)。
- 按钮(
st.button): 点击时返回True(仅限当前执行周期)。if st.button(‘清空数据’): # 这里处理清空逻辑,例如重置session_state st.session_state.clear() st.rerun() # 手动触发重新运行以更新界面 - 复选框(
st.checkbox): 返回布尔值。show_details = st.checkbox(‘显示详细信息’) if show_details: st.write(‘这里是隐藏的详细信息...’) - 单选按钮(
st.radio): 从列表中选择一项。option = st.radio(‘选择分析维度:’, (‘时间’, ‘地区’, ‘产品类别’), index=0) - 下拉选择框(
st.selectbox): 功能同单选,但以紧凑的下拉形式呈现,适合选项多时。 - 多选框(
st.multiselect): 允许选择多个选项,返回一个列表。 - 滑块(
st.slider): 选择数值范围。支持整型、浮点型、日期、时间。age = st.slider(‘你的年龄?’, 0, 130, 25) values = st.slider(‘选择一个范围’, 0.0, 100.0, (25.0, 75.0)) # 范围滑块 - 文本输入(
st.text_input): 单行文本。 - 文本域(
st.text_area): 多行文本。 - 数字输入(
st.number_input): 带增减按钮的数字输入。 - 文件上传器(
st.file_uploader):极其重要的组件,允许用户上传文件。uploaded_file = st.file_uploader(“选择一个CSV文件”, type=“csv”) if uploaded_file is not None: df = pd.read_csv(uploaded_file) st.dataframe(df)
4.2 组件的高级用法与键值参数
每个组件函数都返回用户输入的值。但有一个关键参数key,它和st.session_state紧密相关。
# 没有使用key name1 = st.text_input(‘输入名字(无key)’) st.write(‘值:’, name1) # 使用key name2 = st.text_input(‘输入名字(有key)’, key=“user_name”) st.write(‘值:’, name2) st.write(‘session_state中的值:’, st.session_state.user_name)区别:
- 没有
key:组件的值仅存在于当前脚本执行的返回值中。如果其他逻辑需要引用这个值,你需要通过变量传递或存入session_state。 - 有
key:组件的值会自动双向绑定到st.session_state[‘your_key’]。你既可以通过返回值name2获取,也可以直接通过st.session_state.user_name获取。更重要的是,当你以编程方式修改st.session_state.user_name时,前端输入框显示的值也会自动更新!这是实现复杂交互的基石。
4.3 布局与容器组件
当元素越来越多时,你需要组织它们。Streamlit的布局是自上而下的流式布局,但提供了容器来创建更复杂的结构。
侧边栏(
st.sidebar): 将组件放入侧边栏。# 所有放在st.sidebar下的组件都会出现在侧边栏 with st.sidebar: st.header(‘控制面板’) option = st.selectbox(‘菜单’, [‘A’, ‘B’, ‘C’]) st.slider(‘控制参数’, 0, 100) # 主区域 st.title(‘主内容区’)侧边栏非常适合放置控制项、导航菜单,保持主界面整洁。
列(
st.columns): 创建水平并列的列。col1, col2, col3 = st.columns(3) # 创建三等宽的列 with col1: st.metric(“温度”, “25 °C”, “1.2 °C”) # 指标卡片 with col2: st.metric(“湿度”, “65%”, “-3%”) with col3: st.metric(“风速”, “12 km/h”, “0.5 km/h”)你可以指定列宽比例,例如
st.columns([2, 1, 1])表示第一列占2/4宽。容器(
st.container): 创建一个不可见的容器,可以延迟向其中添加元素。这在需要条件性控制元素位置时有用。展开器(
st.expander): 创建一个可折叠/展开的区域,用于隐藏次要内容。with st.expander(“点击查看详细说明”): st.write(“这里是很长很长的说明文字...”) st.image(“https://...")标签页(
st.tabs): 创建标签页布局,这是组织大量内容的优秀方式。tab1, tab2, tab3 = st.tabs([“图表”, “数据”, “配置”]) with tab1: st.plotly_chart(fig) with tab2: st.dataframe(df) with tab3: st.slider(‘参数’, 0, 100)
5. 状态管理与性能优化实战
随着应用变复杂,状态管理和性能会成为瓶颈。这里分享几个实战中总结的关键技巧。
5.1 深入理解Session State
st.session_state是一个类似字典的对象。除了通过组件key自动绑定,手动操作是常态。
import streamlit as st # 初始化复杂状态 if ‘processed_data’ not in st.session_state: # 模拟一个耗时的数据处理过程 raw_data = load_large_dataset() # 假设这个函数很慢 st.session_state.processed_data = complex_processing(raw_data) st.session_state.data_loaded = True # 应用其他部分直接使用处理好的数据,避免重复计算 df = st.session_state.processed_data st.dataframe(df.head()) # 手动修改状态并触发更新 if st.button(‘重置数据’): # 清除相关状态 del st.session_state.processed_data st.session_state.data_loaded = False st.rerun() # 强制重新运行脚本关键点:
- 惰性初始化:将耗时操作的结果存入
session_state,只在第一次运行时计算。后续交互中,脚本虽然重新执行,但直接读取session_state中的结果,避免了重复计算。 - 状态清理:使用
del或赋值None来清理不需要的状态,避免内存泄漏。 st.rerun(): 手动触发脚本重新执行。在编程式修改了大量状态后,需要调用它来让界面同步更新。
5.2 缓存机制:@st.cache_data与@st.cache_resource
这是Streamlit提升性能的王牌功能。它通过缓存函数返回值,避免在每次脚本重新执行时都运行昂贵的计算或重复加载资源。
@st.cache_data: 用于缓存数据(返回可序列化对象的函数,如DataFrame、列表、字符串等)。@st.cache_data(ttl=3600) # ttl设置缓存过期时间(秒) def load_and_clean_data(file_path): # 模拟一个耗时操作 time.sleep(3) df = pd.read_csv(file_path) df = df.dropna().reset_index(drop=True) return df # 第一次调用会执行函数并缓存结果 df = load_and_clean_data(‘big_data.csv’) # 后续调用(只要输入参数file_path不变)会直接返回缓存的结果,跳过sleep和计算 st.write(‘数据行数:’, len(df))工作原理:Streamlit会哈希函数的输入参数和函数体代码。如果发现相同的“输入签名”和“代码签名”的缓存存在,就直接返回缓存值,否则执行函数。
@st.cache_resource: 用于缓存不可序列化的资源(返回数据库连接、机器学习模型、TensorFlow/Keras会话等对象的函数)。@st.cache_resource def get_database_connection(): # 创建数据库连接,这是一个昂贵且不可序列化的对象 conn = create_engine(‘postgresql://user:pass@localhost/db’) return conn @st.cache_resource def load_ml_model(): # 加载一个大的机器学习模型 model = torch.load(‘large_model.pth’) model.eval() return model conn = get_database_connection() model = load_ml_model()
避坑指南:
- 副作用警告:被缓存的函数应该是“纯函数”,即相同的输入永远产生相同的输出,且没有副作用(不修改外部变量、不执行I/O操作)。如果函数内有
st.write这样的输出语句,它只会在函数第一次执行时被调用。- 参数哈希:输入参数必须是可哈希的。避免传入如DataFrame这样的大对象作为参数,这会导致哈希计算很慢。通常用文件名、配置字符串等作为参数。
- 缓存失效:当函数代码改变时,缓存会自动失效(因为代码签名变了)。你也可以用
st.cache_data.clear()来手动清空所有数据缓存。ttl和max_entries:使用ttl(生存时间)避免缓存陈旧数据,使用max_entries限制缓存条目数防止内存占用过高。
5.3 性能优化实战案例
假设我们有一个应用:用户上传一个CSV文件,然后可以选择不同的算法进行耗时分析,并查看结果图表。
低效写法(每次交互都重新加载和处理文件):
uploaded_file = st.file_uploader(“上传文件”) algorithm = st.selectbox(“选择算法”, [“算法A”, “算法B”]) if uploaded_file: df = pd.read_csv(uploaded_file) # 每次滑块变化都会重新执行这行 result = expensive_analysis(df, algorithm) # 每次都会重新分析 st.plotly_chart(plot_result(result))高效写法(利用缓存和状态):
import pandas as pd import streamlit as st # 1. 缓存数据加载函数 @st.cache_data def load_data(uploaded_file): return pd.read_csv(uploaded_file) # 2. 缓存昂贵的分析函数 @st.cache_data def run_analysis(df, algorithm_choice): # 模拟耗时分析 time.sleep(5) return perform_complex_analysis(df, algorithm_choice) # 主程序 uploaded_file = st.file_uploader(“上传文件”, type=[“csv”]) if uploaded_file is not None: # 加载数据(首次慢,后续快) df = load_data(uploaded_file) st.success(f”数据加载完成,共 {len(df)} 行”) # 选择算法 algorithm = st.selectbox(“选择算法”, [“算法A”, “算法B”], key=“algo”) # 只有当数据和算法选择都就绪,且点击按钮时才进行分析 # 避免一选择算法就触发分析 if st.button(“开始分析”, key=“analyze_btn”): with st.spinner(f’正在运行{algorithm}分析,这可能需要几秒钟...’): result = run_analysis(df, algorithm) st.session_state.analysis_result = result # 存入状态 # 从状态中读取结果并展示 if ‘analysis_result’ in st.session_state: st.plotly_chart(plot_result(st.session_state.analysis_result))优化点分析:
load_data被缓存,同一文件只读取一次。run_analysis被缓存,相同的(df, algorithm)组合只计算一次。即使脚本因其他交互重新执行,只要参数没变,就直接返回缓存。- 使用
st.button+st.session_state来控制昂贵操作的触发时机,而不是在selectbox变化时立即触发(使用on_change参数是另一种方式,但这里用按钮更明确)。 st.spinner在长时间操作时提供视觉反馈,提升用户体验。
6. 部署上线与分享你的应用
本地运行很棒,但分享给别人才是最终目的。Streamlit提供了多种简单的部署方式。
6.1 部署到Streamlit Community Cloud(最简方案)
这是Streamlit官方提供的免费托管服务,对公开项目非常友好。
- 准备代码:将你的应用脚本(如
app.py)和依赖文件(requirements.txt)推送到GitHub仓库。 - 访问社区云:前往 share.streamlit.io 并登录(支持GitHub账号)。
- 点击“New app”。
- 选择仓库、分支和主文件路径。
- 点击“Deploy”。
几分钟后,你的应用就会有一个永久的公共URL(如https://yourapp-name.streamlit.app/),可以分享给任何人。
注意事项:
- 依赖管理:确保
requirements.txt文件准确列出了所有依赖包及其版本。- 密钥管理:绝对不要将API密钥、数据库密码等敏感信息硬编码在脚本中或提交到GitHub。使用Streamlit Community Cloud的“Secrets”管理功能。在本地,可以通过
.streamlit/secrets.toml文件管理;在云端,在应用设置界面添加。- 资源限制:免费套餐有内存和CPU使用限制,对于轻量级应用足够,但重型计算任务可能需要升级。
6.2 使用Docker容器化部署(灵活方案)
对于企业内网部署或需要更多控制权的场景,Docker是最佳选择。Streamlit官方提供了Docker镜像。
创建Dockerfile:
# 使用官方Python镜像 FROM python:3.9-slim # 设置工作目录 WORKDIR /app # 复制依赖文件 COPY requirements.txt . # 安装依赖 RUN pip install --no-cache-dir -r requirements.txt # 复制应用代码 COPY . . # 暴露Streamlit默认端口 EXPOSE 8501 # 健康检查 HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health # 启动命令 ENTRYPOINT [“streamlit”, “run”, “app.py”, “--server.port=8501”, “--server.address=0.0.0.0”]构建并运行:
# 构建镜像 docker build -t my-streamlit-app . # 运行容器 docker run -p 8501:8501 my-streamlit-app部署:可以将此Docker镜像推送到任何容器注册中心(如Docker Hub, AWS ECR, Google Container Registry),然后在云服务器(如AWS EC2, Google Cloud Run, Azure Container Instances)或Kubernetes集群中运行。
6.3 配置与调试技巧
配置文件:Streamlit支持配置文件
~/.streamlit/config.toml(用户级)和项目级的.streamlit/config.toml。可以配置服务器地址、端口、主题、浏览器设置等。# .streamlit/config.toml [server] port = 8502 address = “0.0.0.0” # 允许外部访问 enableCORS = false # 根据需求调整 [theme] primaryColor = “#FF4B4B” backgroundColor = “#FFFFFF” secondaryBackgroundColor = “#F0F2F6” textColor = “#31333F” font = “sans serif”日志与调试:运行应用时,终端会输出日志。添加
--logger.level=debug参数可以获取更详细的日志。在代码中使用st.write或Python的print(输出到终端)进行调试。处理大数据:避免在页面中一次性渲染超大型DataFrame(如上百万行)。使用分页(
st.dataframe自带)、懒加载或聚合后展示。对于图表,考虑数据采样或使用交互式图表库(如Plotly)进行缩放查看。
从本地脚本到一个可分享的Web应用,Streamlit极大地降低了门槛。理解其“重新执行”的核心模型,善用session_state管理状态,利用@st.cache优化性能,你就能构建出既快速又强大的数据应用。这个入门教程涵盖了基础概念和核心操作,在后续的教程中,我们将深入更多高级主题,如自定义组件、多页面应用、深度集成机器学习框架等。
