一、开始 Lightning 开发
如果你是经验丰富的 Visualforce 开发者、想用最好的 Lightning Experience 工具构建现代有吸引力的用户体验,你就来对了地方。这个模块专为从 Visualforce 过渡到 Lightning Web Components(LWC)的开发者设计:你会了解为什么要有 Lightning Component 框架、UI 开发如何不同(客户端渲染 vs 服务端渲染)、LWC 基础(ES 模块与装饰器)、编码概念(属性与条件渲染)、在 JavaScript 中处理用户操作、处理 Salesforce 数据(LDS vs Apex),以及导航服务。前置要求:有 JavaScript 经验。
欢迎 Visualforce 开发者
Visualforce 是为 2006 年的 Web 设计、基于服务端渲染的页面架构。2006 年之后格局变了——如今用户期待更交互、沉浸、响应的体验。为此 Salesforce 推出了构建在 Lightning Component 框架上的 Lightning Experience。
核心的思维转变:
- Visualforce(服务端):页面 → 服务器 → HTML → 浏览器;逻辑在服务端(Apex)运行;页面重载才更新状态。
- LWC(客户端):组件 → 浏览器中渲染;逻辑在客户端(JavaScript)运行;响应式状态即时更新、无需重载。
一句话:Visualforce = 「在服务端渲染,发送 HTML」;LWC = 「在客户端渲染,向服务器取数据」。这是最根本的转变,其余一切都由此而来。Visualforce 在 Lightning Experience 中仍能工作,但要构建真正无缝、现代的体验,你需要 LWC。
Lightning Component 框架的演进
Salesforce 的 UI 框架经历了三代:Visualforce(2006,服务端渲染)、Aura Components(2014,引入客户端渲染但组件模型是专有的)、Lightning Web Components(2019+,基于现代 Web Components 标准——自定义元素、shadow DOM、HTML 模板、ES 模块)。
Lightning Component 框架包含两个组件模型:
- Aura Components(遗留):专有组件模型(.cmp 标记 + JS 控制器 + helper)、双向数据绑定,仍受支持但不推荐用于新开发。
- Lightning Web Components(现代):基于标准(Web Components)、HTML 模板 + JS 类 + CSS、单向响应式数据绑定,推荐所有新开发使用,性能更好、bundle 更小。
两者可在同一页面共存,Aura 可以包含 LWC(渐进迁移的推荐模式)。如果你在开始新开发:用 LWC,毫无疑问。LWC 不是「新的 Visualforce」,而是不同的范式——它构建在浏览器标准上,学到的技能(JS 模块、自定义元素、响应式状态)可迁移到任何 Web 开发,不只 Salesforce。
UI 开发:低代码与专业代码
Lightning Experience 提供两种构建 UI 的方式:
- 低代码(low-code,点击式):Lightning App Builder(拖拽组装页面)、Dynamic Forms、Flow Builder、Quick Actions、URL 按钮、Utility Bar。
- 专业代码(pro-code,JavaScript):Lightning Web Components(HTML+JS+CSS 定制 UI)、Aura Components、Visualforce、Apex(服务端业务逻辑)。
作为开发者,开始新东西时你的本能可能是立即编码——但先考虑用低代码工具做重复的样板工作,把时间留给复杂需求的编码。大多数现实方案两者兼用:用 App Builder 组装标准页面,再用自定义 LWC 增强独特功能。作为 Visualforce 开发者,你的 Apex 技能直接迁移(仍要写 Apex 做服务端逻辑),标记技能适配 LWC 的 HTML 模板,最大的学习曲线是JavaScript。
工具链:专业 LWC 开发用现代工具链——Visual Studio Code(取代 Developer Console)、Salesforce CLI(命令行管理组织与部署)、Salesforce Extension Pack、Git(版本控制)、Node.js + npm(JS 运行时与包管理)。这是全球专业 JavaScript 开发者使用的同一套工具链,技能可迁移。
LWC 基础:组件结构、ES 模块与装饰器
每个 Lightning Web Component 由三个文件组成(相比 Visualforce 的 1 个页面文件 = 关注点分离):
- myComponent.html:模板(标记),用标准 HTML + LWC 基础组件(如
<lightning-card>、<lightning-input>),用{property}绑定属性,用on{event}={handler}处理事件。 - myComponent.js:类(逻辑),ES6 class 继承
LightningElement,用@track、@api、@wire装饰器。 - myComponent.css:样式(作用域隔离——只作用于本组件,绝不泄漏到其他组件),用标准 CSS。
LWC 使用现代 JavaScript 标准:
- ES 模块(import/export):每个 .js 文件是模块,显式 import 需要的内容、export 你的类,无全局命名空间污染,可 tree-shake。
- 装饰器(@ 语法):
@api(把属性暴露给父组件)、@track(让属性响应式,变化触发重渲染)、@wire(连接 Salesforce 数据)。
装饰器是 LWC 实现响应式(reactivity)的方式——属性一变,组件自动重渲染,UI 自动更新。
二、编码概念如何应用于 LWC
本单元通过对比 Visualforce 模式来学习 LWC 架构与编码概念:LWC 架构(Web Components 标准)、属性与 getter/setter、条件渲染、渲染列表、调用 JavaScript(事件处理)、标准组件、组合(父子组件关系)。每个概念都映射到一个你已熟悉的 Visualforce 模式。
LWC 架构与 VF 模式映射
Visualforce 是服务端模板语言:客户端请求页面,服务器渲染后作为 HTML 发送;UI 变化时客户端再请求新渲染的页面。LWC 则把重活放在客户端:客户端请求组件文件,处理生成标记,再渲染 UI;客户端能处理逻辑,很多 UI 变化无需回调服务器,只有需要新数据时才调用服务器(且只请求最少数据)。
LWC 构建在四个 Web 标准上:自定义元素(定义新 HTML 标签)、Shadow DOM(封装组件内部,CSS 与 DOM 与页面其余部分隔离,样式不外泄)、HTML 模板(<template> 定义标记)、ES 模块(import/export 依赖管理)。这些都是浏览器标准,不是 Salesforce 专有技术。
Visualforce 模式到 LWC 的映射:<apex:page controller> → export default class extends LightningElement;{!prop} 合并字段 → {prop} 模板绑定;action="{!method}" 服务端方法 → onclick={handleClick} 客户端 JS;rendered → connectedCallback/renderedCallback;<apex:repeat> → for:each;rendered="condition" → if:true|false。每个 Visualforce 模式都有对应的 LWC 等价物,只是实现方式不同。
用属性与 getter/setter 跟踪状态
在 LWC 中,组件状态存储在 JavaScript 类属性里。@track 让属性响应式——属性值一变,组件自动重渲染,无需手动 refresh、actionFunction 或 rerender。例如 @track counter = 0; 然后 this.counter++; 组件就自动更新。
@track = 内部状态(组件私有);@api = 公共 API(暴露给父组件,父组件可设置,类似 Visualforce 自定义组件的 <apex:attribute>)。父组件更新 @api 属性时子组件自动重渲染——这是单向数据流(父 → 子通过属性,子 → 父通过事件)的核心。
getter/setter 提供计算属性——从其他状态派生的值,类似 Visualforce 的 get 方法:
get fullName() { return this.firstName + ' ' + this.lastName; }
getter 自动响应式:当 getter 引用的任何属性变化时,框架重新求值。选择哪种属性类型:@track 用于独立状态(用户输入)、getter 用于派生值(全名、格式化日期、筛选列表)、@api 用于父组件传入的值(recordId、objectApiName)。
条件渲染与渲染列表
条件渲染:LWC 用模板指令而非 JavaScript DOM 操作——if:true={condition} 与 if:false={condition}(对应 Visualforce 的 rendered 属性)。复杂条件用命名 getter:模板里绑定 if:true={isPremiumAndActive},JavaScript getter 里放条件逻辑——模板声明「显示什么」,getter 决定「何时显示」,方法决定「如何判断」。
渲染列表:for:each={list} 指令替代 Visualforce 的 <apex:repeat>:
<template for:each={contacts} for:item="c"><p key={c.Id}>{c.Name}</p></template>
key 属性至关重要——必须是每项的唯一标识(用记录 Id),帮助 LWC 只更新变化项、而非整表重渲染。还有 for:index 提供从零开始的位置索引。嵌套迭代用嵌套 template,每层有自己的 for:item 变量名,用描述性命名(account、contact)避免混淆。
调用 JavaScript:事件处理与异步
这是 Visualforce 开发者最大的调整之一:Visualforce 里点按钮通常调用服务端 Apex 方法(action="{!save}"),LWC 里点按钮调用客户端 JavaScript 方法(onclick={handleSave})。JavaScript 方法决定是否需要调服务器——很多 UI 交互(显隐分节、计数器、客户端校验)根本不需要服务器往返。事件对象(event.target、event.detail)提供上下文。
LWC 用现代 JavaScript 异步模式替代同步 Apex 调用:
- Promise:
saveRecord({...}).then(result => ...).catch(error => ...) - async/await:
try { await saveRecord(...) } catch(error) {...}
错误处理是显式的——你在 JavaScript 里 catch 错误并决定如何展示(toast 通知、内联错误等),不再有自动错误页面。客户端优先,仅在需要时调服务器。
标准组件(基础 Lightning 组件)
LWC 提供丰富的基础 Lightning 组件库——预构建、生产质量的 UI 元素,替代 Visualforce 标签:<apex:inputField> → <lightning-input>;<apex:pageBlock> → <lightning-card>;<apex:dataTable> → <lightning-datatable>;<apex:pageMessages> → <lightning-toast>。
选择哪个组件取决于你需要多少控制:
lightning-record-form:显示/编辑记录的最快方式(给 recordId 和字段,自动处理加载/编辑/保存),对应<apex:detail>。lightning-record-view-form/lightning-record-edit-form:更灵活的记录展示/编辑布局。lightning-datatable:带排序、内联编辑、行操作的列表。
基础组件内建 SLDS 样式、可访问性与行为,且基于与你自定义组件相同的 Web Components 标准,一切无缝协作。规则:从满足需求的最简组件开始,需要更多控制时升级。
组合:父子组件与 slot
LWC 组件可以包含其他组件,这叫组合(composition)。父组件通过 @api 属性向下传数据,子组件通过 DOM 事件向上通信。子组件对父组件内部一无所知(松耦合),可以独立开发、测试、复用。
黄金法则:数据向下流,事件向上流。父 → @api 属性 → 子;子 → CustomEvent → 父。子组件用 this.dispatchEvent(new CustomEvent('itemselected', { detail: { id: this.itemId } })) 派发事件,父组件用 onitemselected={handleItemSelected} 监听。这是 React/Angular/Vue 同款的单向数据流,可预测、可调试、可扩展。
Slot 让父组件向子组件注入内容(类似 Visualforce 的组件 body 或 facet):子组件定义 <slot></slot>(默认 slot)或 <slot name="header">(命名 slot),父组件在子标签之间放入内容。适合包装组件(卡片、模态框、布局)——结构固定、内容随用随变。
三、在 JavaScript 中处理用户操作
本单元动手实践。你将创建一个 SFDX 项目、构建一个 Lightning web component、处理事件、响应式管理状态,并观察组件如何在客户端重渲染——这是与 Visualforce 服务端重渲染的根本区别。
响应用户交互(含生命周期)
Visualforce 中 UI 变化常需服务器往返(action + rerender),LWC 中 UI 变化发生在客户端:事件处理器在浏览器运行 JavaScript、更新组件状态、框架自动重渲染——不需要 rerender 属性、没有往返。只有在需要数据时才调服务器。
标准输入处理模式:onchange={handleSearchChange} 绑定到方法,方法里 this.searchTerm = event.target.value; 更新属性,组件自动重渲染。对内存数据,客户端筛选即时响应;对服务端数据,用 Apex 调用。
LWC 组件有生命周期钩子:constructor()(创建一次,初始化 @track 属性)、connectedCallback()(插入 DOM,订阅事件、拉取初始数据)、renderedCallback()(每次渲染后,访问渲染后的 DOM 元素,注意会频繁触发)、disconnectedCallback()(移除时清理事件监听、定时器)。这些钩子替代 Visualforce 的页面生命周期(action、oncomplete),让你精确控制代码何时运行。
四、处理 Salesforce 数据
作为 Visualforce 开发者,你习惯用标准控制器与 Apex 绑定。在 LWC 中数据访问不同:Lightning Data Service 声明式处理单记录操作(无需 Apex)、Apex 从 JavaScript 命令式调用处理复杂查询、View State 被消除(LWC 无状态)、错误处理从页面消息转移到 JavaScript catch 块。本单元学习每种用例的正确数据访问模式。
数据访问架构与 Lightning Data Service
Visualforce 数据访问用 StandardController(单记录 CRUD)、StandardSetController(列表)、自定义 Apex 控制器,页面状态保存在View State(加密隐藏表单字段)。LWC 数据访问用 Lightning Data Service(LDS)(声明式单记录访问,取代 StandardController)、@wire Apex(响应式绑定)、命令式 Apex(按需调用),且没有 View State——组件状态在 JavaScript 内存里。
选择哪种数据访问方式:
- 用 LDS:按 Id 加载单条记录、在页面显示记录字段、用标准 UI 编辑记录(取代 StandardController)。LDS 客户端缓存、响应式(recordId 变自动重新拉取)、共享缓存(多组件请求同一记录只拉一次)、自动遵循 CRUD 与字段级安全。
- 用 @wire Apex:查询多条记录、数据要响应式(参数变自动刷新)、想要自动缓存。
- 用命令式 Apex:按钮点击/表单提交时调用、执行 DML 操作、复杂业务逻辑、需手动处理响应。
模式:LDS 用于「给我看这条记录」,Apex 用于「给我看所有符合条件的数据」。从 LDS 开始,需要更强能力时退回 Apex。
Apex 在 LWC 中:注解与无状态
LWC 里的 Apex 与 Visualforce 里的 Apex 根本不同:
- Visualforce Apex:方法通过 extensions/standardController 属性绑定到页面、返回 PageReference、View State 自动维护、用 ApexPages.addMessage() 报错。
- LWC Apex:方法是无状态的(调用间无 View State)、作为 JavaScript 函数导入、用 @wire(响应式)或命令式调用、直接返回数据(无 PageReference)、以异常抛出错误并在 JavaScript 捕获。
关键变化:LWC 里的 Apex 方法是静态工具函数,而非有状态的页面控制器——每次调用独立,状态不跨调用延续。
LWC 可访问的 Apex 方法需要特定注解:@AuraEnabled(cacheable=true)(可被 @wire 调用、启用客户端缓存)与 static(必需,无状态架构)。读操作(SOQL)用 cacheable=true + @wire 自动响应;写操作(DML)省略 cacheable 用命令式调用。
消除 View State 本身就值得从 VF 迁移到 LWC:Visualforce 的 View State 有 135KB 限制(复杂页面会撞上并崩溃)、可能损坏、每次 postback 都携带导致加载慢、加密难调试。LWC 没有 View State——组件状态就是浏览器内存里的 JavaScript 属性,可用 DevTools 直接检查,无大小限制。无状态设计其实更简单:每个方法自包含、无隐藏依赖、易测试、易复用。
调用 Apex:wire 与 imperative
Visualforce 通过控制器属性自动绑定 Apex 方法;LWC 则把 Apex 方法导入为 JavaScript 函数再显式调用:
import searchAccounts from '@salesforce/apex/AccountController.search';
导入后有两种调用方式:
- @wire(响应式):
@wire(searchAccounts, { term: '$searchTerm' }) accounts;—— 参数变化时自动重新调用,适合「显示响应用户选择的数据」。 - 命令式(按需):
searchAccounts({ term: this.searchTerm }).then(result => ...).catch(error => ...)—— 精确控制何时调用,适合按钮点击、表单提交、DML。
用 @wire 处理「显示随用户输入变化的数据」,用命令式处理「因为用户点了按钮所以现在就做」。
记录 Id 与服务器错误处理
获取当前记录 Id:Visualforce 里 {!Account.Id} 直接可用(StandardController 从 URL 读);LWC 里在组件的 meta XML 声明 lightning__RecordPage 为 target,再声明 @api recordId;(框架自动填充),然后用 recordId 配合 LDS/@wire/命令式 Apex。
服务器错误处理:Visualforce 用 ApexPages.addMessage() + <apex:pageMessages> 半自动处理;LWC 里 Apex 错误作为 JavaScript 异常在 .catch() 块中捕获。错误对象含 error.body.message(用户友好文本)、error.body.stackTrace(开发调试)、error.statusCode(HTTP 状态)。你决定如何展示(toast 通知、内联错误、错误面板)以及之后做什么(重试、回滚、重定向)。更可控,也更需要显式设计每个服务器调用的错误 UX。
组件缓存与检索筛选记录
禁用组件缓存:LWC 默认缓存组件定义(编译后的 HTML 模板、JS 类代码、CSS)以提升页面加载性能。开发时这个缓存会导致看不到最新改动——你改组件、部署、刷新,看到的却是旧版本(这是新 LWC 开发者最头疼的问题)。开发时可在 Session Settings 取消勾选「Enable secure and persistent browser caching」,或用 Chrome DevTools 的 Disable cache + 硬刷新。生产环境务必保持缓存开启。
用 Apex 检索筛选记录(常见模式):Apex 方法标记 @AuraEnabled(cacheable=true),LWC 里导入并用 @wire 绑定。参数前的 $ 前缀(如 { numberOfEmployees: '$numberOfEmployees' })告诉框架监视该属性——属性一变,wire 适配器检测到变化、用新参数重新执行 Apex、更新数据、组件重渲染,全自动。这消除了 Visualforce 里 apex:actionFunction + rerender 的模式。需要显式控制时(按钮点击),改用命令式 Apex。
五、使用导航服务并复用 Visualforce
最后一个单元:学习如何用导航服务在 LWC 中程序化导航(替代 Visualforce 的 URLFOR 与 PageReference 模式)、如何在 Lightning Experience 和 LWC 组件内嵌入现有 Visualforce 页面、以及如何战略性地决定什么该在 LWC 中重建、什么该保留在 Visualforce。
URLFOR vs 导航服务
Visualforce 常用 URLFOR 配合 <apex:commandButton> 导航(硬编码 URL 模式,Salesforce 改 URL 时代码就坏),或 Apex 方法返回 PageReference。LWC 中程序化导航的首选是 Lightning Navigation Service:导入 lightning/navigation 的 NavigationMixin、让组件类继承 NavigationMixin、调用 this[NavigationMixin.Navigate]() 传入 PageReference 对象(一个带 type 与 attributes 的 JSON 结构)。
导航服务用有意义的 PageReference 对象消除硬编码 URL——Salesforce 负责构造实际 URL,URL 变了代码也不坏。常见目标类型:记录页(standard__recordPage + recordId/objectApiName/actionName)、命名页(standard__namedPage,如 home)、列表视图(standard__objectPage + list)、外部 URL(standard__webPage)、Lightning 组件(standard__component)。这是 LWC 相比 Visualforce 最干净的改进之一。
在 Lightning Experience 中复用 Visualforce
你不必一次性重建所有东西。现有 Visualforce 页面可以在 Lightning Experience 中使用:作为选项卡(iframe 内渲染)、通过 App Builder 里的 Visualforce 组件嵌入 Lightning 页面、或在 LWC 中用 iframe 嵌入。限制:嵌入的 VF 运行在 iframe 里(与 Lightning 容器隔离)、不能直接用 Lightning Message Service 通信、样式上下文不同、不能与 LWC 原生共享状态。
跨 VF 与 LWC 通信的变通办法:VF → LWC 用 URL 参数或存到记录再经 LDS 读;LWC → VF 用 iframe src 的 URL 参数或 window.postMessage()(跨源 iframe 通信的标准浏览器 API)。平台支持混合页面——同时含 LWC 与 Visualforce 组件的 Lightning 页面。
渐进迁移策略:把现有 VF 包装进 Lightning 页面 → 识别高价值 VF 部分 → 重建为 LWC 组件 → 逐个替换 → 最终纯 LWC 页面。增量迁移、低风险、用户持续看到改进。
重建 vs 复用:迁移策略
决定何时在 LWC 中重建、何时保留 Visualforce,是经济决策而非技术决策:
- 在 LWC 中重建:页面用户流量高(投入有回报)、用户抱怨性能/UX、需要移动端支持、需要无重载的响应式更新、反正要加新功能。
- 保留 Visualforce:页面很少用或偏管理用途、工作良好且用户满意、复杂到重建需数月、计划废弃、用了无 LWC 等价物的高级 apex: 组件。
不是所有东西都要迁移——把 LWC 投入聚焦在能带来最大用户价值的地方。
你的 Visualforce 知识在 LWC 中仍然宝贵:Apex 控制器 → @AuraEnabled static 方法;StandardController → LDS;自定义组件 → LWC 组合;apex:repeat → for:each;rendered → if:true|false;URLFOR → Navigation Service;PageReference → NavigationMixin;View State → JavaScript @track 属性。真正需要新学的是:JavaScript(ES6+、模块、Promise)、Web Components 标准、VS Code + Salesforce CLI 工具链、客户端状态管理、事件驱动架构。你的 Apex 技能 100% 可迁移、标记技能适配 LWC 模板、控制器模式变成 JavaScript。从 Quick Start: LWC badge 开始,构建你的第一个组件——你已经掌握 Salesforce,现在在学习它的现代 UI 框架。
文章来源:Trailhead - Lightning Web Components for Visualforce Developers

























































