fastlane pilot实战:iOS TestFlight上传自动化完全指南

发布时间:2026/10/17 9:49:17
fastlane pilot实战:iOS TestFlight上传自动化完全指南 每次往 TestFlight 传新的 iOS 测试包我都能感受到一股强烈的“掌上时代前夜”气息Xcode 里归档、导出身、登录开发者网站、建分组、填 Beta 审核信息、等构建处理翻绿再挨个通知测试员去刷新 TestFlight。这套流程只要有一环漏掉测试就得晚半天更麻烦的是它完全无法复用下一次发版还是同样一堆手工操作。后来我开始用 fastlane 的 pilot。简单说pilot 就是 fastlane 家族里专门操作 TestFlight 的命令行工具上传 ipa、创建和修改测试组、批量增删测试员、提交 Beta 审核、查询构建处理状态全都能在一条命令里完成。名字听起来像个开飞机的实际干的全是地勤的活而且干得比人靠谱。这篇内容按“准备—上传—通知—汇总—自动化”这条线把 pilot 在真实项目里的完整用法拆开给你看。适合两类人一类是每天要发多个测试包的 iOS 开发者另一类是正在搭 CI 流水线想把 TestFlight 上架也纳入自动化的工程师。看完你至少能直接照抄一套可用的配置并且知道哪些参数最容易踩坑。1. 为什么我劝你把 TestFlight 上传交给 pilot1.1 手动流程到底烦在哪先说一个常见的发版场景。你刚刚在 Xcode 里归档完然后用 Organizer 或者 Transporter 把 ipa 传到 TestFlight接下来要做的动作通常包括登录开发者后台找到这个 App 的 TestFlight 页面。等构建处理完成状态从 Processing 变成 Available。给某个测试组勾选这个 build或者新建一个分组。如果要发外部测试还要填写 Beta App Review 信息手动提交审核。如果测试员是新加的还要一个个发邀请邮件。这一套做下来顺利的话 10 到 15 分钟不顺利的话光是在“双因素认证”和“找不到某个按钮”之间来回切换就能耗掉半小时。而且全程高度依赖人工记忆忘了提交审核外部测试员就永远收不到版本填错分组内部研发没看到包产品却收到了构建号重复直接上传失败只能回去重新改配置再来一次。这些操作最大的问题不是烦而是不可复现。手动流程一旦跑通你不会留下任何“下次可以照着做”的痕迹一旦跑不通你又得重新回忆自己上次是怎么点过来的。1.2 pilot 到底替你干了哪些事pilot 的定位非常明确把 TestFlight 相关的操作全部变成 API 调用你可以用命令行驱动也可以在 fastlane 的 lane 里调用。它能做的事包括但不限于上传 ipa 到 TestFlight。创建、查询、修改测试组。添加、删除测试员。填写 Beta App Review 信息并提交审核。等待构建处理完成并输出最终状态。返回版本号、构建号、测试员列表等信息方便后续脚本处理。一句话总结开发者后台里 TestFlight 页面能点的按钮pilot 基本都能通过命令完成。因为它是通过 App Store Connect API 调用的所以天然支持 CI 环境也能很好地和其他自动化解法赛跑。有一点你需要先分清fastlane 里还有一个叫testflight的行动action有人说它是旧方案pilot 是推荐入口这个说法并不完全准确。两者实际能力有重叠文档也在迭代。但如果你从零开始直接学pilot就够了它的参数命名更直观对 API Key 的支持也更顺手。2. 开工前把环境和凭证一次准备到位2.1 安装 fastlane 并初始化项目pilot 是 fastlane 的一部分所以第一步是安装 fastlane。我不太建议直接sudo gem install fastlane因为系统 Ruby 环境会带一堆权限问题更高雅而且更稳的做法是用 Gemfile 管理依赖。在项目根目录创建一个 Gemfilesource https://rubygems.org gem fastlane然后执行bundle install bundle exec fastlane --version看到版本号输出说明 fastlane 已经就绪。接下来执行bundle exec fastlane initfastlane init会问你要不要自动配置、要不要关联 App Store 等信息你不用全部走完最关键的是它会生成一个名为fastlane的目录里面有Fastfile文件。未来你所有 lane 都写在Fastfile里。工程上我习惯把Gemfile和Gemfile.lock一起提交到代码仓库这样团队成员和 CI 机器都能拿到一致版本的 fastlane不会再出现“在你机器上能跑在我机器上就报错”的尴尬。2.2 登录方式怎么选Apple ID 双认证 vs API Key这是新手最容易卡住的地方。早期 fastlane 都通过 Apple ID 加密码登录但 Apple 后来强制双因素认证导致 fastlane 每次都要处理一次性验证码在 CI 上尤其难受。你需要提前用spaceauth生成会话过期后又得重新弄。所以我现在的态度非常明确能用 App Store Connect API Key就不要用 Apple ID 密码。API Key 的创建方式如下登录 App Store Connect进入“用户与访问”。找到“集成”选项卡点击“App Store Connect API”旁边的生成密钥。给密钥起个名字角色建议只勾选“App 管理”或者最少权限的角色不要为了省事直接给 Admin。记下 Key ID 和 Issuer ID。下载.p8文件。这个文件只会提供一次下载机会务必保存好。然后把以下变量配置到环境变量里export ASC_API_KEY_ID你的KeyID export ASC_API_ISSUER_ID你的IssuerID export ASC_API_KEY_FILEPATH/Users/你的名字/.fastlane/AuthKey_xxxx.p8fastlane 会通过读取这些变量来自动完成认证。如果你把变量写进 Fastfile我建议通过app_store_connect_api_key行动来注入例如app_store_connect_api_key( key_id: ENV[ASC_API_KEY_ID], issuer_id: ENV[ASC_API_ISSUER_ID], key_filepath: ENV[ASC_API_KEY_FILEPATH] )对本地开发来说把环境变量放到~/.zshrc或~/.bashrc里都行但千万不要把.p8文件提交进 Git 仓库也不要写死在 Fastfile 里。.p8泄露等于别人可以直接操作你所有 App 的 TestFlight后果不需要我多说。2.3 用一条命令验证环境是否打通配置好之后我建议先跑一条只读命令验证环境比如列出某个 App 下的测试员bundle exec fastlane pilot list --app_identifier com.example.myapp如果能正常打印出测试员列表说明认证已经通过app_identifier 也没填错。如果提示找不到 App第一件事就是检查 Bundle Identifier 是否一致第二件事则是检查 API Key 的角色权限。这一步虽然简单但能帮你把“环境问题”和“使用问题”分开。很多人一上来就跑pilot upload结果环境没通报了一长串错根本没法判断是代码问题、签名问题还是凭证问题。3. 上传与自动提交 Beta 审核把一条命令拆明白3.1 一条完整的 pilot upload 命令先看我最常用的一条命令bundle exec fastlane pilot upload \ --ipa build/MyApp.ipa \ --app_identifier com.example.myapp \ --groups 内部Beta组,外部反馈组 \ --submit-beta-review \ --wait-for-build-processing true \ --notify-external-testers false \ --beta-app-description 本次回归重点支付、登录、首页加载每个参数的作用是这样的--ipa本地 ipa 文件的路径。如果是用 fastlane 的gym构建的路径就是build/MyApp.ipa。--app_identifierApp 的 Bundle Identifier必须和 App Store Connect 里已经创建的 App 一致。--groups要分发到哪些测试组多个组用逗号分隔。组名必须已经存在否则直接报错。--submit-beta-review上传完成后自动提交 Beta App Review。如果省略则只上传构建不提交审核需要后续手动操作。--wait-for-build-processing true命令会阻塞等待 Apple 处理完构建状态会输出到日志里。CI 场景可能需要配合超时时间使用。--notify-external-testers false控制是否向外部测试员发送通知。我先设为 false因为通常外部组走独立流程内部组则一定会在 TestFlight 里立刻看到版本。--beta-app-description给测试员看的“测试内容说明”对应开发者后台里 Beta App Review 信息中的描述字段。这里最容易被忽视的是--groups和--submit-beta-review的联动关系。如果你把构建分给了外部测试组但忘了提交审核外部测试员一直收不到通知如果你提交审核但没填联系人信息审核又会直接被拒。pilot 只是代替你操作它不会替你思考业务逻辑。3.2 不理解这几点提交审核就是碰运气要理解--submit-beta-review你需要先搞懂 TestFlight 的两个概念内部测试组和外部测试组。内部测试组里的成员来自你的开发者团队账号上限一般为 100 人不需要 Beta App Review。只要构建上传成功并处理完成内测成员打开 TestFlight 就能看到新版本。外部测试组非常像“公开发放测试名额”不限团队内部。外部测试员需要填写邮箱并被邀请而且有 Beta App Review 这个门槛。每次把新构建分给外部组都需要提交审核等审核通过后才能看到版本。所以当你看到一个从没无法自动提交的构建先问自己这个版本是给内部组还是外部组如果是内部组连--submit-beta-review都可以不要如果是外部组就必须把 Beta App Review 信息完善起来。pilot 还支持传入更详细的审核信息。在 Fastfile 里这样写pilot( app_identifier: com.example.myapp, ipa: build/MyApp.ipa, groups: [外部反馈组], submit_beta_review: true, beta_app_review_info: { contact_first_name: 测试, contact_last_name: 联络人, contact_email: ENV[REVIEW_CONTACT_EMAIL], demo_username: demo_user, demo_password: ENV[DEMO_PASSWORD], notes: 本次重点验证支付流程和首页加载速度 } )beta_app_review_info对应 App Store Connect 后台里的 Beta App Review 表单。联系人名、联系邮箱、测试说明这些字段通常都要求填写缺少了就提交不了。把联系方式放环境变量是为了避免个人邮箱写死在仓库里。3.3 在 Fastfile 里固化参数让团队少踩坑命令行写一次可以但如果每次发版都要敲一堆参数那就违背了自动化的初衷。我更建议把参数固化到 Fastfile 的 lane 里。下面这个 lane 比较典型lane :upload_beta do app_store_connect_api_key( key_id: ENV[ASC_API_KEY_ID], issuer_id: ENV[ASC_API_ISSUER_ID], key_filepath: ENV[ASC_API_KEY_FILEPATH] ) pilot( app_identifier: com.example.myapp, ipa: build/MyApp.ipa, groups: [内部Beta组, 外部反馈组], submit_beta_review: true, beta_app_review_info: { contact_first_name: 测试, contact_last_name: 联络人, contact_email: ENV[REVIEW_CONTACT_EMAIL], notes: 本次回归重点支付、登录、首页加载 }, wait_for_build_processing: true, notify_external_testers: false ) end这样做的好处非常多。第一分组、说明、联系方式都集中在代码里团队任何人跑这一条 lane 结果都一样第二参数变更可以走 Code Review第三后续接 CI 时只需要一行fastlane upload_beta。如果你连 ipa 都还没打可以把gym放在pilot之前lane :beta_release do increment_build_number(build_number: Time.now.strftime(%Y%m%d%H%M)) gym( scheme: MyApp, output_directory: build, output_name: MyApp.ipa, configuration: Release ) pilot( app_identifier: com.example.myapp, ipa: build/MyApp.ipa, groups: [内部Beta组, 外部反馈组], submit_beta_review: true, wait_for_build_processing: true, notify_external_testers: false ) end这样一条 lane 就把“打包 上传 提交审核”全打通了。3.4 等待构建处理状态该不该阻塞TestFlight 上传完成后Apple 后台还要做病毒扫描、解压、重新签名等处理这个过程通常要 5 到 15 分钟极端情况可能要更久。pilot提供了--wait-for-build-processing参数用来决定命令要不要等处理完成再退出。在你手动执行时建议设成true因为你可以等并且看到日志里的状态变化会觉得很踏实。但在 CI 上这会成为一个问题公共执行机通常有最长运行时间限制如果构建处理一直没结束整个流水线就被卡死了。我个人的处理方式是CI 中把wait_for_build_processing设为 true但给 runner 设置一个比预期更长的超时时间比如 30 分钟如果还担心阻塞可以在上传完成后就结束 job另起一个定时 job 去轮询处理状态查询工具继续用pilot的能力或者用 App Store Connect API 去拉取构建信息。这个选择没有标准答案看你们团队的运维风格。你要理解的是一个点--wait-for-build-processing true不代表“构建一定会处理成功”只是“命令会等出结果”。结果可能是 Available也可能是 Processing Failed。失败时日志里通常有原因直接读日志比去后台猜要快得多。4. 测试人员管理与通知再做一次看得懂的汇总4.1 用 pilot 管理测试人员增删查改pilot 不止能做上传它管理测试人员也方便。我把常用的命令列在这里查看某个分组下的测试人员bundle exec fastlane pilot list --app_identifier com.example.myapp -g 内部Beta组添加一个测试员bundle exec fastlane pilot add \ --app_identifier com.example.myapp \ --groups 内部Beta组 \ --email qaexample.com查找测试员bundle exec fastlane pilot find --app_identifier com.example.myapp --email qaexample.com移除测试员bundle exec fastlane pilot remove \ --app_identifier com.example.myapp \ --groups 内部Beta组 \ --email qaexample.com细节决定体验。添加测试员时如果邮箱不在正确的组里命令会提示失败测试员邮箱大小写也比较敏感建议统一用小写。另外外部测试员在加入后系统会自动发送邀请邮件如果对方没收到先检查是否填错了邮箱再检查是否已经存在相同邮箱的另一个账户。初期团队测试组只有一两个手动不觉得麻烦等测试规模上来每次发版前把最新名单导出发给测试负责人会很节省沟通成本。这就是“汇总”的第一步。4.2 通知测试人员的三种姿势版本传上去了如何让测试员知道有三种常见方式。第一让 TestFlight 自己发通知。内部测试员在构建处理完成后打开 TestFlight 就能看到新版本但通常不会收到强提醒。外部测试员则会在 Beta App Review 审核通过后收到 Apple 官方邮件或 App 内的推送。pilot 里的notify_external_testers参数就是控制这个行为的。如果设为 false即使版本审核通过也不会自动通知外部测试员适合你希望“先不发、等确认再发”的场景。第二在你的 lane 里主动发群通知。很多测试团队不看邮件但一定看企业微信、飞书、钉钉或 Slack。上传成功后你可以把版本信息拼成一条消息用sh调用 webhook 推送到群里。示例大概长这样payload iOS Beta 新版本 #{version} (#{build_number}) 已上传测试组内部Beta组 sh curl -s -X POST \ #{ENV[RELEASE_WEBHOOK_URL]} \ -H Content-Type: application/json \ -d {\msgtype\:\text\,\text\:{\content\:\#{payload}\}}先说明一下技巧点webhook 地址不要直接写死在 Fastfile 里放到环境变量或 CI secrets 中。通知文案里除了版本号和构建号最好附带“变更范围”和“测试账号”减少测试员来回追问的次数。第三把通知拆到“确认后”再发。如果你不想每次上传都发群消息而是等几轮内测通过后再发给外部组那依然可以用 pilot 单独跑一步。比如先把包上传好但不提交审核产品确认无误后再执行bundle exec fastlane pilot upload --ipa ... --groups 外部反馈组 --submit-beta-review --notify-external-testers true这里顺便吐槽一句真正难的不是“发消息”而是“决定什么时候发”。pilot 能帮你把时机控制得很细这一点在手动流程里反而容易忽略。4.3 汇总把版本、分组、测试员状态沉淀成可见清单很多团队在发版后都会遇到一个灵魂拷问这次版本到底有哪些测试员能看到外部反馈组几个人谁还没被加入分组pilot 给不了你一个现成的“汇总 Dashboard”但它能提供所有原始数据汇总只是几行脚本的事。我常用的做法是在快结束的时候执行bundle exec fastlane pilot list --app_identifier com.example.myapp -g 内部Beta组 internal_testers.txt bundle exec fastlane pilot list --app_identifier com.example.myapp -g 外部反馈组 external_testers.txt然后把这两个文件合并再附上本次版本号和构建号发到群里或者写进当天的发布文档。也可以直接在 Fastfile 里写一段 Ruby把数据拼装成 Markdown 表格再输出lane :summary do groups { 内部Beta组 pilot_list(groups: [内部Beta组]), 外部反馈组 pilot_list(groups: [外部反馈组]) } summary_text 测试人员汇总\n groups.each do |name, members| summary_text #{name}: #{members.count} 人\n end puts summary_text end这里用pilot_list只是示意实际能拿到什么字段取决于 fastlane 版本和 pilot 的商品实现。重点是思路把数据抽出来汇成一份人看得懂的清单而不是让大家自己登录后台看。5. 从手动到自动化一条 lane 打通构建-上传-通知-汇总5.1 一个可以直接抄的 Fastfile 模板现在真正进入“省心模式”。我把完整的 lane 放在下面你可以按自己的项目名称、Bundle Identifier、测试组名替换后直接使用。lane :beta_release do # 1. 自动生成构建号避免手工改版本号 new_build_number Time.now.strftime(%Y%m%d%H%M) increment_build_number(build_number: new_build_number) # 2. 打包 gym( scheme: MyApp, output_directory: build, output_name: MyApp.ipa, configuration: Release ) # 3. 上传 TestFlight 并提交 Beta 审核 pilot( app_identifier: com.example.myapp, ipa: build/MyApp.ipa, groups: [内部Beta组, 外部反馈组], submit_beta_review: true, beta_app_review_info: { contact_first_name: 测试, contact_last_name: 联络人, contact_email: ENV[REVIEW_CONTACT_EMAIL], notes: ENV[RELEASE_NOTES] || 本次自动发布的测试包 }, wait_for_build_processing: true, notify_external_testers: false ) # 4. 生成汇总信息 version get_version_number summary_message iOS Beta 新版本 #{version} (#{new_build_number}) 已上传 TestFlight测试组内部Beta组、外部反馈组 # 5. 发送到群里 sh curl -s -X POST \ #{ENV[RELEASE_WEBHOOK_URL]} \ -H Content-Type: application/json \ -d {\msgtext\:\#{summary_message}\} end这段代码有几个细节值得留意。第一increment_build_number会在 Xcode 项目里改CFBundleBuildNumber因此使用前确保当前 Git 工作区是干净的否则 build 号改动会混进你不想提交的代码里。第二gym默认会使用 Release 签名你需要先在 Xcode 工程里确认 Release 配置的证书和描述文件是齐全的。第三我把通知文案里的 webhook 地址和 Beta 审核联系人邮箱都放到环境变量里完全没有硬编码。如果你们团队的版本号和构建号生成策略更复杂比如根据 Git tag 或者最近的 commit 数量生成也没有问题只需要把第一步替换成自己的逻辑。pilot 不会管你的版本号怎么来它只负责把 ipa 传上去。5.2 CI 环境下的参数注入与安全当你想把beta_release这条 lane 搬到 CI 上时有几个关键点必须处理。一是凭证。CI 机器不应该有任何 Apple ID 密码或者本地 session只保留 API Key。把ASC_API_KEY_ID、ASC_API_ISSUER_ID、ASC_API_KEY_FILEPATH全部配置成 CI Secrets。.p8文件如果存放在 CI 构建机里确保权限是600不要变成其他用户可读。二是密钥文件的保存路径。不同 CI 平台对 Secrets 的注入方式不一样有的直接给环境变量有的要用文件映射。建议在 Fastfile 最前面做一次校验unless ENV[ASC_API_KEY_FILEPATH] File.exist?(ENV[ASC_API_KEY_FILEPATH]) UI.user_error!(缺少 App Store Connect API Key 文件请检查 ASC_API_KEY_FILEPATH) end三是 Runner 超时。如果使用wait_for_build_processing: trueCI 任务超时时间建议不小于 20 分钟。如果不想等太久就在上传成功后把参数改成 false并额外用一个定时任务去查询最终状态。这一点在 3.4 已经说过实际操作时最影响心情的就是“CI 任务卡在 Processing”。四是产物管理。build/MyApp.ipa每次构建都会覆盖这没问题。如果你需要保留历史包就把输出名带上构建号比如MyApp_#{new_build_number}.ipa这样出问题时还能回包排查。5.3 按分支自动切换测试组让流程更细全量发版用一套固定分组可以但更常见的场景是只有稳定的主干分支才发到外部测试组普通分支只发内部组。对应的 Fastfile 可以写成lane :beta_release do current_branch ENV[GIT_BRANCH] || git_branch groups if current_branch develop [内部Beta组, 外部反馈组] else [内部Beta组] end pilot( app_identifier: com.example.myapp, ipa: build/MyApp.ipa, groups: groups, submit_beta_review: groups.include?(外部反馈组), wait_for_build_processing: true ) end我在意的是“提交审核”这个动作也要跟着分支走。如果当前分支根本没发外部组那submit_beta_review就不该开启否则白白走一轮审核还可能因为信息不齐而失败。把这些规则写进 lane 后团队的“发版纪律”就从口头约定变成了代码逻辑这是一个很稳妥的演进方向。6. 常见问题与避坑实录6.1 我踩过的报错速查表pilot 用多了你会发现报错其实就那么几类。我把见过的高频问题整理成一个表方便你快速定位报错现象常见原因解决方式Could not find App Store Connect applicationBundle Identifier 写错或 API Key 权限不足检查 app_identifier确认 API Key 角色包含 App 管理权限The provided build has already been submitted for review构建号没变重复提交审核升版本号或构建号后再上传日常用脚本自动生成构建号Unable to find a group named xxx测试组名不存在或者名称有空格先pilot list查看准确名称再到后台确认分组状态构建一直 ProcessingApple 后台处理中通常 5 到 15 分钟耐心等待CI 中把任务超时调大不要频繁重试上传提交 Beta 审核失败Beta App Review 信息不完整缺少联系人/测试说明在beta_app_review_info补齐联系方式备注外部测试员收不到通知notify_external_testers为 false或者分组没加人显式开启该参数并确认测试员已加入目标分组明明上传成功但 TestFlight 里看不到构建正在处理或者上传到了另一个 App用--wait-for-build-processing true等待并检查 app_identifier第一类“操作正确但配置错”的问题占了大多数。很多新手拿到报错第一反应是搜解决方案其实更快的是先检查自己的 app_identifier、构建号、分组名这三个基础项。6.2 几条可以复用的实操心得分享几个我长期踩坑后沉淀下来的习惯。第一每次发版前后都保持 Git 工作区干净。increment_build_number会直接修改工程配置文件如果你在 CI 上忘记 commit 这个改动下一次构建可能又回退到旧构建号导致上传失败。我个人习惯是在 lane 里生成构建号后把它同步写进一个version.properties文件避免依赖 Xcode 工程的实际状态。第二把测试组设计成“内部组优先”。我不建议把研发和外部用户混在同一个组里。内部组用于提交代码后立刻自测外部组用于经过一轮验证后再发。pilot 支持多组并行这给了流程设计很大的自由度。第三上传前多看一眼 ipa 的 Build 号。如果你手里同时有旧包和新包很可能会搞混。可以用系统自带的工具快速查看xcrun altool --version-file build/MyApp.ipa确认版本号、构建号符合预期再上传这 30 秒能省掉一条“传错包”事故。第四pilot upload --help是你的好朋友。fastlane 更新很快同一个参数在不同小版本里过时的情况也有不用死记硬背参数多查多试。用别的工具同理每次升级 fastlane 后我都习惯重新跑一遍bundle exec fastlane pilot upload --help确认关键参数没变化。最后再分享一个很实际的扩展思路在当前beta_releaselane 里pilot 上传成功之后还可以顺手把 dSYM 文件上传到某个崩溃收集平台把崩溃日志的符号化工作也自动化掉。TestFlight 的测试包经常会被用户拿来“折腾”没有符号表的话线上崩溃根本看不到堆栈。pilot 管的是“把包送进去”旁边的构建产物怎么归档、怎么分发都是顺手就能接上的环节。我个人在实际操作里最大的体会是pilot 本身不复杂复杂的是你周围的流程和凭证。只要先把“上传 提交审核”这两步跑稳再逐步叠加上通知、汇总、CI、分组策略这一套组合带来的效率提升会非常明显。你不需要记住每一个参数你只需要知道 pilot 能做什么遇到问题知道去查哪里。这一套流程走通以后团队发版就从一个“手艺活”变成了一条安静躺在那里的流水线。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询