面向客户的 Unlocked Packages

学习从 change set 开发转向包开发:用 Salesforce DX 和 unlocked packages 组织元数据、构建并安装第一个包(DreamHouse LWC),以及三种拆解现有元数据的模型和包依赖管理。...

📅 2026/10/4 ✍️ ponybai 🏷️ salesforce, developer, salesforce-dx, headless

学习目标

slide_2

完成本单元后,你将能够:

  • 描述企业客户如何使用 unlocked packages。
  • 描述包开发与 change set 开发的不同。
  • 描述如何使用包来部署元数据。

为什么包开发是未来

slide_3

如果你完成了 Package Development Model 模块,就知道模块化、基于包的开发是游戏规则的改变者。你可能在想:如何把这些原则付诸实践?

无论你是平台新手还是老手,打包都适合你。作为长期客户,你在平台上定制和构建得越多,org 中的复杂度就越高——你的单一 Salesforce org 成了所有元数据的巨大容器,我们称之为「happy soup(快乐汤)」。如果你最近推出过 Salesforce 定制,你是用 change set 甚至非托管包把元数据部署到生产 org 的,这种传统开发模式就是 change set 开发,其「事实来源」是生产 org。

但在包开发模型中,新的「事实来源」是版本控制系统。你用 Salesforce DX 项目把源代码组织到包目录中,最终目标是用这些目录创建可版本化、易维护、易更新、易安装、易升级的包。转向包开发不是「全有或全无」的选择,你可以从小处着手、逐步采纳。

什么是包?

slide_4

如果你刚接触打包,可以把包(package)想象成一个装满元数据的容器,是可分发的功能单元。

想象你为员工构建了一个费用追踪应用,包含自定义对象、Apex 类、Lightning 组件等。在 change set 模型中,这些元数据都在 org 里,但并未以易升级维护的方式隔离组织。在包开发模型中,你把元数据组织到定义清晰的容器——即包中。最令人信服的理由归结为:组织你的元数据。没有包,元数据会变得难以驾驭。

Unlocked Packages 来救援

slide_5

Salesforce 提供几种不同类型的包。我们先使用一种特别的包类型——unlocked package,特别适合内部业务应用。

Unlocked packages 让你以可追踪的方式在 org 中添加、编辑、删除元数据,从而更轻松快速地复用组件、升级应用。为追踪变化,你需要创建包的版本(version)——每个版本都是不可变产物(immutable artifact),是包内容的快照。在生产 org 中,你可以检查哪些元数据来自哪个包版本。

不再用电子表格追踪元数据变化,不再用便签!

什么是 Unlocked Package?

slide_6

Unlocked package 给了你很大的灵活性:

  • 灵活性:管理员可以响应紧急变更请求直接在生产 org 中修改,因为 unlocked package 中的元数据可在生产 org 中修改。
  • 责任:unlocked packages 是开发者控制的——安装任何新包版本都会覆盖直接在生产 org 中所做的更改。关键是管理员必须把生产中的直接更改告知开发团队,以便相应地更新包。

你能往 unlocked package 里放什么?好消息——几乎所有类型的 Salesforce 元数据和组件都可以。Metadata Coverage 报告是各渠道元数据覆盖信息的权威来源,见 developer.salesforce.com/docs/metadata-coverage。

如何开始?

slide_7

采用 Salesforce DX 工具和开发原则不是「全有或全无」的事。你可以小步走,也可以直接跳进深水区。

  • 如果你刚接触 Salesforce 或开始新项目,可以从一开始就遵循包开发模型。
  • 如果你有大量未区分的源代码,可以逐步采纳打包,开始把元数据切分到逻辑容器中。

你可以先从打包可复用的组件和 schema 开始,时机成熟时再定义包之间的依赖。速度多快、复杂度多高,由你决定——这就是 unlocked packages 的威力。

用 Salesforce DX 进行包开发

slide_8

看看打包工作流(假设你是唯一的开发者兼发布经理,另一位同事负责 QA):

  1. 开发:创建一个与 DX 项目中目录关联的包,修改元数据,用 scratch org 开发和单元测试。
  2. 构建与测试:创建包版本,QA 安装测试,修复 bug、加功能,再创建新版本——迭代。
  3. 发布:把好版本安装到沙箱做用户验收测试(UAT),最终安装到生产。

安装包版本类似于部署元数据,可安装到任何 org(scratch org、沙箱、生产)。这就是包开发模型的基本应用生命周期。

发布第一个包之后

slide_9

软件开发的脚步从不停歇,下一个版本很快就到。你可以在添加、修改、删除包元数据时创建所需的新版本。每个包版本都有版本号(如 1.3.0.2),用包升级(package upgrade)把更改应用到已安装的版本上。

「修改元数据 → 创建包版本 → 测试包版本 → 部署到生产」这个过程可以无限重复。准备好试试了吗?来构建你的第一个 unlocked package。

构建你的第一个 Unlocked Package

slide_10

本单元动手实践:配置环境、克隆 DreamHouse LWC 源代码、配置并创建 unlocked package、在 scratch org 中测试、安装到 Trailhead Playground。

学习目标

slide_11

完成本单元后,你将能够:

  • 熟悉打包的 CLI 命令。
  • 描述基本的打包用例。
  • 打包 DreamHouse LWC 示例应用并安装到 Trailhead Playground。

为什么我们爱包开发

slide_12

包开发遵循软件开发生命周期的最佳实践,与 Salesforce DX 的项目、CLI 命令和 scratch org 兼容;把生命周期阶段间的所有变更封装到版本化产物中;更易接纳新功能请求;提供更好的审计历史;组织源代码;促进迭代和模块化开发;支持包间依赖;支持持续集成和持续交付。

典型用例:财务部的费用报销应用、HR 的推荐(Referral)应用——任何内部业务应用都适合用 unlocked packages 交付。每个应用都从全新项目开始,源代码以 source format 存于 DX 项目并提交到版本控制,交付时创建 unlocked package,在 scratch org 或沙箱测试后安装到生产。

配置环境

slide_13
  1. 创建新的 Trailhead Playground(全新 org)。
  2. 在 Setup 中启用 Dev Hub。
  3. 启用 Unlocked Packages and Second-Generation Managed Packages。
  4. 创建 GitHub 账号(如没有)。
  5. 安装 Salesforce CLI。

全新 playground 确保干净环境;Dev Hub 是创建包的必要条件;CLI 提供打包命令。

获取 DreamHouse 源代码

slide_14

以 DreamHouse LWC 应用为例(一个使用 Lightning Web Components、Apex 等的独立房地产应用)。在命令行中切到想放源代码的目录,运行:

git clone https://github.com/trailheadapps/dreamhouse-lwc

git clone 命令用 Salesforce DX 项目结构创建 dreamhouse-lwc 文件夹,包含 DX 项目文件和 scratch org 定义文件。一个 DX 项目可以有多个包,通过目录结构隔离不同包,在合适处共享组件。

配置你的包

slide_15
  1. 授权 Dev Hub org 并登录:
    sf org login web --set-default-dev-hub --alias DevHub
  2. 确认 Dev Hub 已连接:
    sf org list
  3. 切到 dreamhouse-lwc 目录。
  4. 打开 sfdx-project.json,记下 sourceApiVersion,然后用干净配置替换:
    {
       "packageDirectories": [
          { "path": "force-app", "default": true }
       ],
       "namespace": "",
       "sfdcLoginUrl": "https://login.salesforce.com",
       "sourceApiVersion": "61.0"
    }
  5. 确保 sourceApiVersion 与原文件一致,否则更新。

为什么不用命名空间?

slide_16

包命名空间对 unlocked packages 是可选的。包含命名空间有助于组织包组件,但需要额外的设置和规划,所以本单元跳过。

重要:如果你正把元数据从 happy soup 迁移到 unlocked package,请创建不带命名空间的 unlocked packages——这样元数据从未打包状态移到 unlocked package 时,元数据元素的 API 名称不会改变,无需重命名。

创建包

slide_17

克隆 DreamHouse 后,从 dreamhouse-lwc 目录创建无命名空间的 unlocked package:

sf package create --name dreamhouse --description "My Package" --package-type Unlocked --path force-app --no-namespace --target-dev-hub DevHub

参数说明:--name 是包名(别名,供后续命令使用);--path 是包含包内容的目录;--package-type 指定包类型(unlocked)。

打开 sfdx-project.json,packageDirectories 里会看到包名和版本占位符,命令还创建了 packageAliases 段,把包名(别名)映射到包 ID(0Ho)。若忘了别名,运行 sf package list 列出 Dev Hub 关联的所有包。

创建 Scratch Org 测试

slide_18

创建一个 scratch org(别名 MyScratchOrg)来安装 unlocked package。测试用的 scratch org 是执行打包开发生命周期单元测试阶段的便捷方式。该命令用默认 scratch org 定义文件,创建与 Trailhead Playground 同版本的 Developer Edition scratch org,时长 30 天。

sf org create scratch --definition-file config/project-scratch-def.json --alias MyScratchOrg --duration-days 30

创建包版本并安装

slide_19

准备发布时,创建包的快照——包版本(package version)。先更新 sfdx-project.json 的 versionName 为 Version 1.0、versionNumber 为 1.0.0.NEXT,然后在 dreamhouse-lwc 目录创建包版本:

sf package version create --package dreamhouse --installation-key test1234 --wait 10 --target-dev-hub DevHub

创建完成后,packageAliases 段会新增条目 "dreamhouse@1.0.0-1": "04txxx"。用版本别名安装到 scratch org:

sf package install --wait 10 --publish-wait 10 --package dreamhouse@1.0.0-1 --installation-key test1234 --no-prompt --target-org MyScratchOrg

安装完成后打开 scratch org 验证:sf org open --target-org MyScratchOrg,再到 Setup → Installed Packages 查看。unlocked package 中的 Apex 代码必须满足最低 75% 代码覆盖率才能安装到生产 org(本例 DreamHouse 示例仓库覆盖率不足,故未计算)。

发布并安装包版本

slide_20

包刚创建时是 beta 状态,不能安装到生产 org。当版本准备好发布时,可以把它提升(promote)为 released:

sf package version promote --package dreamhouse@1.0.0-1

(需要满足 75% 代码覆盖率,DreamHouse 示例可能不满足,所以我们跳过提升,继续用 beta 版本。)

最后安装到 Trailhead Playground:

  1. 把 playground 加入授权 org 列表:
    sf org login web --alias MyTP
  2. 安装包版本:
    sf package install --wait 10 --publish-wait 10 --package dreamhouse@1.0.0-1 --installation-key test1234 --no-prompt --target-org MyTP
  3. 打开 playground:
    sf org open --target-org MyTP
  4. Setup → Installed Packages 查看,点击 dreamhouse → View Components,再从 App Launcher 打开 DreamHouse 应用。

组织你的元数据

slide_21

本单元学习把未打包元数据组织到包中的策略、三种拆解模型,以及包依赖的处理方式。

学习目标

slide_22

完成本单元后,你将能够:

  • 列出把未打包元数据组织到包中的关键策略。
  • 识别 unlocked packages 如何相互依赖。
  • 描述 3 种包开发模型及各自适用场景。

把包开发原则付诸实践

slide_23

把元数据组织到包中往往是个迭代过程,不是「全有或全无」。通过打包 DreamHouse,我们展示了:把 CLI、项目和 unlocked packages 集成到应用生命周期;采纳组织元数据和创建包边界的最佳实践;在构建新应用时使用 unlocked packages。

现在你可以:独立测试和部署应用源代码、把应用 schema 与其他元数据隔离、通过重复流程添加新功能、为应用版本化、把新版本作为升级安装到现有版本。

为新建或现有应用组织元数据

slide_24

DreamHouse 的元数据按类型组织:

  • Schema:自定义对象(Broker__c、Property__c、Favorite__c)。
  • Lightning 应用和组件:Property Explorer、Property Record Page。
  • 流程(Flows):新房源通知、价格变动推送。
  • Einstein 服务:上传图片的图像处理。
  • Bots:通过 Facebook Messenger、Slack、Alexa 搜索房源。

一个 unlocked package 包含所有这些元数据,作为一个可部署、可版本化的单元。包边界就是应用边界——自包含、完整、可独立部署。

拆分 org 现有元数据

slide_25

对用了多年 change set 开发的现有客户来说,怎么解开 happy soup?没有唯一的处方——一半是科学、一半是艺术,你才是判断哪些元数据属于哪些 DX 项目的最佳人选。

开始先回答这些问题:开发团队能否独立发布应用/新功能/定制?能否识别代表某个应用的元数据?能否把元数据组织成不同功能的模块?创建非托管包时看到了哪些依赖?

如果 org 里有多个应用和定制,预计会看到 DX 项目之间的一些依赖——这是正常的,也是 unlocked packages 依赖管理的意义所在。

拆解元数据的三种模型

slide_26

实际场景中,你可能会组合使用这些策略并按业务需求调整:

  • 基于应用(App-based):识别代表某个应用的元数据,类似 DreamHouse 的做法,但元数据已存在于 org 中。
  • 基于定制(Customizations-based):组织生产 org 中对标准对象的定制和功能变更,如对 Sales Cloud、Service Cloud 或 AgentExchange 应用的定制。
  • 共享库(Shared library):存在相互依赖时,用一个公共 DX 包组织一组 Apex 类或常用自定义对象,其他包依赖这个公共包。

用非托管包作为起点

slide_27

把未托管元数据组织到多个包的一个好起点工作流:

  1. 从生产 org 选一小部分自包含的未打包元数据。
  2. 创建非托管包隔离这些元数据,观察系统自动拉入了哪些依赖元数据(帮你发现隐藏依赖)。
  3. 用 sf project retrieve start 从非托管包检索源代码。
  4. 建立 Salesforce DX 项目和 git 仓库管理包元数据。
  5. 用 sf project deploy start 推到 scratch org,验证元数据。
  6. 用 --no-namespace 标志创建 unlocked package。
  7. 测试并部署 unlocked package。
  8. 通过所有 CI 和沙箱 UAT 后,提升包版本。
  9. 在生产 org 安装 unlocked package。

一旦为该元数据创建包,它就会自动移入包中,并覆盖 org 中已有元数据、更新内部引用。

关于包依赖

slide_28

Unlocked packages 支持丰富的依赖链:

  • Unlocked → AgentExchange 包:你对某个 AgentExchange 包的定制可放在 unlocked package 中,安装时 AgentExchange 包必须已存在。
  • Unlocked → Unlocked:例如费用报销包与现有 Payroll 包共享后端集成,费用报销包依赖 Payroll 包。
  • 多层依赖:包 A → 包 B → 包 C,支持多级依赖。

依赖在 sfdx-project.json 的 packageDirectories 段中表达。如果拆分 org 元数据非常困难,可以考虑 org-dependent unlocked packages——允许包依赖安装 org 中未打包的元数据。


文章来源:Trailhead - Unlocked Packages for Customers