DEX475

Use the Wire Service to Get Data

课程介绍

嗨,同学们,咱们今天来聊聊 Lightning Web Components 里一个特别核心的概念——Wire 服务。你可能会问,Wire 服务是什么?简单说,它就是 LWC 专门用来从 Salesforce 后端取数据的机制,而且它取来的数据是,不可变的,,会以流的方式推给你的组件。你可以把它理解成一个智能的数据管道,一旦数据有变化,它就会自动把新数据推给你,你啥都不用干。 那具体怎么用呢?我们在组件的 JavaScript 类里,用 `@wire` 这个装饰器,指定一个叫做 ,LDS wire 适配器, 的东西。这些适配器底层是构建在 UI API 或者 Connect API 之上的,不同的适配器返回的数据形状也不一样。有的适配器返回一条记录,有的返回列表,有的返回元数据,你选哪个取决于你想要什么。 在你决定用一个 wire 适配器之前,老师给你一个非常重要的提醒:一定要先看看有没有更简单的解决方案,比如系统自带的 `lightning-record-form`、`lightning-record-edit-form` 这些基础表单组件。它们内部已经帮你封装好了很多复杂逻辑,如果你只是想快速做一个查看或编辑记录的界面,直接用这些组件可能几行标签就搞定了,根本不需要你自己手写 wire 适配器。而且,当你确实需要写 wire 适配器时,记住一个原则:,始终选择那个能返回你所需最少数据的适配器,。别动不动就拉一大堆字段下来,只取你真正用得到的,性能才会好。 接下来,咱们这一章会带着你把完整的 Wire 服务学透。咱们会讲到: - 两种写法的语法:一种是直接用适配器的 ID,比如直接写 `@wire(getRecord)`;另一种是带配置属性的写法,像 `@wire(getRecord, { recordId: '$recordId', fields })`,这种可以动态传参。 - 怎么从 `@salesforce/schema` 模块里导入对象和字段的引用,让你在写字段列表时安全又方便,甚至还能处理关联字段。 - 一个特别重要的概念:,反应式动态属性,。你会看到我们在属性前面加上一个 `$` 符号,比如 `$recordId`,这样当这个属性的值变化时,wire 适配器就会重新请求数据,完全是响应式的。 - 接收到数据后,咱们有两种处理方式:一种是把结果直接装饰到一个,属性,上,自动拿到它的 `data` 和 `error`;另一种是装饰到一个,函数,上,这样你可以在函数里写自己的业务逻辑,比如何时显示加载器、何时处理返回的数据。 - 咱们还会深入理解数据在整个组件生命周期里是怎么流动的,从 `constructor` 到 `renderedCallback`,知道数据在哪个阶段可用,才能写出更稳健的代码。 - 当然,少不了最实用的例子,比如用 `getRecord` 拉取一条记录,并展示在界面上。 - 还有错误处理,我们要学会用 `FetchResponse` 对象来优雅地处理网络错误、权限问题等等。 - 最后,我们会把这些有线的数据和基础组件(像模板里的输入框、列表)结合起来,做出完整的功能。 同学们,Wire 服务是 LWC 数据交互的基石,掌握它,你就能灵活高效地和 Salesforce 数据打交道了。接下来,咱们就一步一步把这些内容吃透。记得跟着我的节奏,把每个例子都动手敲一遍,你很快就能得心应手。好,咱们开始吧!

课程章节

本课程共有 7 个章节

  • 1

    Wire Service Syntax & @salesforce/schema Imports

    第 292 页

    大家好,今天我们聊聊 Lightning Web Components 里一个非常常用的功能——Wire 服务。你写组件时,经常要从 Salesforce 拿数据,这时候用 Wire 就很方便。不过它有一些细节如果没注意到,数据可能出不来,调试起来挺头疼的。我就把这些点用最简单的话给你讲清楚。 首先,Wire 服务是通过 `@wire` 这个装饰器来用的,它需要三个东西:一个适配器 ID,一个配置对象,还有你要把结果放到哪个属性或函数里。适配器 ID 就是告诉它用哪个数据源,比如 `lightning/uiRecordApi`,注意这里用的是斜线符号,像路径一样。配置对象里放参数,例如你要的记录 ID 或者字段什么的。一定要记住,配置对象的属性绝不能是 `undefined`,哪怕你设成 `undefined`,Wire 服务直接不触发,什么反应都没有。所以如果你用变量,必须保证它在 Wire 执行时已经有值了。 接下来我们说字段引用。直接从 `@salesforce/schema` 里导入字段,比如 `Account.Name`。这样做的好处特别多:第一,编译的时候就能检查字段存不存在,写错了马上报错;第二,能防止字段被删除,如果管理员删了那个字段,组件打包就会失败,你提前知道;第三,字段重命名会级联更新,比如管理员改字段名,你这个引用会自动随着变;最后,打包或变更集的时候能确保这个字段被包含进去,不会漏。所以强烈建议用 schema 导入,别自己硬编码字段名字符串。 导入对象和字段有固定格式。对象导入直接用 API 名称,比如 `import ACCOUNT_OBJECT from '@salesforce/schema/Account'`。字段导入用 `对象.字段` 格式,比如 `import NAME_FIELD from '@salesforce/schema/Account.Name'`。如果你要跨关系拿数据,最多支持三级,格式是 `对象.关系1.关系2.关系3.字段`,再深就不行了。比如你想拿某个账户的第一个联系人的报告对象的字段,就可以写成 `Account.Contacts.ReportsTo.FieldName`,但最多三级关系。 复合字段这里有点特殊。什么复合字段呢?比如地址字段,一个字段里面包含街道、城市、邮编这些子字段。对于读取,你可以在 schema 里直接导入这个复合字段本身,比如用 `import BILLING_ADDRESS from '@salesforce/schema/Account.BillingAddress'`,这样读出来是对象。但是写入的时候,你不能写整个复合字段,必须拆成它的组成字段一个一个写。而且地址和地理位置这两种复合字段的子字段,只支持字符串语法来引用,不能用 schema 导入。所以如果你要设置 `BillingStreet`,你就直接用字符串 `'BillingStreet'`,而不是 schema 导入。 最后提醒几个导入限制。人员账户(Person Account)的字段挺特殊,比如 `PersonEmail`,你不能直接从 Account 对象导入,要从 Contact 导入,格式是 `Account.PersonContact.Email` 这种,或者说从 Contact 对象里引用 `__PC` 字段。外部对象(以 `__x` 结尾的)根本就不支持 schema 导入,只能用字符串。还有知识库对象 `Knowledge__kav`,你要用字段的时候也必须用字符串语法,不能从 schema 导入。这些特殊情况最好记一下,免得写出来报错。 总结一下,Wire 服务很好用,但要注意配置对象别传 undefined,字段尽量用 schema 导入,关系别超三级,复合字段读写有区别,特殊对象用字符串。这些点掌握了,你写数据相关的组件会顺手很多。那这节课就到这里,我们下次见。

    查看详情
  • 2

    Reactive $ Prefix — Dynamic Configuration

    第 293 页

    同学们,今天我们来聊聊Lightning Web Components里一个非常酷的东西,就是那个带美元符号的`$`前缀,它能让你的组件真正“活”起来。 想象一下,你在配一个线适配器,比如获取某个记录的详情。如果你直接写一个普通的属性值传进去,这个值算一次就完事了,后面就算属性变了,组件也不会重新获取数据。但是,一旦你在配置对象的属性前面加上`$`,比如`$recordId`,你就告诉了Salesforce的通讯服务:“嘿,帮我盯着这个值,只要它一变,马上用新数据重新给我跑一遍这个适配器。” 具体来说,比如你用了`@wire`从服务端拉数据,配置里写了`{ recordId: '$recordId' }`,那么`recordId`这个属性无论是私有属性、还是通过getter/setter模式,甚至是带有`@api`装饰器的公共属性,只要它的值发生变化,框架就会自动检测到,然后重新调用适配器,获取新数据,最后触发组件重新渲染。整个过程是全自动的,不用你手动去调刷新。 不过这里有个小陷阱要记住:`$`前缀只对配置对象的最顶层属性起作用。你要是把它嵌套在数组里,比如写成`['$accountId']`,那它就变成了一个普通的字符串“$accountId”,完全失去了反应性。所以,一定要用在正确的位置。 当你用`@wire`装饰一个属性时,组件刚创建完,还没拿到数据的时候,这个属性的默认值是`{ data: undefined, error: undefined }`。也就是说,`data`和`error`都是`undefined`。所以你在模板里直接访问`wireProperty.data.field`就有可能报错。这时,你必须在模板里用`lwc:if`来检查,比如`lwc:if={wireProperty.data}`,只有数据真的来了,才去渲染依赖它数据的那部分界面,这样就能避免访问`undefined`上的属性。 另外,线适配器的输出还可以串联起来,形成一条“响应式数据管道”。举个例子,你第一个线适配器拿到了某条记录的数据,存在`record`对象的`data`里面,你可以直接用`$record.data.fieldName`作为第二个线适配器的输入参数。这样,一旦第一个适配器返回的数据发生变化,第二个适配器也会自动重新获取数据,整个数据链全都联动起来,非常强大。 总结一下,`$`前缀就像是给你的数据接入了一条“自动感应神经”,让组件能实时响应数据变化。记住它的规则:只用在配置对象顶层,和任何反应式属性搭配使用,模板里别忘了用`lwc:if`保护,再试试点串起多个适配器,你会发现构建动态、实时的Salesforce界面变得特别简单。 好了,这一小节就到这里,下次我们接着深入实践。

    查看详情
  • 3

    Decorate a Property vs Decorate a Function

    第 294 页

    我们来聊聊在 Lightning Web Components 中,用 `@wire` 装饰器拿 Salesforce 记录数据的时候,到底是用“属性装饰”还是“函数装饰”?这其实是个很常见的纠结,但选哪个,完全取决于你要拿数据来做什么。 先说说最简单的“属性装饰”。它直接把拿到的结果赋给一个类属性,比如 `data` 和 `error`。这样你就能很方便地通过 `record.data` 或者 `record.error` 来访问。如果你想直接展示某个字段,比如账户名称,你可以用 `getFieldValue` 这个助手函数,或者直接按 JSON 结构去取。这种方式特别直接、干净,最适合“拿到数据就显示”的场景,不用写额外的处理逻辑。 但有时候,你希望在数据一到手的时候,马上做点额外的动作——比如把数据重新格式化一下,或者更新页面上的其他内容,又或者需要做一些错误处理、甚至触发一些副作用。这时候,“函数装饰”就派上用场了。它给你一个回调函数,这个回调函数会接收到一个对象,里面包好了 `data` 和 `error`。注意在解构的时候,顺序不重要,你写 `{data, error}` 就行,它不会管你先写哪个。然后在这个回调里面,你就能尽情地处理数据、转换格式、处理报错,或者把值保存到你自己的响应式属性里。更重要的是,这个回调不一定非要在组件连上或者渲染之后才触发,它可能在之前或之后运行,所以你最好在模板里加个 `lwc:if` 的判断,防止数据还没回来就去渲染,导致 undefined 报错。 另外还有一个很实用的小帮手,就是 `lightning/uiRecordApi` 模块里的 `getFieldValue()` 函数。它专门帮你从返回的记录结构里把字段值轻松抽出来,不用你自己一层层去钻对象,省时省力,代码也干净。 总结一下:如果你的目标只是拿到数据直接显示,用属性装饰,简单又明了;如果你需要在数据到时做点儿加工,那就用函数装饰,灵活又强大。记住这两个场景,选起来就很容易。

    查看详情
  • 4

    Wire Service Data Lifecycle

    第 295 页

    好,我们来看一下这个关于有线服务数据生命周期的内容。我会一条一条地给你讲清楚,让你理解框架是怎么帮我们管理数据的,我们写组件的时候需要注意什么。 首先,这句话很重要:有线服务的数据生命周期是由框架来管理的,而不是由我们自己的组件来管理。也就是说,我们不用操心什么时候去请求数据、什么时候更新,这些框架都帮我们做好了,我们只需要声明这个组件需要哪些数据就行。这样一来,代码就简单多了,也减少了出错的可能。 接着看这里,有线适配器只有在所有动态参数,也就是那些以美元符号开头的参数,都已经定义了值的时候,才会真正去触发。比如,你的组件有一个 `@wire` 需要 `recordId`,但是这个 `recordId` 可能是从父组件传进来的。如果父组件还没传过来,它的值就是 undefined,那么有线服务就会一直等待,直到 `recordId` 有了具体的值,才会去取数据。这也是一种保护,防止我们用不完整的参数去请求数据,得到一个错误的结果。 然后,我们看一下它初始的状态。在组件构造完成、但是数据还没到的时候,`@wire` 提供给组件的数据对象长这样:`{ data: undefined, error: undefined }`。也就是说,我们可以直接根据 `data` 是不是 undefined 来判断数据到了没有,如果 `error` 不是 undefined,那肯定是出错了。我们可以在模板里或者方法里用这个来做条件渲染,比如显示一个加载的提示。 接下来这段话提醒我们,数据到达的时间是不确定的。如果数据已经在缓存里了,那几乎是瞬间就到了;如果缓存里没有,那就得等服务器来回的时间,可能需要一两秒。所以,我们不能假设数据一定会在某个特定的生命周期钩子到达,比如不能认为在 `connectedCallback` 里就一定能拿到数据,或者认为在 `renderedCallback` 里数据一定是最新的。永远不要写那种依赖数据到达时机的代码,那样会导致有时能运行有时不能的 bug。我们要做的是,响应式的处理数据的变化:当 `data` 改变时,框架会通知我们,我们再去更新界面。 另外,一个有线连接可以在生命周期内多次发出请求,而不需要我们去更改什么配置。比如,底层缓存失效了,或者我们做了一些引起数据变化的操作,LDS(Lightning Data Service)就会自动帮我们刷新数据,这和组件的生命周期钩子没有直接关系。我们不需要手动去重新连接,只要安心等着数据更新就行。 再强调一次,有线适配器的调用是异步的,除非有缓存的情况。因为有缓存的时候可能直接返回数据,所以响应返回的顺序可能不是按调用顺序来的。如果你连续发出两个请求,有可能后一个请求先返回,这种情况是可能发生的。我们不能假定响应的顺序就是调用的顺序,处理逻辑时要考虑到这一点。 有的时候,我们可能需要手动刷新数据,比如做了一个 mutation(像更新记录)之后。框架提供了两个方法来手动刷新:对于 Apex 方法的有线连接,使用 `refreshApex()`;对于 GraphQL 的有线连接,使用 `refreshGraphQL()`。调用它们应该在 mutation 完成后调用一次就好,千万不要把它放在定时轮询的循环里去不停刷新,那样会消耗大量的服务器资源,而且不是有线的设计初衷。想让数据自动定时间隔刷新,应该用别的方式,而不是不停调用 refresh。 最后一点,要注意,服务器端同步发生的更改,比如触发器或者流程自动更新了记录,并不会触发你有线服务重新获取数据。LDS 是基于缓存和通知机制的,像这种在服务器后台直接改的数据,没有通过 UI API 的变更通知,所以本地缓存不知道已经变了。如果你需要拿到这种及时的更新,可能需要考虑用别的方式,比如定时刷新(但要小心使用),或者用 Streaming API 来订阅变更。 好了,简单总结一下:有线服务让数据管理变得省心,但我们要理解它的异步特性、缓存机制和刷新规则。记住,不要依赖数据的到达时机,写响应式的代码,在 mutation 后调用一次 refresh,但别在轮循环里用。理解了这些,就能更好地驾驭 LWC 的数据绑定了。 这些就是今天这个 slide 的重点内容,有没有什么地方需要我再详细解释一下的?

    查看详情
  • 5

    Get Record Data — Practical Example

    第 296 页

    这一页Slide,我们来看一个实际的例子,告诉大家怎么在 Lightning Web Component 里面,用 `getRecord` 把联系人的数据完整地显示出来。这是一个很典型的模式,大家以后写组件的时候可以直接套用。 首先,在模板里我们用了 `lwc:if` 这个指令。为什么要用它呢?因为数据还没从服务器返回来时,`contact` 对象里面的 `data` 可能是 `undefined`,直接去读字段就会报错。用 `lwc:if` 判断一下,只有当 `contact.data` 有值的时候,才去渲染内容,这样不仅避免出错,还能顺便处理加载中和错误状态。 那数据取回来以后,怎么拿到字段的值呢?UI API 返回的记录数据,是嵌套在 `contact.data.fields.字段名.value` 这个路径下面的。比如联系人姓名,就是 `contact.data.fields.Name.value`。我们一般会给每个要用的字段,单独写一个 getter 方法,这样在模板里直接用 `{name}` 而不是那一长串路径,代码更干净。 接着,这个组件通常会被放在记录页面上,它怎么知道当前是哪个联系人的记录呢?靠的就是 `@api recordId`。在组件 JavaScript 里申明 `@api recordId`,Salesforce 平台会自动把页面上下文的记录 ID 传进来。再把这个 `recordId` 传给 `getRecord` 适配器,配置成 `$recordId`,注意前面有个美元符号,这样就变成了响应式属性。意思是,如果页面上的记录发生了切换,比如从联系人 A 跳到联系人 B,`recordId` 一变化,`getRecord` 就会自动重新请求新数据,不需要你手动干预。 为了代码更清晰,官方还推荐我们用 `getFieldValue` 这个工具方法,直接从记录对象里提取字段值。比你自己手动 `contact.data.fields.xxx.value` 这样一层层去点,包更健壮,也更容易理解。写法通常是 `getFieldValue(contact.data, CONTACT_FIELD)`,这里 `CONTACT_FIELD` 是 import 进来的字段引用,比如从 `@salesforce/schema/Contact.Name` 引入。 在显示联系人电话和电子邮件的时候,别直接用普通文本,可以用 Lightning 基础组件 `lightning-formatted-phone` 和 `lightning-formatted-email`。它们会自动根据用户的区域设置来格式化号码和邮件地址,甚至还能处理可点击的链接,用户体验更好。 最后,错误处理这块,我们通常会封装一个 `c-error-panel` 组件。它会接收 `contact.error`,用友好的方式把错误信息展示给用户,而不是让整个页面崩掉。这样整个显示联系人的模式就完整了:通过 `getRecord` 拿数据,用 `lwc:if` 做防御,`getFieldValue` 取值,格式化组件展示,错误面板兜底,`recordId` 自动关联当前记录。把这个模式记熟,开发记录相关的组件会又快又稳。

    查看详情
  • 6

    Handle Errors in Lightning Data Service

    第 297 页

    同学们,今天我们来聊聊LDS电线适配器里的错误处理,这个知识点非常重要,但容易踩坑。我先问大家一个问题:你在JavaScript里处理错误,是不是习惯用try-catch把可能出错的代码包起来?没错,这是同步代码的好习惯。但在Lightning Web Components里,用LDS电线适配器的时候,这个try-catch就不一定管用了。 为什么呢?因为电线适配器的响应是异步的。你想象一下,我们给电线函数传个参数,它不会马上返回结果,而是在背后帮我们取数据,然后通过回调通知我们。所以,假设你在外面包个try-catch,代码执行到那里,try-catch就执行完了,可错误可能还没发生呢。错误是发生在后面回调里的,比如在setSYS回调里,或者在电线适配器自己的响应里。这时候外层的try-catch根本抓不到这些错误。 所以我们得记住一个原则:始终把try-catch放到回调里面去。具体来说,对于电线函数,我们推荐使用if (data) { ... } else if (error) { ... }这种分支来处理。在else if (error)这个分支,专门处理那些配置性的错误,比如网络不通、传给适配器的参数不对,这些错误电线适配器会直接给你一个error对象。而你拿到数据之后,在if (data)分支里,你可能会对数据做进一步处理,比如解析、转换,这些操作本身可能出错,那就在这个分支里面再用try-catch包一下,确保万无一失。 再来看这个错误对象长什么样。LDS电线适配器返回的错误,其实是模仿了浏览器原生的Fetch API的Response对象。所以它有status、body、ok和statusText这些属性。你拿到error手里,就可以根据status判断是400还是500,根据ok是不是false,等等。 不过body里的内容,它会根据你请求的东西不同而变化。比如你用UI API去读取数据,body往往是个数组;如果是写入操作,比如创建或更新,body可能是个对象,里面带着字段级的错误信息。如果你调用的是Apex方法,body就是Apex方法抛出的那个错误的body。要是纯粹的网络错误,可能就是个简单的对象。所以处理错误的时候,你得清楚自己调的是什么,才能正确地解析body。 还有一点,电线适配器在触发之前,data和error这两个变量都是undefined。所以我们在模板里绑定数据的时候,得多加小心,得先用if:true指令或者类似的判断,保证数据不是undefined才去访问它的属性,否则页面容易崩。 最后推荐一下,Salesforce提供的lwc-recipes项目里,有个ldsUtils模块,里面封装了不少可重用的错误处理工具。大家可以参考一下,能帮我们少写很多重复代码,也能让错误处理更规范。 总结一下,LDS电线适配器的错误处理,核心就是:别让外层的try-catch骗了你,把错误抓到回调里面来;用if (data)和else if (error)分类处理;搞懂error对象的结构;数据未定义时要保护模板。这样,你的组件就会更健壮,用户也不会看到莫名其妙的崩溃啦。好了,这就是今天的内容,大家消化一下,有问题随时提。

    查看详情
  • 7

    Wire Service with Base Components

    第 298 页

    同学们,我们接着刚才的内容往下讲。当你发现 Lightning-Record-Edit-Form 或者 Record-View-Form 这些标准表单组件已经满足不了你的业务需求时,不要着急,我们其实有更灵活的办法。 你可以把 Salesforce 提供的有线服务,也就是 Wire Service,和基础组件结合起来,这样就能完全自己掌控 UI 的设计和行为了。 首先,输入组件,比如 lightning-input、lightning-combobox 这些,它们本身就带着很完善的数据校验功能。你加了 required 属性,或者指定了 pattern、min、max,它们会自动帮你在页面上显示出错提示,不需要你额外写一大段校验逻辑。 其次,还有一些嵌入式组件,比如输出邮件、电话号码的组件,它们会根据用户的语言和地区设置,自动生成符合 HTML 语义的链接——比如 mailto 链接、tel 链接,甚至还能直接跟 Google 地图集成,帮你在地图上显示地址,这些都不用你操心。 举个例子,我们要做一个自定义的“姓名”表单,既需要从记录里取姓名字段的值,又需要从称呼这个下拉列表里取可选项。那就可以同时用上两个 Wire Adapter:一个用 getRecord 适配器拿到字段当前的值,另一个用 getPicklistValues 适配器拿到称呼字段的所有可选项。然后自己写一个简单的 getter 函数,把 picklist 值的对象数组,转换成 lightning-input-name 组件需要的 options 格式就行了。 再比如地址的展示,我们可以用 lightning-formatted-address 来规范地显示地址,再用 lightning-map 的一个静态地图模式,把 getRecord 拿到的街道、城市、省、邮编、国家这五个字段拼接起来,生成一个 Google 地图的缩略图预览。这样用户就能直观地看到地址在地图上的位置。 这种开发模式最大的好处就是:布局完全由你说了算,你想用什么组件就用什么组件,不用再受标准 form 的限制。无论是字段的排列顺序、呈现样式,还是交互逻辑,你都有绝对的控制权。所以,当你觉得标准表单不够用的时候,就大胆地拿起 Wire Service 和基础组件,去打造真正符合需求的界面吧。

    查看详情