news 2026/7/10 22:01:02

Appium自动化测试入门:从环境搭建到第一个脚本编写

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Appium自动化测试入门:从环境搭建到第一个脚本编写

1. 项目概述:为什么从Appium开始?

如果你刚接触移动应用自动化测试,或者是从Web端的Selenium转战过来,听到“Appium”这个名字可能会觉得既熟悉又陌生。熟悉的是,它号称是“移动端的Selenium”,用类似的WebDriver协议来驱动应用;陌生的是,面对Android和iOS两大平台、真机与模拟器/仿真器、各种驱动和依赖,环境搭建的复杂度似乎陡增。我刚开始接触时,也被网上各种版本冲突、环境报错的帖子吓到过。但后来我发现,只要理清脉络,从编写第一个能成功运行的脚本开始,后面的一切都会豁然开朗。这个“从安装到运行”的过程,不仅仅是跑通一个Demo,更是理解Appium整个工作流和生态的绝佳切入点。它能帮你建立起最核心的认知:Appium是一个遵循客户端-服务器架构的HTTP服务,你的脚本(客户端)通过发送符合WebDriver协议的指令,来控制远端的Appium服务器,进而操作手机上的应用。

这个过程解决了什么核心问题?它让你摆脱了手动点点点的重复劳动,为后续的自动化测试套件、持续集成流水线打下了第一块基石。无论你是测试工程师、开发人员还是对自动化感兴趣的技术爱好者,亲手搭建环境并跑通第一个脚本,都是最具成就感也最扎实的起步。接下来,我会带你走一遍我踩过坑、验证过的最稳当的路径,从零开始,直到看到手机屏幕在你的代码指令下自动操作。

2. 环境准备与核心组件拆解

在动笔写第一行测试代码之前,我们需要一个稳固的“舞台”。这个舞台由几个关键角色构成,理解它们各自的作用和相互关系,能让你在遇到问题时快速定位。

2.1 核心组件:Node.js、Appium Server与客户端库

首先,Appium服务器本身是用Node.js写的,所以Node.js是必须的第一个依赖。我建议直接安装最新的LTS(长期支持)版本,从官网下载安装包即可,安装时记得勾选“自动安装必要的工具”选项(像npmnpx)。安装完成后,在终端里运行node -vnpm -v,能正常显示版本号就说明基础环境OK了。

接下来是主角:Appium Server。这是接收你脚本指令的核心服务。安装方式很简单,通过npm全局安装:npm install -g appium。这里有个小坑需要注意:如果你之前安装过旧版本的Appium(比如1.x),最好先全局卸载 (npm uninstall -g appium) 再安装,避免冲突。安装成功后,运行appium -v应该能看到版本号,比如3.5.2。这里提一嘴,你搜索词里出现的npm list -g appium命令,就是用来查看全局安装的Appium及其位置的,像/users/xxx/.npm-global/lib这个路径就是典型的macOS上通过npm全局安装的包位置。

但是,光有Appium Server还不够。从Appium 2.0开始,它采用了插件化架构。Server本身只是一个核心引擎,具体的自动化能力(比如驱动Android设备或iOS设备)由不同的驱动(Driver)提供。对于Android自动化,目前最主流、最稳定的驱动是UiAutomator2。所以,安装完Server后,你需要单独安装这个驱动:appium driver install uiautomator2。这个命令会从Appium的驱动仓库中拉取并安装uiautomator2驱动。你可以通过appium driver list来查看已安装的驱动。

最后是客户端库(Client Library)。Appium Server提供标准的WebDriver接口,但我们需要用某种编程语言来调用这些接口,这就是客户端库的作用。对于Python,官方的客户端库是Appium-Python-Client。它继承自Selenium的Python绑定,所以你安装它的时候,会自动把selenium依赖也装上。安装命令就是经典的:pip install Appium-Python-Client

注意:这里的环境顺序很重要。先装Node.js和Appium Server,再装所需驱动,最后在Python虚拟环境中安装客户端库。很多新手问题都出在顺序错乱或环境混杂上。

2.2 移动端环境:Android SDK与设备准备

我们的脚本最终要控制Android设备(或模拟器),所以Android开发环境是绕不开的。核心是Android SDK,特别是其中的adb(Android Debug Bridge) 工具。adb是连接电脑和Android设备的桥梁,Appium底层很多操作都依赖它。

对于Mac用户,我推荐通过Homebrew安装Android命令行工具:brew install android-commandlinetools。安装后,需要设置环境变量,比如把ANDROID_HOME指向SDK的安装路径,并把adb所在目录(通常是$ANDROID_HOME/platform-tools)加入到系统的PATH中。之后在终端运行adb devices,如果能看到设备列表(即使是空的),说明adb基本可用。

设备方面,你可以使用真机模拟器。对于初学者,我强烈建议从Android Studio自带的模拟器(AVD)开始。原因有三:第一,环境纯净,没有真机上各种厂商定制系统的干扰;第二,方便创建各种型号和系统版本的设备进行测试;第三,截图、录屏方便。在Android Studio中创建好一个AVD并启动它,确保adb devices命令能识别到它(会显示类似emulator-5554 device的字样)。

实操心得:真机调试需要开启“开发者选项”和“USB调试”。有时候adb识别不到设备,可以尝试:1. 换一条质量好的USB数据线;2. 在开发者选项里“撤销USB调试授权”后重新连接;3. 重启adb服务:adb kill-server然后adb start-server

2.3 一个被忽视的关键工具:Appium Inspector

在你搜索的热词里,appium inspectorappium inspector 教程出现的频率很高,这恰恰说明了它的重要性。Appium Inspector是一个图形化工具,用于定位应用界面上的元素。你可以把它理解为移动端的“浏览器开发者工具”。

它的工作原理是:连接到一个正在运行的Appium Server会话,实时获取设备屏幕截图和UI层级结构(类似于XML),然后你可以通过点击截图来获取元素的属性(如resource-id、text、class、xpath等)。这些属性就是你后续编写定位脚本的依据。

从Appium 2.0开始,Inspector不再随Server捆绑安装,需要单独下载。你可以从Appium官方的GitHub Releases页面下载对应操作系统的桌面版Appium Inspector。使用前,需要在其配置中填入与你的测试脚本中一致的Capabilities(能力参数)和Appium Server地址。它是编写和调试定位策略不可或缺的助手,能极大提升效率。

3. 第一个脚本的逐行解读与编写

环境就绪,我们来动手编写第一个脚本。我将以一个最简单的目标为例:打开Android系统的“设置”应用,然后点击进入“电池”选项。这个例子不依赖任何外部APK,非常适合入门。

3.1 脚本骨架与依赖导入

首先,创建一个Python文件,比如叫first_appium_test.py。我们使用Python内置的unittest框架来组织测试用例,因为它简单直接,适合演示。

import unittest from appium import webdriver from appium.options.android import UiAutomator2Options from appium.webdriver.common.appiumby import AppiumBy
  • unittest: Python标准库中的单元测试框架,提供了setUp(测试前置)、tearDown(测试后置)和断言方法的结构。
  • webdriver: 从appium包导入,这是我们创建远程连接的核心。
  • UiAutomator2Options: 这是Appium 2.x推荐的新方式,用于设置和管理Capabilities,比旧版的字典方式更清晰、类型更安全。
  • AppiumBy: 定义了Appium扩展的一些定位器策略,虽然大部分与Selenium的By通用,但使用它可以保持一致性。

3.2 核心灵魂:Capabilities详解

Capabilities是一组发送给Appium Server的“指令”或“描述”,告诉服务器你想要启动一个什么样的自动化会话。这是连接客户端、服务器和待测应用的纽带,理解每个参数的含义至关重要。

# 定义Capabilities capabilities = dict( platformName='Android', # 平台,必须是'Android'或'iOS' automationName='uiautomator2', # 自动化引擎,这里指定我们安装的UiAutomator2驱动 deviceName='Android', # 设备名称,在Android上可以是任意字符串,但通常用于日志标识 appPackage='com.android.settings', # 要启动的App包名 appActivity='.Settings', # 要启动的App初始Activity名 language='en', # 设置设备语言 locale='US', # 设置设备地区 # 可选:指定UDID,当连接多设备时用于区分 # udid='emulator-5554' )
  • platformNameautomationName:这是两个必须且关键的参数。它们告诉Appium Server使用哪个驱动来处理会话。uiautomator2是针对Android 5.0+的现代驱动。
  • deviceName:在Android环境下,这个参数的实际作用不大,主要是日志和报告里显示用。即使你的设备叫“MyPhone”,这里写“Android”也没问题。但在Selenium Grid或多设备并行时,它有标识作用。
  • appPackageappActivity:这是启动特定Android应用的关键。如何获取它们?有几个方法:1. 问开发;2. 如果你有APK文件,可以用aapt dump badging <your_app.apk> | grep package... | grep launchable-activity命令查看;3. 在已安装应用的设备上,使用adb shell dumpsys window | grep mCurrentFocus命令查看当前前台应用的包名和Activity。
  • languagelocale:这些是“可选但推荐”的参数。指定它们可以确保应用在预期的语言环境下启动,避免因系统语言不同导致文本定位失败。

注意事项:Capabilities的键(key)在Appium 2.x中,大部分都需要加上appium:前缀,例如appium:appPackage。但是,Appium Python客户端的最新版(基于Selenium 4)的UiAutomator2Options().load_capabilities()方法会自动帮你处理这个前缀,所以用字典直接写也没问题,这样代码更清晰。但如果你直接用webdriver.Remote(command_executor, desired_capabilities)这种旧式传参,就必须自己加前缀。

3.3 连接服务器与测试生命周期管理

接下来,我们定义Appium Server的地址,并构建测试类。

appium_server_url = 'http://localhost:4723' # Appium Server默认监听4723端口 class TestAppium(unittest.TestCase): def setUp(self): # 将Capabilities字典加载到UiAutomator2Options对象中 options = UiAutomator2Options().load_capabilities(capabilities) # 创建WebDriver会话,连接到Appium服务器 self.driver = webdriver.Remote(command_executor=appium_server_url, options=options) # 隐式等待,全局设置查找元素的超时时间 self.driver.implicitly_wait(10) def tearDown(self): # 测试结束后,退出并关闭会话 if self.driver: self.driver.quit()
  • setUp:在每个测试方法(以test_开头的方法)执行前运行。这里我们初始化驱动会话。webdriver.Remote是建立连接的核心,它向http://localhost:4723/wd/hub(Appium 2.x后路径简化了)发送一个POST请求,携带Capabilities。服务器解析后,会通过adb命令启动指定的应用,并返回一个会话ID(session id),后续所有操作都基于这个会话。
  • implicitly_wait(10):设置隐式等待10秒。这是一个非常重要的最佳实践。它意味着后续所有find_element操作,如果元素没有立即出现,驱动会轮询查找,最多等10秒。这能有效避免因网络延迟、应用启动慢或动画导致的“元素找不到”错误。
  • tearDown:在每个测试方法执行后运行。driver.quit()方法会向服务器发送删除会话的请求,服务器则会关闭被测应用并清理资源。务必确保会话被正确关闭,否则会导致端口占用、设备资源残留等问题。

3.4 编写测试动作与元素定位

现在,编写我们的测试用例:找到并点击“Battery”(电池)选项。

def test_find_battery(self): # 使用XPath定位器,寻找文本内容为“Battery”的元素 battery_element = self.driver.find_element(by=AppiumBy.XPATH, value='//*[@text="Battery"]') # 点击该元素 battery_element.click() # 为了让我们肉眼看到效果,等待几秒 import time time.sleep(3)
  • 定位策略:这里使用了AppiumBy.XPATH。XPath是一种在XML文档中定位节点的语言,Android的UI层级(通过uiautomator2dump出来)就是一种XML结构。//*[@text="Battery"]这个表达式意思是:在整个文档中,寻找任意节点(*),且该节点的text属性等于“Battery”。
  • 为什么用text定位?对于系统设置这种标准应用,菜单项的文本通常是固定的、可读性最好的标识。但在定位你自己的应用时,优先使用resource-id(对应Android中的android:id),因为它是开发人员赋予的唯一标识,最稳定。如果id不可用,再考虑textcontent-descclass
  • click():执行点击操作。Appium会将该命令转化为对设备的一次触摸事件。

最后,添加启动测试的代码:

if __name__ == '__main__': unittest.main()

4. 完整运行流程与现场调试

脚本写好了,但让它成功跑起来,还需要正确的步骤和一点调试技巧。

4.1 分步执行与验证

  1. 启动Appium Server:打开一个终端窗口,直接输入命令appium。如果一切正常,你会看到大量日志输出,最后几行通常包含Appium REST http interface listener started on 0.0.0.0:4723这样的信息,表示服务器已在4723端口启动成功。让这个终端窗口保持运行

  2. 确保设备在线:打开另一个终端窗口,运行adb devices。你应该能看到你的模拟器或真机设备,状态为device。如果显示unauthorized,去设备上点击确认“允许USB调试”的提示。

  3. 执行测试脚本:在第三个终端窗口(或你的IDE终端里),导航到脚本所在目录,运行python first_appium_test.py

4.2 预期结果与问题排查

如果一切顺利,你会看到:

  • Appium Server的终端里,滚动出现新的日志,显示创建新会话(POST /session)、启动应用等操作。
  • 你的Android设备屏幕亮起,系统“设置”应用被自动打开。
  • 稍作停顿后,应用会自动滑动并点击进入“电池”设置页面。
  • 等待3秒后,应用关闭。

但第一次运行就成功的概率可能没那么高。下面是一些常见错误及排查思路:

  • 错误:Unable to create a new remote session. ...

    • 可能原因1:Appium Server没启动。检查第一步的终端,确认服务器在运行。
    • 可能原因2:Capabilities配置错误。仔细检查platformName,automationName,appPackage,appActivity的拼写和值。特别是appActivity,有时需要完整的包名路径(如com.android.settings.Settings),但通常以点开头(.Settings)也可以。最可靠的方式是用adb shell dumpsys window命令去查。
    • 可能原因3:设备未连接或未授权。再次运行adb devices确认。
  • 错误:An element could not be located on the page using the given search parameters.

    • 可能原因1:元素定位表达式写错了。打开Appium Inspector,连接到同一个Appium Server和设备,打开设置应用,查看“Battery”这个文本在UI树中的准确属性。也许它的text是“电池”(中文系统),或者它外面还有多层布局。
    • 可能原因2:应用还没加载到那个页面。虽然我们用了隐式等待,但可能页面加载时间超过10秒。可以在find_element前加一个显式等待,或者增加隐式等待时间。
    • 可能原因3:需要滚动才能看到元素。如果“Battery”不在首屏,需要先滚动。可以改用driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiScrollable(...).scrollIntoView(text("Battery"))')这种滚动查找的方式。
  • Appium Server日志报错:Original error: Cannot start the 'com.android.settings' application

    • 可能原因:设备上不存在这个应用,或者Activity名不对。在设备上手动确认一下设置应用能否打开。对于模拟器,系统应用是肯定存在的。

调试技巧多看Appium Server的日志!它是排查问题的第一现场。日志级别默认是info,如果你需要更详细的信息,可以在启动Appium时加上--log-level debug参数。日志里会清晰记录它接收到的命令、发送给adb的指令以及设备的响应。

5. 从Demo到实战:脚本优化与扩展思路

第一个脚本跑通,只是万里长征第一步。要让这个脚本具备实战价值,还需要进行一系列优化和功能扩展。

5.1 优化一:使用Page Object模式组织代码

直接把定位和操作写在测试方法里,在小型demo中没问题,但随着用例增多,维护将是灾难。Page Object (PO) 模式是自动化测试的核心设计模式。它将每个页面(或页面片段)抽象成一个类,页面的元素定位符和基本操作封装在这个类的方法里,测试用例只调用这些方法。

例如,为“设置”主页面创建一个类:

class SettingsMainPage: def __init__(self, driver): self.driver = driver # 可以在这里定义一些元素的定位符,方便管理和复用 self.battery_option_locator = (AppiumBy.XPATH, '//*[@text="Battery"]') def go_to_battery(self): """从设置主页进入电池设置""" battery_ele = self.driver.find_element(*self.battery_option_locator) battery_ele.click() # 返回下一个页面的对象,实现链式调用 return BatteryPage(self.driver)

这样,你的测试用例就会变得非常简洁、易读:

def test_find_battery(self): main_page = SettingsMainPage(self.driver) battery_page = main_page.go_to_battery() # 接下来可以在battery_page上进行断言或其他操作

5.2 优化二:更健壮的元素定位与等待策略

XPath虽然强大,但有时性能较差且易受UI变化影响。Appium提供了更多定位策略:

  • ID定位:优先选择。driver.find_element(AppiumBy.ID, "com.android.settings:id/some_id")
  • Accessibility ID定位:对应Android的contentDescription,用于无障碍访问,也是很好的选择。driver.find_element(AppiumBy.ACCESSIBILITY_ID, "Battery")
  • Android UiAutomator:利用Android原生框架的定位能力,非常强大。
    # 通过文本定位 driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("Battery")') # 组合条件定位 driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().className("android.widget.TextView").text("Battery")')

对于等待,除了全局的隐式等待,显式等待(Explicit Wait)在处理复杂条件时更精确。它可以等待某个特定条件成立(如元素可点击、元素出现、元素消失等)。

from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待“Battery”元素出现,最多等15秒,每0.5秒检查一次 wait = WebDriverWait(self.driver, 15, poll_frequency=0.5) battery_ele = wait.until(EC.presence_of_element_located((AppiumBy.XPATH, '//*[@text="Battery"]'))) battery_ele.click()

5.3 扩展:常用操作与断言

一个完整的测试用例除了操作,还需要验证结果,这就是断言。

  • 基本断言unittest提供了assertEqual,assertTrue,assertIn等方法。
    # 点击电池后,可以断言当前页面标题或某个特定元素出现 self.assertIn("Battery", self.driver.page_source) # 简单但粗糙的方式 # 更好的方式是定位页面上的一个特征元素进行断言 battery_summary = self.driver.find_element(AppiumBy.ID, "com.android.settings:id/battery_summary") self.assertTrue(battery_summary.is_displayed())
  • 常用操作封装:除了click(),还有send_keys()(输入文本)、swipe()/scroll()(滑动)、tap()(轻触)、press_keycode()(模拟物理按键)等。将这些常用操作封装在Page Object或一个基础的BasePage类里,会极大提升代码复用率。

5.4 向真实项目迈进:配置管理与持续集成

当脚本越来越多,你需要考虑:

  • 配置外部化:将Capabilities、服务器地址、设备信息等写入配置文件(如config.yaml.json)或环境变量,避免硬编码。
  • 测试数据分离:将测试用的账号、文本等数据放在独立的文件(如Excel、CSV、JSON)中。
  • 日志与报告:使用Python的logging模块记录运行日志。集成测试报告框架,如pytest-htmlAllure,生成美观的测试报告。
  • 集成到CI/CD:将你的测试脚本放入Jenkins、GitLab CI等持续集成工具中,设定触发条件(如每日构建、代码合并后),实现自动化回归。

6. 避坑指南与高频问题实录

结合我自己的踩坑经历和社区常见问题,这里总结一份速查清单。

问题现象可能原因排查步骤与解决方案
adb命令不识别或报错1. Android SDK未安装或路径未配置。
2. 环境变量PATH未包含platform-tools目录。
1. 确认已安装Android SDK命令行工具。
2. 在终端执行echo $PATH查看,并确保类似~/Library/Android/sdk/platform-tools的路径在其中。
3. 对于Mac,在~/.zshrc~/.bash_profile中添加export PATH=$PATH:~/Library/Android/sdk/platform-tools,然后source配置文件。
Appium Server启动失败,端口被占用4723端口已被其他进程(可能是之前未退出的Appium实例)占用。1. 查找占用端口的进程:lsof -i :4723(Mac/Linux) 或netstat -ano | findstr :4723(Windows)。
2. 终止该进程:kill -9 <PID>或 任务管理器结束。
3. 或者,启动Appium时指定其他端口:appium -p 4724
真机无法被adb devices识别1. USB调试未开启。
2. 数据线仅支持充电。
3. 电脑驱动问题(Windows常见)。
4. 设备未授权。
1. 进入手机“开发者选项”,确认“USB调试”已开启。
2. 换一条确认可以传输数据的USB线。
3. 在设备连接时,查看电脑设备管理器是否有未知设备,尝试安装对应品牌驱动。
4. 连接时留意手机屏幕是否有“允许USB调试”的弹窗,勾选“始终允许”后确定。
脚本报错...uiautomator2 driver not installed未安装uiautomator2驱动,或Appium Server版本与驱动不兼容。1. 运行appium driver list查看是否已安装uiautomator2
2. 若未安装,运行appium driver install uiautomator2
3. 若已安装但报错,尝试更新驱动:appium driver update uiautomator2
元素能找到但点击无效,或点击位置不对1. 元素不可点击(状态为disabled)。
2. 元素被遮挡。
3. 坐标点击不准确(使用tap或坐标定位时)。
4. 需要等待元素可点击状态。
1. 使用Appium Inspector查看元素属性,确认clickableenabledtrue
2. 尝试使用element.click()而非坐标。
3. 使用显式等待,等待元素可点击:WebDriverWait(driver, 10).until(EC.element_to_be_clickable(locator))
4. 对于复杂手势,考虑使用TouchActionW3C ActionsAPI。
运行速度慢,每一步操作都有明显延迟1. 使用了效率低的定位器(如复杂的XPath)。
2. 隐式等待时间设置过长。
3. 设备性能或模拟器速度慢。
4. 动画效果未关闭。
1. 优化定位器,优先使用ID、Accessibility ID。
2. 适当减少隐式等待时间,或改用针对性的显式等待。
3. 在Capabilities中设置disableWindowAnimation: true等参数来关闭系统动画。
4. 在开发者选项中关闭“窗口动画缩放”、“过渡动画缩放”等。

最后,再分享一个我个人的小习惯:在项目根目录下创建一个requirements.txt文件,记录所有Python依赖(如Appium-Python-Client),并使用虚拟环境(venvconda)来管理项目依赖。这样能最大程度避免包版本冲突,让项目在任何新电脑上都能快速搭建起测试环境。从第一个脚本开始,就养成好的工程习惯,后面的路会好走很多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/10 22:00:44

如何为阿里云 Elasticsearch 创建推理端点,连接器,Workflow 并写入数据

阿里云上有很多的服务。在今天的文章中&#xff0c;我重点介绍如何创供我们使用的推理端点&#xff0c;连接器&#xff0c;并使用代码写入我们的数据。在进行下面的操作之前&#xff0c;建议观看视频&#xff1a;阿里云 Elasticsearch 开通试用到登陆 Kibana 教程。 https://ww…

作者头像 李华
网站建设 2026/7/10 21:57:43

OpenAI 推出 GPT-Live:告别回合制,AI 语音交互体验大升级!

AI 语音交互痛点催生 GPT-Live经常使用 AI 语音助手的用户&#xff0c;大概率经历过令人烦躁的时刻&#xff1a;稍微停顿&#xff0c;AI 就急着打断输出&#xff1b;环境噪音会被误判为说话结束&#xff0c;交流如同严格遵循「你一句、我一句」的回合制。为解决此问题&#xff…

作者头像 李华
网站建设 2026/7/10 21:57:00

ITIL 4 DPI认证信息整理

ITIL 4 DPI&#xff08;指导、计划和改进&#xff09;是ITIL 4 Managing Professional&#xff08;MP&#xff09;路径中的认证模块之一。以下根据公开信息&#xff0c;就该认证的定位、知识体系及考试安排进行整理。一、认证体系定位ITIL 4 MP路径包含四个模块&#xff1a;课程…

作者头像 李华
网站建设 2026/7/10 21:56:11

RimSort终极指南:告别《边缘世界》模组冲突的5个高效技巧

RimSort终极指南&#xff1a;告别《边缘世界》模组冲突的5个高效技巧 【免费下载链接】RimSort RimSort is an open source mod manager for the video game RimWorld. There is support for Linux, Mac, and Windows, built from the ground up to be a reliable, community-m…

作者头像 李华
网站建设 2026/7/10 21:50:27

Typora绘制-用户旅程图

**注意&#xff1a;**用户旅程图的高亮新旧版本会正常显示。 用户旅程图 mermaid官方网站文档教程链接(英文) 社区维护的中文文档站 1、新版本语法 journey如下仅是基础的常用功能语法&#xff0c;如需更全面内容可参考官网教程文档 任务行格式 兼容旧版&#xff1a;任务:…

作者头像 李华
网站建设 2026/7/10 21:50:22

私有化部署RustDesk中继服务器

一.RustDesk介绍 RustDesk 是一款开源远程桌面控制软件&#xff0c;支持自己搭建信令和中继服务器。与 TeamViewer、ToDesk 等专有远程访问解决方案相比&#xff0c;RustDesk 完全免费使用&#xff0c;跨平台&#xff0c;支持 Windows、macOS、Linux、Android 、 iOS 等多种操作…

作者头像 李华