JavaScript
好,同学们,我们这一章要深入了解 Lightning Web Components 里的 JavaScript 全面参考。可以把它当作你编写 LWC 组件的“语法说明书”。因为每一个 LWC 组件,都必须有一个以 ES6 模块形式写出的 JavaScript 文件,所以掌握扎实的 JavaScript 基础,是第一步。 我们先从最基本的现代 JavaScript 功能聊起。包括数组的各种方法、简洁的箭头函数、类、模块、对象,以及处理异步操作的 Promise 和 async/await。这些你可能在标准 JavaScript 里学过,但在 LWC 里它们用得尤其多,比如用数组的 map 去渲染列表,用 async/await 去等待服务端数据。一定要把这些基础打牢。 有了这些基础,我们再来看组件之间怎么优雅地共享代码。在 LWC 里,我们完全依赖 ES6 模块的导出和导入机制——也就是默认导出和命名导出。你有两种常见的共享模式:一种是在同一个文件夹下,多个组件直接引用一个公用的 JS 文件;另一种是把代码封装成所谓的“API 模块组件”,让其它组件通过标准的模块导入来使用。这样做很清晰,也更容易复用。在这个过程中,编译器会自动解析入口点,同时也支持补充文件的导出。但要特别小心循环导入的问题,就是 A 引用 B,B 反过来又引用 A,这会导致代码运行混乱,我们一定要避免。 接着,我们经常需要引入第三方库,比如图表库 D3。在 LWC 里,你不能像传统前端那样随意插一个 script 标签。正确做法是:先把第三方库上传成静态资源,然后使用平台提供的 platformResourceLoader 去加载它。如果你需要对某个元素进行精细的操控,比如让 D3 去操作 SVG,就要使用 lwc:dom="manual" 这个指令,保护那些元素不被 LWC 自动重绘。当然,用第三方库时还要注意 NPS 和 LWS 的合规性,确保你的代码符合 Salesforce 的安全和性能要求。 然后,我们再来探索怎么调用 API 获取数据。最常用的就是 Fetch API。通过它,你可以非常方便地调用 Salesforce 自己的数据接口,比如走 Apex 暴露的 REST 接口,或者调用外部的第三方 API。异步请求可是开发数据驱动组件的核心。 再往下,我们学习一个很酷的功能——动态组件实例化。有时候你不想写死一个组件,而是想根据运行时的数据,决定显示哪个组件。这时候,我们就可以使用 lwc:component 和 lwc:is 这两个特殊属性,在模板里动态地创建和渲染不同的组件。这让你的界面变得极其灵活。 最后,我们还会简单了解一下开发人员预览中的 TypeScript 类型定义。虽然目前还是预览阶段,但对于大型项目来说,类型定义能帮我们提前发现很多错误,让开发体验更好,值得大家关注。 好了,这些就是本章要覆盖的核心内容。从基础语法到代码共享,再到第三方库、API 调用、动态组件,甚至未来的 TypeScript 支持。一步步掌握它们,你就拥有了构建复杂 LWC 组件的完整知识体系。我们接下来逐一深入。
本课程共有 37 个章节
好,我们来看这一页,它讲的可是LWC开发里必须掌握的几个JavaScript核心特性。你在写组件时,每个组件都会有个JavaScript文件,这个文件就是一个ES6模块。那为了写得更高效,你得熟悉下面这些关键的语法和功能。 首先是数组方法,像map和filter,它们是你处理数据的左膀右臂。你想把一组数据转成另一组,或者筛选出符合条件的记录,都用得到。它们写起来很简洁,逻辑也很清晰。 然后是箭头函数,它不光写法短,更重要的是它的作用域,它会自动绑定定义时的上下文。所以在模板里写事件回调,或者在代码里传callback函数,用箭头函数就特别方便,不会搞丢当前的this。这一点你在LWC里写组件逻辑时会经常用到。 接下来说类,ES6的class语法是LWC的基础。你的每一个组件类都必须继承自LightningElement,这是一个固定规则。所以你得知道怎么定义类、怎么写构造函数、怎么加属性和方法,这是搭建组件骨架的前提。 代码组织离不开模块化,ES6的import和export让我们能很方便地在不同文件间共享函数、对象或整个组件。你需要什么就import,想暴露什么就export,这样你的工程才能清晰、可维护。 对于对象本身的操作,像Object.keys和Object.values这些方法很实用,它们能帮你快速拿到对象的属性名或者值的列表,方便你遍历和转换数据结构。 处理异步操作时,你一定会遇到Promise。但现在在实际开发中,我们更推荐async/await语法,它让异步代码读起来像同步代码一样从上到下执行,可读性好得多。再结合try/catch,错误处理也非常直观,你就不用写一堆.then和.catch了。 最后还有变量声明,这个要形成习惯:对于不会重新赋值的变量,用const声明;值可能会变的,用let。把var彻底忘了,它会产生作用域提升之类的问题,在LWC里绝对不要用。 把这些基本功打扎实,LWC的开发效率就能上一个台阶。好了,我们继续往下看。
好,同学们,我们继续来看今天的知识点——在Lightning Web Components里面,组件之间怎么共享JavaScript代码。 其实啊,我们用的是标准的ES 6模块机制,LWC完全支持它。那具体怎么共享呢,这里有两种模式,都很常用。 第一种,我们叫它“同一文件夹模式”。这种方法特别适合把一个组件内部的逻辑拆分开,比如你有一个比较复杂的组件,里面写了很多工具函数,直接全塞在一个JS文件里太乱。这时候,你可以在组件文件夹里再放一个单独的JavaScript文件,比如叫“utils.js”,然后在主组件里用相对路径,像“'./utils'”这样把它导入进来。但是要注意,这种导入方式只能让同一个文件夹里的组件用,别的组件是找不到这个文件的。换句话说,它是用来做内部代码组织的,不是为了把功能暴露给整个项目到处用的。 第二种模式呢,就是专门为跨组件共享设计的,我们叫“API模块组件模式”。你可以创建一个独立的库文件夹,注意,这个文件夹里面没有HTML文件,它只包含JavaScript。这个库文件夹的主文件的名字要和文件夹名一样,比如你建了一个叫“myUtils”的文件夹,那里面就有一个“myUtils.js”作为主入口。其他组件想用这个库的功能时,直接用“c/myUtils”这种熟悉的组件引用语法就能导入,非常方便。 但要留心一个限制:其他组件只能从这个库的主JavaScript文件导入东西。如果你在库文件夹里还放了一些补充文件,比如“helpers.js”,在主文件外面定义的导出,外面是看不见的。那怎么把这些补充文件的函数也暴露出去呢?很简单,你需要在主文件里用“export ... from ...”语法,把它们重新导出一遍,这样外面才能用。 最后,有个硬性规定别忘了,每个JavaScript文件的大小不能超过128 KB,不管你是库文件还是组件文件,都得遵守。好了,这两种模式你清楚了吗?我们接下来可以看个例子。
好,同学们,咱们今天来看看Lightning Web Components里面一个特别有用的模式,叫做API模块组件。它的作用是什么呢?就是让你能够在多个组件之间共享代码,避免重复写同样的逻辑。 你把它想象成一个工具箱,里面放了一些常用的函数,哪个组件需要,就从这个工具箱里拿。 那怎么创建这个工具箱呢?有几个关键的要求,一定要记住。首先,文件夹和它里面的主JavaScript文件,名字必须一模一样。比如我们这个例子,文件夹叫mortgageUtils,那主文件就一定是mortgageUtils.js,大小写都要对上。 因为这个组件只是提供代码,不需要显示任何界面,所以它没有HTML文件。但是,平台要识别它是一个组件,还是需要一个配置文件,就是那个.js-meta.xml文件。这个文件必须有,即使里面内容很简单。 接下来是怎么用它。当你在其他组件里想要导入这个工具箱的时候,用的语法是 import 某某 from 'c/mortgageUtils'。注意了,这里有个容易错的地方:c后面跟的只是文件夹的名字,不用加.js或者任何扩展名。如果你写成c/mortgageUtils.js,或者c/mortgageUtils/utils,那就会报错了。所以,记住,只写文件夹名。 还有一个重要的点,别的组件只能访问你从主JavaScript文件里明确导出的那些东西。如果你在主文件里引用了一些辅助文件里的函数,但是没在主文件里再导出它们,那其他组件是看不到的。所以,要想公开那些辅助文件里的代码,你必须先在主文件里把它们重新导出一次。 总结一下,API模块组件就是不带界面的代码库,命名要一致,导入时只写文件夹名,想分享什么就从主文件导出什么。这样就能轻松实现代码复用了。 好了,这一页的重点就这些,大家理解了吗?
同学们,今天我们来聊聊模块导出中最简单的一种方式:,默认导出,。你可以把它想象成一个模块的“主菜”。一个模块只能有一个默认导出,也就是说,你只能从一个文件里主打输出一样东西。 语法很简单,有两种写法。第一种,直接用 `export default function() { ... }`,就把这个函数设为默认导出。第二种,你可以先定义一个函数,然后在文件末尾写 `export { myProcess as default }`,把已经声明的东西标记成默认。两种方式效果一样。 那导入的时候有什么好处呢?最大的便利就是——你可以随便起名字。比如你从 `utils.js` 导入默认导出的函数,可以写成 `import myCoolFunction from './utils'`,用 `myCoolFunction` 这个名字去调用,完全不用管它原本在源文件里叫什么。当然,为了代码好读,大家通常会让导入的名字和原函数名、或者文件名保持一致,但这只是一个习惯,不是强制要求。 那什么时候用默认导出呢?非常适合模块只有一个核心功能的时候。比如,你有一个专门做汇率转换的模块,或者一个存放应用程序配置的对象,这些情况用默认导出就很干净。模块使用者一看就知道:“哦,这个文件主要就输出这个东西”。 对比一下,另一种,命名导出,就不一样了。命名导出可以导出多个变量,导入时必须用花括号 `{ }`,而且名字必须和导出时一模一样。比如你导出 `export const taxRate = 0.1`,导入就得写 `import { taxRate } from ...`,名字对不上就会报错。所以,命名导出像是一盒子不同的小工具,你得准确叫出每个工具的名字。 在 Salesforce 的 LWC 开发里,这两种模式都会用到。如果你去看官方的 `lwc-recipes` 仓库,里面既有展示默认导出的例子,也有命名导出的例子,可以参考一下,加深理解。 总结一下:默认导出就是一个模块的“压箱底本事”,一个文件只有一个,导入名字自定,适合单一用途的模块。命名导出则是一组功能,导入要按名字精确匹配。掌握好这些,你写组件间的共享代码就会清晰很多。好啦,这块内容我们就说到这儿,有什么问题随时提出来。
同学们好,今天我们一起来看看Lightning Web Components里一个非常实用的概念——命名导出。这个名字听起来可能有点陌生,但其实很好理解。简单说,就是在一个模块里,你可以同时导出多个函数、变量或者类,每个导出的东西都有自己的名字。 想象一下,你有一个专门做抵押贷款计算的工具模块,里面有两个功能:一个叫getTermFields,它会返回一组期限选项,每个选项都有标签和对应的值;另一个叫calculateMonthlyPayment,专门处理财务计算,帮我们算出每个月的还款额。你在文件最后可以这样写:export { getTermFields, calculateMonthlyPayment }。这一写,就相当于给这两个函数都贴上了标签,让别的模块能直接按名字来取用。 那别的组件要怎么用呢?很简单,导入的时候要用一对花括号,里面写上你需要的那个函数的确切名字,这和JavaScript里的解构很像。比如:import { getTermFields, calculateMonthlyPayment } from 'c/mortgageApi'。这样做的好处是,你完全可以只导入自己用得上的那部分,不需要把模块里所有的东西都搬过来,非常灵活。 万一你当前的组件里已经有一个同名的变量或者函数了,也不用担心。你可以用as关键字给它起个小名儿,比如写成import { calculateMonthlyPayment as calcPayment },这样就能避免冲突,读起来也更顺。 另外提醒大家一点,像这种纯粹提供计算逻辑的模块,它其实不需要HTML界面,所以就没有对应的HTML文件。在Lightning Web Components里,服务类型的模块常常就是这么干的,它就是一个干干净净的代码库。 好了,简单总结一下:命名导出让我们能从一个模块里轻松分享多个功能,导入时按名取用,还能重命名。希望这个讲解帮你理清了概念,我们下一节继续。
今天我们来聊一聊 LWC 编译器是怎么找到模块入口文件的。这其实有个很明确的规则,你只要记住一点:,导入的时候,永远只写模块文件夹的名字,千万别写里面的具体文件名,。 比如你写 `import { X } from 'c/moduleName'`,编译器一看到这个,就会先去文件夹里找 `moduleName.js`。如果找到了,好,它就是入口文件。如果找不到 `.js` 文件呢?它还会再试一次,看看有没有 `moduleName.css`,这个主要是给那种只有样式的模块准备的。要是连 `.css` 都没有,那就直接报错,编译失败。 所以,重点就在于,像 `c/utils/utils.js`、`c/utils/other.js` 或者只写到 `c/utils/utils` 这种写法,全都是无效的,编译器不认。正确的做法就是简单写 `c/utils`,然后编译器会自动解析到 `utils.js` 作为入口。 这种设计其实是为了强制封装。模块里面的文件结构,那是它自己的事,外面使用的人根本不需要关心。只要通过这个模块的公共接口,也就是入口点,来用它暴露出来的东西就好了。这样每个模块的内部实现可以随便改,只要入口不动,就不影响别的代码。 用一句话总结就是:,导入模块时,用文件夹名,别碰文件名,编译器会帮你找 `.js`,找不到就 `.css`,再没有就罢工。,
现在我们来聊聊如何在Lightning Web Components里更好地组织你的JavaScript代码,特别是当你想把一些工具函数放在单独的辅助文件里,但又希望组件能直接从主模块调用它们。 你可能会遇到这种情况:你有一个工具库,比如 `utils.js`,里面放了很多常用函数。你不想让组件直接导入这个辅助文件,而是希望通过一个主文件,比如 `main.js`,来统一管理对外暴露的内容。这样做的好处是,你的组件只需要面对一个入口,内部结构怎么变,只要入口不变,组件都不用动。 关键技巧就是使用“重新导出”语法。假设你在 `utils.js` 里定义了一个函数 `calculateDiscount`,你想让它通过 `main.js` 对外提供。你可以在 `main.js` 里写: `export { calculateDiscount } from './utils';` 这一行代码同时做了两件事:它从 `./utils` 导入 `calculateDiscount`,又立即把它导出。这叫做重新导出,非常简洁。 如果你想一次性把 `utils.js` 里所有的导出都重新暴露出去,可以用通配符语法: `export * from './utils';` 这样,外部使用者就可以直接从 `main.js` 导入 `calculateDiscount`,就像这个函数是定义在 `main.js` 里一样。 但是,有一个很重要的点要记住:重新导出语法只是对外部消费者有效,在 `main.js` 文件内部,你是不能直接使用 `calculateDiscount` 的。如果你想在 `main.js` 内部也用到这个函数,你必须另外再写一条普通的 `import` 语句,比如: `import { calculateDiscount } from './utils';` 这样,`main.js` 既能自己用,又能把这个函数转发给组件。 整体来说,这个模式能帮你把代码拆分成清晰的模块,同时保持组件导入路径的整洁。你只需要知道从主文件导入,不用关心底层辅助文件的具体位置。 我们做一个小结:如果你想把辅助文件里的代码通过主文件对外暴露,就用 `export { ... } from './file'` 或者 `export * from './file'` 进行重新导出,然后组件从主文件导入。如果主文件自己也要用,记得单独加上一条内部的 `import`。 这个知识点在实际开发中很常用,能帮你构建出维护性更好的代码结构。
各位同学,今天我们讲一个在Lightning Web Components里千万要小心的问题——循环导入。 想象一下,两个人互相等着对方先伸出手才能握手,结果谁也动不了。代码里也一样:模块A从模块B导入东西,而模块B又从模块A导入东西,这就形成了一个“依赖圈”。这种圈可以是直接的,比如Alpha导Beta、Beta导Alpha,也可以是绕了一大圈又回到自己。 在LWC里,这样的循环是明确不支持的。因为模块加载的时候,必须先把依赖都准备好,才能执行自己。可一旦出现循环,就会导致某个模块还没来得及完成初始化,就先被引用了,导入的值就变成了undefined。接着你的代码就会莫名其妙地崩溃。 症状往往很隐蔽,不一定是大摇大摆报错。你可能看到“TypeError: xxx is not a function”,或者“Cannot read properties of undefined”,明明单独测每个组件都好好的,把它们一结合就炸了。更坑的是,某天平台升级,模块的评估顺序稍微一变,之前能跑的代码突然就报错,让你摸不着头脑。 Slide上给了一个很典型的直接循环例子:一个叫Alpha的模块从Beta里导入东西,Beta又从Alpha里导入东西。运行时直接失败,没有任何侥幸。 所以,怎么避免?很简单:永远不要让两个模块互相导入。如果它们需要共享逻辑,就把那部分抽出来放到第三个公共模块里,比如叫Utils,让Alpha和Beta都去导入它,而不是彼此纠缠。平时画一下依赖图,或者用一些lint规则来检查,养成好习惯,就能远离这种头痛的问题。 记住,LWC里循环导入是硬伤,一定要绕开。好,这一节先到这里,大家动手写代码时留个心。
好,我们来看这一页的内容,讲的是循环导入的识别和解决办法。 要识别循环导入,第一步就是跟踪导入链。具体怎么做呢?从那个报错的模块开始,顺着它里面的每一条导入语句,去看看那些被导入的模块,它们自己又导入了哪些东西。就这么一层一层追下去,直到你绕了一圈,又回到了一开始的模块,这就确认了确实存在循环。反过来,如果所有可能的路径都走到底,也没有再碰到最初的模块,那说明没有循环。 一旦确定了是循环导入的问题,我们通常有四种解决的策略。 第一种,也是最常见、最干净的做法,就是把共享的那部分代码提取到一个独立的第三个模块里。然后,原来互相导入的两个模块,都改成从这个新的公共库里面导入,这样循环就自然断开了。 第二种,如果发现两个模块耦合得特别紧,那干脆把它们合并成一个模块。这样一来,有问题的导入边界直接被消除了,循环也就不存在了。 第三种,我们可以反转依赖关系。意思就是,不再用 import 直接把东西拿进来,而是把它作为函数的参数传进去,或者通过组件的公共属性来传递。由调用方来提供这些值,导入的依赖就解开了。 第四种,
好,今天咱们聊聊在Lightning Web Components里怎么用第三方的JavaScript库。 首先啊,在做之前,一定要先看看Salesforce的AppExchange上是不是已经有现成的解决方案了,或者咱们Lightning基础组件本身就能实现这个功能。这样一来,能省下不少开发时间。 假如确实要引入外部库,得记住一个关键原则:库文件必须作为静态资源上传到Salesforce,绝对不允许从CDN或者外部的URL直接加载。这是为了安全,得遵守Salesforce的内容安全策略。 在你的组件代码里,要怎么导入呢?我们要用@salesforce/resourceUrl这个作用域模块来引用静态资源,同时,借助lightning/platformResourcePlayer提供的方法,比如loadScript来加载脚本,loadStyle来加载样式。它们都是平台给我们准备的加载工具。 加载的过程是异步的——你调用loadScript或者loadStyle的时候,它会返回一个Promise。所以,我们在.then()回调里安全地去使用这个库。记住,别直接在加载语句下面就调用库的方法,得等它加载完。 关于兼容性,这跟你组织的安全架构有关。如果你的组织用的是Lightning Locker(就是老的Lightning Security),那对库的要求比较严格,很多库得按照规范改一改才能用。但如果你的组织启用了Lightning Web Security(新的安全模型),那多半现代的JavaScript库都能直接拿来用,基本不用修改,只有少数可能需要微调一下。 所以总结起来,就是先考虑替代方案,再上传静态资源,用平台方法安全加载,最后注意你的安全模式。这就保证了咱们既能用上丰富的第三方功能,又不会违反Salesforce的安全策略。
同学们,今天我们来看一个在Lightning Web Components里比较特殊,但也挺实用的指令——`lwc:dom="manual"`。 大家知道,LWC的引擎会自动帮我们管理DOM,还有样式封装,这样我们写的组件样式就不会影响到外面,外面的也不会跑进来。但是在某些情况下,比如你要集成一个第三方的图表库,像D3.js这种,它需要自己直接操作DOM来画图。这时候如果你直接用`appendChild`这类方法手动往组件里加元素,你就会发现样式不对劲,因为LWC的样式封装会阻止这些手动添加的元素拿到组件内的CSS。 那怎么办呢?就用`lwc:dom="manual"`这个指令。它的用法很简单,你在模板里放一个普通的HTML元素,比如一个`<div>`,然后给它加上`lwc:dom="manual"`。这样一来,这个元素就变成了一个“手动操作容器”。引擎知道这里面要搞手动操作了,就不会把样式封装作用在这个容器内部,于是你后面通过JavaScript手动塞进去的元素,就能正常应用组件的样式了。 不过要记住,这个指令只是在这个容器内部“解除”了样式封装的限制,组件其他部分还是受到保护的。也就是说,把需要第三方库操作的部分圈定在一个小范围内,既能让第三方库正常工作,又尽量不破坏整个组件的封装性。 但是这里有两个重要的提醒:第一,`lwc:dom="manual"`应该只在必要的时候用。大多数情况下,你完全不需要它,让LWC引擎自己管理DOM是最好的,既简单又安全。第二,Salesforce不会对任何第三方库提供官方支持,如果你选择使用,就要自己负责测试,确保在平台升级时这些库还能正常工作。 所以记住:当你需要手动操作DOM,而且发现样式丢失时,就想一想,用一个带`lwc:dom="manual"`的容器元素,就能解决问题。好了,这个概念很简单,但很实用,特别是在做可视化、集成某些复杂库的时候。大家明白了吗?
同学,我们来看这一页Slide,它展示了一个特别重要的概念:在Lightning Web Components里面,怎么集成第三方的可视化库,比如D3。 你想想,Salesforce的LWC框架对DOM操作管理很严格,一般不允许我们直接去动组件内部的元素。但是像D3这样的库,它需要直接操作DOM来绘制图表。那怎么办呢?这个示例就给了我们一个完整的解决方案。 首先,模板里放了一个空的SVG元素,作为D3力导向图的画布。关键点来了——在这个SVG元素上,你看到那个`lwc:dom="manual"`指令了吗?这行代码特别关键。它相当于告诉LWC引擎:“嘿,这个元素的DOM内容,要交给D3来手动填充,你别插手。而且,CSS样式封装依然要保留,这样样式不会泄露出去。” 这就是让LWC和D3和平共处的秘诀。 接下来,SVG元素的宽和高,绑定到了两个响应式属性上:`svgWidth`和`svgHeight`。这样一来,当这些属性值改变时,SVG尺寸就会动态调整,图也能随之重绘,非常灵活。 还有那个`slds-m-around_medium`类,这是Salesforce的设计系统提供的标准间距类,让图表周围有合适的留白,保持界面美观。 那么在开发之前,你需要先做好准备工作:下载D3库的文件,把它压缩成zip包,然后作为静态资源上传到你的Salesforce组织里,名字就叫“d3”。这样组件里才能用`import d3 from '@salesforce/resourceUrl/d3'`这种方式引用它。 最后,如果你想要参考一个完整的实现,可以去看官方提供的lwc-recipes仓库里的`libsD3`组件,它是一个很好的起点。 好,这一页的核心就是:用`lwc:dom="manual"`给第三方库开个后门,让它安全地操作DOM,同时保持LWC的封装特性。这个技术不只适用于D3,其他任何需要手动操作DOM的库都能用这个套路。你理解了吗?
同学们,我们来看一下这个JavaScript文件里几个非常重要的编程模式,这些是你在做复杂组件时经常会用到的。 首先,文件顶部有一行注释 /* global d3 */。这其实是告诉我们的代码检查工具 ESLint,d3 这个变量是全局存在的,你不用再额外声明它,这样就不会报错了。 接着,我们用 @salesforce/resourceUrl/d3 这种方式导入 D3 这个静态资源。这里要注意一个命名的小细节:导入时绑定用的名字是大写的 "D3",而实际在 Salesforce 里静态资源的名字是小写的 "d3"。一会儿我们代码里用的就是那个大写绑定的变量来加载库。 然后,我们动用了 renderedCallback 这个生命周期钩子。它的作用很简单,就是保证组件对应的 DOM 元素已经完全渲染好了,这时候 D3 再去操作这些 DOM,才不会出错。 为了防止重复初始化,我们设置了一个叫 d3Initialized 的标志位。如果没有这个标志,每当组件因为数据变化重新渲染时,D3 都会重新创建一次图表,这显然不是我们想要的。有了标志,只在第一次渲染时执行初始化,后续就直接跳过了。 在加载资源方面,我们用 Promise.all 同时加载 D3 的脚本文件和样式表,这样就能并行请求,提升整体性能。 最后,我们用 try/catch 把加载过程包起来。万一库加载失败了,我们就用 ShowToastEvent 弹出一个用户友好的错误提示,而不是让界面莫名其妙地卡住。 这些模式组合起来,就能让我们的第三方库集成变得既健壮又高效,大家下去可以自己试试看。
今天我们来聊聊怎么在 Lightning Web Components 里用 D3 做一个漂亮的力导向图。 首先,核心方法是 `initializeD3`,它用了 D3 的模拟、链接和节点这套经典模式。 但要注意,在 LWC 里,我们不能像平时写普通网页那样直接用 `document.querySelector` 去拿 DOM 元素,必须改成 `this.template.querySelector('svg.d3')`。为什么?因为 LWC 强制要求 DOM 访问必须封闭在组件自己的影子 DOM 里,用 `this.template` 前缀就相当于告诉你:“嘿,只能在自己组件这片小天地里找元素,别去外面乱翻。”这样能保证组件的封装性,不会污染别人,也别被别人影响。 数据是从本地的一个 `/data` 模块导入的,里面有节点和链接的信息。D3 的力模拟器会启动,加上三种力:链接力、电荷力和向心力,让整个图自然散开,节点像有弹性一样被链接拉着,同时又互相排斥。 链接画出来是线条,线条的粗细不是随便定的,而是和链接的值(value)的平方根成正比,这样视觉上更直观。节点呢,是彩色的圆圈,按组(group)着色,而且还能拖拽,鼠标按住就能移动,很灵活。 当你把鼠标悬停在某个节点上时,会自动弹出一个工具提示,显示这个节点的 ID,让你知道它是谁。 最后别忘了 `tick` 函数,它会在每一帧模拟步骤里不断更新所有节点和链接的位置,这样动画就动起来了。 这样,一个动态、可交互的力导向图就在 LWC 里立起来了。关键在于记住用 `this.template.querySelector` 去锁定你的 SVG 容器,剩下的事情就和常规 D3 开发差不多啦。
嘿,同学们,今天咱们来聊聊在Lightning Web Components里怎么加载静态资源。你可能觉得“加载个JS文件,有什么好聊的?”其实啊,这里面有几个常见的模式,掌握好它们,能让你的代码跑得更顺、更快,还不会出乱子。 咱们来看第一种,也是最简单的——只加载一个JavaScript文件,不需要什么样式。这就好比你去便利店只买一瓶水,拿了就走。代码里直接调用`loadScript`,完事儿了。 第二种呢,就稍微聪明一点了。如果你有多个JS文件要加载,比如两个、三个,你会一个一个按顺序加载吗?那样就像排队买票,一个买完才轮到下一个,浪费时间。这时用`Promise.all`,它能同时发出请求,让多个文件并行下载,速度上能快不少。这就好比派几个朋友分别去不同窗口,谁先买好谁回来,不用一个个傻等。 第三种情况是最常见的——现实世界里,一个组件往往既需要一些JavaScript逻辑,又需要配套的样式。这时候你就要把脚本和样式表一起加载。模式就是这样,先加载脚本,再加载样式,或者两者都通过Promise包装好,保证它们都到位了再继续。 好,说完模式,咱们得记住几个,关键的最佳实践,,这些都是过来人踩过的坑,记住了能让你少走弯路: 第一,,错误处理,。加载资源万一失败了呢?网络抽风、文件名写错……你一定要在Promise链上加个`.catch()`,或者用`try/catch`把错误兜住。不然你的组件可能就默默崩溃了,用户一脸懵。 第二,,防止重复初始化,。`renderedCallback`这个生命周期钩子,可不是只在第一次渲染时调用哟,每次组件重新渲染它都会跑。所以如果你在里面做初始化操作,比如加载资源,就可能一遍又一遍加载。怎么办呢?用一个布尔标志,比如`isInitialized`,第一次加载完就标记为true,下次进来直接跳过。 第三,,DOM访问,。如果你要在组件加载后操作DOM,比如找某个元素绑定事件,请在`renderedCallback`里做,因为只有这时模板才真正渲染到页面上。还有,记住用`this.template.querySelector`,不要用`document.querySelector`,否则你会窜到其他组件里去,破坏封装性。 第四,,清理工作,。当组件从页面移除时,`disconnectedCallback`会触发。如果你在组件里绑定了全局事件、开了定时器,或者有什么外部引用,记得在这里清理掉,防止内存泄漏。就像出门要关灯断电一样,养成好习惯。 最后,一定要使用`lightning/platformResourceLoader`这个模块来加载静态资源。这是Salesforce官方唯一支持的方式,别自己偷偷用原生方法去动态创建`<script>`或`link`标签,那样不靠谱,可能在Locker Service沙箱里出问题,而且平台优化也享受不到。 好了,记住这三种模式,遵守这些最佳实践,你处理静态资源加载就会得心应手。咱们下节见!
同学们,咱们来看这一页 Slide。它讲的是,当你的 Salesforce 组织还没有升级到 Lightning Web Security 时,必须确保组件满足所谓的“收件箱合规性”。这个合规性主要有三点核心要求。 第一点,避免跨命名空间的 DOM 操作。说白了,就是每个组件只能动自己范围内的 DOM,千万不要伸手去碰别人命名空间里的东西。 第二点,代码要支持 JavaScript 的 ES5 严格模式。比如,不允许出现那种没声明就直接用的全局变量,写法必须规规矩矩的。 第三点,避免使用会被平台安全机制拦掉的 API。你可以去查一下 JavaScript API 查看器,上面有完整的允许列表,写代码前最好先对照一下。 那么怎么测试合规性呢?很简单,先搭一个最小化的示例应用,把基本功能跑通,然后再扔到收件箱控制台里实际去测一遍。 这里还要留意几种常见的违规情况。比如不小心创建了全局变量,这就坏了严格模式的规矩;用了 eval 或者 new Function,这类写法会直接触发内容安全策略被拦截;还有那种大范围扫描整个 DOM 的操作,应该改成有针对性的精准操作;另外,用了非标准或不支持的 DOM API 也不行。 最后,如果你发现依赖的某个第三方库不合规,那就得考虑修改它、分叉出一个自己的版本,或者干脆找个能替代的库。这样一来,你的组件就能安安稳稳地在平台里运行了。
同学们,我们来看这一页幻灯片,讲的是在使用 Lightning Web Components 的时候,引入第三方 JavaScript 库最容易遇到的四种违规类别。这些基本上涵盖了咱们在收件箱合规扫描里看到的大多数问题。一旦把第三方库放到组件里,安全扫描工具,就是那个 Inbox,就会帮你检查出来。别担心,每一种都有对应的修复方法,我们一个一个说。 第一种,叫“意外的全局值”。这是什么情况呢?就是你在代码里,把库赋给了一个没有用 let、const 或者 var 声明的变量。比如你写了个 `myLib = someLibrary()`,在 LWC 的严格模式下这就会被逮到,因为它会不小心污染全局作用域。修复其实很简单,你需要显式地把这个库挂到 window 对象上,加上命名空间,写成 `window.myLib = someLibrary()`,这样严格模式就知道你是故意的,就不会报错了。 第二种,是“内容安全策略违规”,简称 CSP 违规。Lightning 平台为了安全,禁止使用像 `eval()`、`new Function()` 或者动态创建 `<script>` 标签这种做法。如果你的第三方库里包含了这些,那直接就会触发违规。怎么修呢?要么联系库的作者把这类不安全写法去掉,要么你自己在本地把这些调用删掉,改用其他安全的方式。一定不能留着,必须替换掉。 第三种,叫“DOM 访问违规”。很多库喜欢直接去扫描整个 `document`,比如用 `document.querySelector` 找东西,但在 LWC 的沙箱里,组件只能操作自己模板里的 DOM,不能越界。如果它满文档乱找,就会报这个错。解决办法是,在你需要放库操作的那个容器元素上,加上指令 `lwc:dom="manual"`,这就相当于给了这个元素一个手动通行证,允许库在里面自由操作。但是要注意,你得确保这个库只在那个指定的小范围内活动。 那怎么知道你的库用了哪些不支持的 DOM API 呢?有一个好帮手—— Inbox API 查看器工具,你可以在扫描结果里点开它,它会清清楚楚列出所有违规的 API。对于每一个不支持的 API,你就找到 Salesforce 提供的受支持的替代方案,逐一替换掉就可以了。 最后,如果经过上面这些改造,库仍然没法兼容,咱们有三个选择。第一,礼貌地去问库的维护人员,看他们愿不愿意出一个兼容 Lightning 的版本。第二,如果这是个开源库,更好,你可以自己动手把修复贡献回去,利人利己。第三,如果都不行,那就只能分叉代码,自己维护一个定制的版本。这里要特别提醒大家:最终在生产环境中,这个库能不能安全运行,责任都在你身上。所以,每次更新库版本时,都一定要重新检查这些兼容性,这样应用才能又强大又安稳。好了,这四种常见违规和应对思路咱们就讲完了,有什么问题随时问我。
同学们,咱们今天来看看Lightning Web Security,也就是LWS,在兼容第三方库这块儿带来的巨大进步。 以前我们在Salesforce里用的是Locker服务,那个安全机制限制很多,动不动就让一些常用的JavaScript库跑不起来,调试起来非常头疼。而LWS换了一种思路,它使用的是一个JavaScript虚拟沙箱。这个虚拟沙箱更聪明,它模拟的是标准浏览器环境,而不是像Locker那样自己去限制一大堆东西。 所以,带来的直接好处就是:绝大多数现代的第三方库,你直接拿过来就能用,不需要做任何修改。因为LWS下面暴露出来的DOM API,基本上就跟你在普通浏览器里看到的一样,再也没有Locker加上的那些奇奇怪怪的限制了。 不过呢,有一个小坑需要注意一下。有些库会显式地在代码里写上“use strict”启用严格模式。LWS在处理严格模式指令时,用了自己的方式,这可能会导致一部分这样的库需要做一些调整才能正常工作。这种情况并不常见,但你得知道。 那如果真是点背,某个库在LWS下还是跑不起来,该怎么办呢?解决路径其实跟我们之前在Locker下遇到问题一样。要么直接去联系库的维护者,看看他们愿不愿意适配;要么你厉害的话,自己给那个库贡献一个修复补丁;再不然,干脆把这个库fork一份,自己维护一个内部版本。 最后给大家一个强烈建议:如果你们组织现在还在用老旧的Lightning JavaScript,也就是还跑着Locker服务的话,赶紧把迁移到LWS当成一个优先任务。它真的能让你跟第三方JavaScript打交道时,省掉无数麻烦,工作流程一下子就清爽多了。
同学们,今天咱们来聊一个很实际的话题:在Lightning Web组件里怎么安全地调用外部API。这个话题涉及一个非常重要的安全机制,叫做,内容安全政策,,英文是Content Security Policy,简称CSP。 CSP其实是一个W3C标准,它的作用就像是给你的组件竖起了一道围墙,专门控制页面可以加载哪些外部资源。在Salesforce的Lightning平台上,这个CSP默认是非常严格的:它会,阻止所有向外部发起的API调用,,同时也,阻止WebSocket连接,。也就是说,如果你直接在组件里写一个fetch请求到某个外部网址,十有八九会被浏览器直接拦截掉。 那如果你想调用某个特定的外部API,该怎么办呢?你就需要把这个API的基础URL(比如 https://api.example.com )添加到Salesforce的设置里,作为一个,受信任的URL,。这个操作会修改CSP的响应头,明确告诉浏览器:“嗨,这个域名是安全的,可以放行。”这样你的组件才能顺利地和那个外部服务通信。 好,明白了安全的背景,我们再来看看最佳实践。如果我们的目标是访问Salesforce自己的数据,,首要选择,应该是,Lightning Data Service,,简称LDS。为什么?因为LDS构建在用户界面API之上,性能非常好,而且,自动帮我们处理了身份验证、缓存、数据共享规则,这些麻烦事。你完全不需要去手动维护访问令牌,代码也干净很多。 但总有LDS覆盖不到的场景,比如需要执行复杂的业务逻辑、跨对象的操作,或者需要调用不是标准CRUD的接口。这个时候,我们就得自己写,Apex类,了。记住,要用 `@AuraEnabled` 注解来标记这些Apex方法,这样它们才能在Lightning Web组件里被直接调用。所有对Salesforce内部API的复杂访问,都应该通过Apex来做,保证安全。 接下来,如果要调用,第三方的外部API,,就是Salesforce以外的服务,流程稍微复杂些。第一步还是刚才说的:把第三方API的基础URL添加为受信任URL。第二步,在组件中使用现代的,Fetch API,来发起HTTP请求。这是标准的Web技术,用起来很方便。 但是,这里有个,极其重要的大坑,,大家一定要避开:,永远不要把认证密钥、API秘钥、Token之类的敏感信息直接写在客户端的JavaScript代码里,。因为前端代码是可以被任何人查看的,你要是把密钥放在JS文件里,就等于把它公开发布了。正确的做法是:把真正的HTTP调用和身份验证逻辑都放到,Apex,中去做。你可以用Apex的 `HttpRequest` 类来构建请求,在服务端安全地附加认证信息,然后把结果返回给组件。这样外界永远接触不到你的密钥。 总结一下这节课的核心: - CSP默认会拦截外部调用,要用受信任URL来放行。 - 访问Salesforce数据时,优先用LDS,不行就上Apex。 - 调用第三方API时,前端用Fetch API发请求,但认证和安全逻辑一定要放在Apex里,绝对不要把密钥暴露在客户端。 好了,大家把这几个原则记牢,以后做集成的时候就能既高效又安全了。下节课我们讲具体的代码实现。
同学们,我们今天来聊聊在 Lightning Web Components 里头怎么跟 Salesforce 数据打交道。你可能会问,这有什么讲究吗?当然有,首选工具就是 Lightning Data Service,我们简称它叫 LDS。这东西特别好用,因为它底层是搭在公共用户界面 API 上的,所以那些麻烦事,比如缓存怎么管、共享规则怎么遵守、字段级别的安全怎么控制,它都自动帮你处理好了。大部分时候,你只想对标准或自定义对象做些增删改查操作,那通过 @wire 或者命令式调用 LDS 就完全够用了。 不过,咱们也得知道它的边界。LDS 啊,它只支持 UI API 的一小部分功能。就像一把万能钥匙,能开很多锁,但不是所有门都能打开。所以,当你需要访问别的 Salesforce API,像元数据 API、工具 API,或者要执行一些复杂的多对象事务时,LDS 就有点力不从心了。这时候怎么办呢?别急,我们就得自己动手写 Apex 类了。 好,那怎么把 Apex 跟 LWC 连接起来呢?很简单,你在 Apex 方法上标一个 @AuraEnabled 注解,这就等于把它公开出来,让前端能找到。然后,在 JavaScript 里,你用 @salesforce/apex 这个范围模块把它导入进来。调用时,有两种路子:一种是通过 @Wire 反应式地调用,意思是数据一变,前端就自动刷新,很省心;另一种是直接命令式调用,就是你说调就调,控制更灵活。这取决于你的场景。 最后,你可能会想,既然用 LDS 这么方便,为啥还要绕道 Apex?因为 Apex 方法稳当啊,它跑在服务器端,安全性有保障,还能访问完整的 Salesforce API,并且支持事务操作,确保数据一致性。总之一句话,LDS 擅长日常简单操作,Apex 则是处理高难度任务的后盾。记住这个原则,你开发时就游刃有余了。
同学们,这节课我们来聊聊在 Lightning Web Components 里怎么调用外部接口,也就是用 Fetch API 发 HTTP 请求。别担心,它不是什么新框架,就是浏览器自带的标准功能,所以你在 LWC 里直接用就行,不需要额外安装任何东西。 不过呢,在 Salesforce 里用 Fetch API 有一个小前提——你得先配置好站点的 CSP 受信任 URL。注意,这里加的只是基础 URL,比如 `https://api.example.com`,不是后面带具体路径的完整端点。这一步一定要先做,不然你的呼叫会被浏览器拦住。 Fetch API 天然是基于 Promise 的,所以你可以很优雅地用 `async/await` 来写,让代码读起来更像同步流程,非常好维护。 但这里有一个非常容易踩的坑,大家一定要记住:Fetch 返回的 Promise 只有在网络层面的故障时才进入 `catch`,比如你断网了、请求超时了。如果是服务器返回了 404 或者 500 这样的错误状态码,`catch` 是捕获不到的,Promise 仍然是成功的!所以正确做法是拿到响应后,自己用 `response.ok` 或 `response.status` 去判断,手动处理业务错误。 另外,`finally` 块不管最终是成功还是失败都会执行,特别适合用来做一些清理工作,比如隐藏一个加载中的旋转动画,这样用户体验会更好。 还有一点关于安全的,特别重要:永远不要把 API 密钥、令牌这种东西写在你的客户端 JavaScript 代码里。因为打包部署后,这些信息会暴露出去,非常危险。在 Salesforce 里,最佳实践是通过一个 Apex 类,用 `HttpRequest` 去代理你的请求,这样凭据安全地保存在后端,前端只调用这个 Apex 方法。 如果你想看一个完整的例子,去搜一下 Salesforce 官方的 `lwc-recipes` 仓库,里面有一个 `miscRestApiCall` 组件,演示了怎么调用 Google Books API,跟着学一遍就会了。 好了,Fetch 在 LWC 里的要点就这么多,现在我们一起来看看代码示例吧。
好,我们来看一下动态组件实例化这个特性。它确实很强大,但用的时候也要注意性能,因为它对资源的使用比较敏感。 简单来说,动态组件允许你把组件的代码加载推迟到真正要用的时候,而不是一开始就全部加载。这样做的好处很明显:用户打开页面时,初始需要下载的代码包会变小,页面加载更快。 但代价是什么呢?每一个动态导入,如果不走缓存的话,都需要一次额外的网络请求,这会增加一些运行时的开销。所以,你需要权衡什么时候用动态加载,别到处用,否则反而会影响体验。 在使用这个特性之前,有两个设置必须做。第一个是必须要在你的组织里启用 Lightning Web Security,也叫 LWS。这是动态导入的硬性要求,没启用的话,动态组件是用不起来的。第二个是在开发阶段,我强烈建议你临时禁用浏览器的持久缓存,这样你每次修改代码,都能立即加载到最新版本,避免因为缓存导致调试起来很困惑。 接下来是组件的配置文件,也就是那个 .js-meta.xml 文件。里面必须包含 lightning__DynamicComponents 这个功能标记,并且 apiVersion 要设置为 55.0 或更高。这是让组件具备动态实例化能力的前提。 最后要注意一点关于打包和分发的支持情况:托管包里是支持动态组件的,但未锁定包,也就是 Unlocked Package,目前还不支持。所以在做 ISV 或者发布应用的时候,要根据你的包类型来决定能否使用这个特性。 总结一下,就是牢记先开 LWS,开发时关缓存,元数据配好功能标记和 API 版本,最后包类型要看清楚。这样你就能安全高效地用好动态组件了。
今天我们聊聊LWC里一个很实用的技巧:动态加载组件。 想象一下,你页面上有个位置,但暂时不知道应该放哪个组件进去,就像搭积木时先留个空位。在LWC里,我们可以用 `<lwc:component>` 这个特殊的标签,它就是一个“占位符”。它本身不渲染出任何实质内容,只是告诉框架:“这里一会儿要放一个真正的组件进来”。 那怎么把真正的组件填进去呢?关键就在 `lwc:is` 这个指令。你把它绑到一个变量上,到时候这个变量存的是一个组件的“构造函数”。框架一旦发现这个构造函数有值,就会把占位符替换成真正的组件实例。如果构造函数一直是空的(比如 `null` 或 `undefined`),那么页面就什么都不显示,这特别适合你想根据某些条件来决定要不要加载某个组件,也就是条件的组件加载。 好,那我们上哪弄到这个构造函数呢?可不能像平常一样在文件最上面用 `import` 语句,那样是静态的。我们要动态导入,用的是 `import()` 函数。注意,这是函数调用,不是那个静态的 `import` 语句,它返回一个Promise,等模块下载完,Promise就会resolve,并把模块导出内容给你。 这时候我们要的是那个模块的默认导出,因为通常LWC组件都是默认导出的。解构的时候写法是 `{ default: ctor }`, 这个 `ctor` 就是我们需要的组件构造函数。拿到之后,把它赋给那个绑定在 `lwc:is` 上的变量,框架就自动把新组件渲染出来了。 有一点要牢记:组件名称要写成 `c/yourComponentName` 的格式,`c` 是命名空间,一定要带上,否则框架找不到。 最后说一个写代码时的小陷阱。很多同学想把动态导入的Promise直接放在 `connectedCallback` 里用 `async/await`。但是请注意,`connectedCallback` 本身是同步执行的,如果你把它声明成 `async`,它不会等里面的await完,这可能导致子组件还没准备好,引发一些奇怪的问题。所以正确的做法是,在 `connectedCallback` 里调用一个单独的异步助手方法,比如叫 `loadComponent()`,让它去执行动态导入并赋值。这样既保证了生命周期钩子是同步的,又能异步加载你的组件。 简单总结一下:`lwc:component` 是个预留坑位,`lwc:is` 是往坑位里放哪个组件的开关,而这个开关的值由动态 `import()` 获取,最后在 `connectedCallback` 里用一个异步函数悄悄去做这件事。这样你的页面就能按需、灵活地加载组件了。 好了,关于动态加载组件的核心点就是这些,下节课我们来看具体的代码示例,会更直观。
好,同学,我们来看这段关于动态导入的推荐做法。 先说个重要的概念:现在,处理动态导入,Lightning Web Components 官方推荐使用 ,async/await, 模式。它比老式的 Promise 链更清晰,也更容易维护。 那实现的时候,有一个非常关键的架构决策,你要记住——,一定要让 `linkedCallback` 这个生命周期钩子保持同步,,然后把真正的异步工作委托给一个单独的 `async` 助手方法。为什么?如果让 `linkedCallback` 直接返回一个 Promise,框架不会等待它,这会导致组件还没准备好就渲染了,产生很多时序上的 Bug。所以这种“同步钩子 + 异步助手”的模式,能帮你完美避开这些问题。 接下来,我把整个流程梳理一下,你一听就明白: 1. 组件挂载时,,`linkedCallback` 被触发,,它马上调用我们定义好的异步加载助手。 2. 这个助手会 ,启动动态 `import()`,,去加载外部组件。 3. 在导入完成之前,我们的主组件不会傻等——它会 ,先用一个占位符渲染,,比如放一个小小的加载图标或文字。 4. 等动态导入的 Promise 解析完成,拿到 ,组件的构造函数,。 5. 这时候,我们用这个构造函数替换掉占位符,,生成真正的动态组件,。 6. 最后,这个新挂载的动态组件,,它自己的 `linkedCallback` 也会被触发,,完成它内部的初始化。 这样既保证了主组件快速响应,又完全控制了异步加载的时机。 最后,还有一个测试中容易踩的坑:当你用动态导入时,最终渲染出来的自定义元素,它的标签名是 LWC 内部的默认值,并不是你源代码里的那个名字。那在测试里你怎么选中它呢?不要依赖标签名,而是要用 ,`lwc:ref` 指令,,或者给动态容器加一个 ,自定义的 `data-*` 属性,,然后用 `querySelector` 去获取,这样才可靠。 这就是整个 async/await 动态导入的推荐做法和要点,大家理解了吗?
好,咱们今天来聊聊动态组件的选择和它的生命周期。 你可能会觉得,动态组件嘛,用起来很方便,但要选中它、操作它,就得留个心眼了。关键是,这个组件在被真正挂载到DOM之前,你是没法通过常规方式去选中它的。就像你想叫一个人,但他还没进房间,你喊他没用。 那怎么知道组件准备好了呢?有两种常用方法。第一种,在动态组件自己的代码里,用`connectedCallback`这个生命周期钩子,它会在组件挂载到DOM的那一刻触发。也就是说,在这里面去操作自己,是绝对安全的。 第二种,在父组件里,你可以用`renderedCallback`,然后去检查`this.refs.myCmp`。但注意啊,`this.refs`这个东西并不是在构造函数执行完后立马就有值的,得等到父组件渲染完,也就是调用完`renderedCallback`的时候,它才可用。所以你写的时候要加个防护判断,确保引用存在了再去用。 另外,说到测试,比如用Jest写单元测试的时候,有个容易踩的坑:动态组件的标签名通常是内部生成的,你如果直接依赖那个标签名去查询元素,测试可能随时会坏掉。更好的做法是什么呢?你给动态组件加上一个自定义数据属性,比如`data-id="dynamic-cmp"`,然后在测试里用属性选择器去找到它。这样你的测试就稳多了。 简单总结一下:动态组件得等它挂载好才能选到,用`connectedCallback`或`renderedCallback`加防护来判断;测试时别靠内部标签名,用自定义数据属性来定位。这样你的代码既可靠又好维护。
好,咱们今天聊一聊 Lightning Web Components 里动态组件的几个关键点。这块内容其实不复杂,但有些小细节你理解了之后,开发的时候会少踩很多坑。 首先,动态组件和静态组件几乎支持完全一样的 HTML 属性和指令。像 id、class、事件绑定这些,动态组件该有的全都有。唯一要注意的例外,就是一个叫 ,lwc:externative, 的属性,动态组件是不支持的。所以如果你在静态组件里用过它,切到动态组件的时候,这里要留个心。 接下来是渲染顺序。当你在模板里用了 `lwc:component` 这个元素,它里面是可以放一些子元素的。这些子元素,不会跟动态组件同时出现,而是会等到动态组件自己渲染完成之后,才被挂到页面上。也就是说,动态组件先到位,里面的静态子元素后出现。这个顺序在调试布局和加载时序的时候要记住。 再讲一个非常重要的机制:当动态组件的构造函数发生更改的时候——说白了就是你要切换成另一个组件——LWC 不会只是温柔地把组件换掉,而是会把整个子树连根拔掉,然后重新建一棵新的。这棵“子树”包括这个动态组件本身,以及它里面嵌套的所有东西。所以,切组件时,之前组件的所有状态、本地数据都会丢失,这是一个彻底的销毁和重建过程。 然后是属性传递。给动态组件传值,有两种方式。第一种最简单:当你早就知道要渲染哪个组件,而且属性值也是写死的,直接在标记里写就行,就跟静态组件传属性一模一样。比如 `<c-my-comp name="hello">` 这样。 第二种方式就更灵活了,用 ,lwc:spread, 指令。它的作用,是让你可以在运行时动态计算一个属性对象,然后一下子把所有键值对传给组件。这在你不知道具体渲染哪个组件,或者不同组件接受的属性集不一样时,特别有用。你想啊,一个组件要 `title`,另一个可能只要 `message`,你提前把属性打包成一个对象,用 `lwc:spread` 往上一贴,完美适配,代码也干净。 所以,总结一下:动态组件几乎全兼容,只差一个 `lwc:externative`;里面的子元素会后渲染;切组件就是整棵子树替换;属性传值,静态的直接写,动态的靠 `lwc:spread` 搞定,尤其适合那种需要灵活分发属性的场景。 把这些点吃透,动态组件你就能用得得心应手了。
同学们,今天咱们来看一个非常实用的综合性例子,它把动态组件的四个关键指令放在一起协同工作。这样你就知道怎么在实际项目里灵活切换和配置组件了。 首先,这四个指令分别是:`lwc:is` 用来指定要渲染哪个组件的构造函数,`lwc:spread` 负责把动态属性一股脑儿传给子组件,`lwc:on` 专门绑定自定义事件的处理程序,还有一个是标准 HTML 里的 `onclick`,咱们用它来做切换按钮,控制哪个组件显示。 这个例子的精妙之处在于用了两个 getter,根据当前加载的组件不同,动态返回不同的配置。一个叫 `childProps`,它会判断:如果现在是组件 A,就返回一套属性;如果是组件 B,就返回另一套属性。另一个是 `eventHandlers`,它把不同的自定义事件名——比如 childA 派发的 `customEventA`、childB 派发的 `customEventB`——分别映射到各自对应的处理函数上。 切换动作由一个叫 `switcherElement` 的方法完成,它做的事情就是在两个组件的构造器之间来回变。每当你点击按钮,构造器一变,LWC 引擎就会自动把旧的组件从 DOM 里移除,然后根据新的构造器实例化新组件,同时把 getter 里准备好的属性和事件处理程序准确应用上去。新组件挂载后会触发 `connectedCallback`,从里面抛出事先定义好的自定义事件,父组件就能收到并做出响应了。 这样一来,我们就可以用一个简单的模式完成“动态加载不同组件,并且让每个组件携带自己需要的属性和监听专属的事件”,整体代码既干净又灵活。这个示例帮你把动态组件的核心思想串了起来,以后遇到需要按条件展示不同组件并传不同参数、响应不同事件的场景,直接套用这套逻辑就行。
我们来看看这段内容,它讲的是在Lightning Web Components里,怎么把记录ID传给动态组件,以及怎么监听事件。 我直接用大白话给你翻译一下:当你要把一个记录的ID传给动态加载的组件时,方法其实跟传给普通属性一模一样。你在父组件的模板里,用“record-id”这个属性,把值传过去。动态组件那边呢,用“@api recordId”这个装饰器来接收就行了。 那这个ID怎么来的呢?通常,父组件自己也是通过“@api recordId”从页面上下文里拿到这个ID,然后再传给子组件。你看,就这么简单。 再来说说事件通信。动态组件虽然是用`lwc:component`动态加载的,但它发事件和监听事件的机制,跟静态写的子组件没什么区别。你要做的就是先在父组件的JavaScript里定义一个对象,比如叫`eventHandlers`,把你想监听的事件名称和对应的处理函数配成一对一对的。然后,在模板里用`lwc:on`指令把这个对象传给动态组件。 当子组件需要告诉父组件一些事情时,它就创建一个CustomEvent,在里面带上详细数据,然后派发出去。父组件的处理函数就能收到这个事件,从事件对象里拿到数据。 这样一来,父组件和动态加载的子组件之间就能实现完整的双向通信了——父组件能把数据传进去,子组件也能把消息传回来。 好,这块内容你理解了吗?其实就是“属性往下传,事件往上抛”这个原则,哪怕组件是动态生成的,套路完全一样。
同学们,咱们这节课来聊一聊动态组件在性能方面需要特别注意的地方,这可是动态组件的“主战场”。 首先你要知道,在 Lightning Web Components 里,静态导入和你通常写的 `import` 语句一样,框架在打包的时候会把所有静态导入的组件代码都合并成一个大文件,浏览器一次请求就全拿到了,后面要用的时候马上就能渲染,没有任何额外的网络开销。 但动态导入就不一样了,比如你用 `import('c/xxx')` 这样的写法,是在代码跑到那一行才去请求对应的 JavaScript 文件,这就意味着要多一次网络来回。除非浏览器之前已经缓存过这个组件了,否则每次都能明显感觉到延迟。 所以这儿有两个重要的建议,听好喽: 第一个,写动态导入的时候,尽量用字符串字面量,比如 `import('c/myModal')`,而不是 `import('c/' + componentName)` 这种拼接的变量。因为用字符串字面量的话,打包工具或者未来的框架版本就能提前分析出来你要用到哪些组件,甚至可以提前把它们打包到一起,避免运行时再去请求,这叫“可静态分析”。虽然咱们现在的框架还没做这个优化,但是将来的版本很可能就会这么做,你这么写是在为长期性能做准备。 第二个,千万别滥用动态导入。我的建议是,项目初期一律先用静态导入,把组件都老老实实 import 进来放在那儿。等到你确实发现打包后的文件太大了,大到已经明显拖慢了页面第一次加载的速度,这时候你再有针对性地把那些不常用的、大的组件改成动态导入。记住,性能优化永远是先测量、后优化,而不是一上来就到处用动态导入,反而可能把用户体验搞得更差。 好,关于动态组件的性能要点就这两条,简单但非常重要,实际项目里一定得把握好这个平衡。有什么问题随时打断我。
同学们,今天咱们聊聊 Lightnin Web Components 里加载图表组件的一个实际例子。假设页面上要展示三种图表:柱状图、折线图、饼图。以前有人为了“按需加载”,写了一个动态导入的方法,但那种写法是“不可分析”的,什么意思呢?框架没法提前知道你到底要加载什么,所以无法做优化。现在咱们来看看怎么把它改好。 第一种情况:如果三种图表打包在一起总大小很小,比如总共就10KB,那最简单、最快的做法就是静态导入。在组件的开头,直接 `import` 这三个图表构造器,存到一个映射对象里。然后当你需要哪个图表,就直接从这个映射对象里根据名字拿出来,同步创建。这样代码最干净,页面第一次加载时就把所有图表代码都下载了,但因为它本来就很小,根本不影响速度,还省去了动态加载的复杂逻辑。这叫静态导入版本,最简单,也最快。 第二种情况:如果每个图表组件都比较大,比如各自有30KB,那你就想让用户用到哪个才加载哪个,这样能省带宽。这时候你可以用“可分析的动态导入”。怎么写呢?你仍然定义一个映射对象,但这次对象里存的不是直接导入的构造器,而是一个个“thunk 函数”,也就是小箭头函数,函数里面才 `import('./barChart')` 这种语法。你看,`import()` 调用不是在顶层直接执行,而是包在一个函数里,这叫“延迟执行”。框架看到这种写法,就能分析出来:“哦,这个组件未来可能加载这三个模块”,于是它可以做预取、预加载等优化。同时,也保留了懒惰加载的特性。这种写法既灵活,又对框架友好,是推荐的做法。 下面我强调一个常见的反模式:直接接受一个组件名称字符串作为属性,然后在代码里动态拼接导入路径,比如 `import('./' + this.chartType)`。这种写法非常糟糕,有两个问题:第一,它不可分析,因为框架不知道最终字符串会变成什么,也就没法优化;第二,它把加载责任全压在自己身上,父组件想换个图表还得知道内部的路径规则,调试困难。正确的做法是:子组件直接接受一个“构造器”属性,由父组件来负责导入。父组件知道自己要用什么,就可以用静态导入,或者用可分析的动态导入把构造器传进来,这样责任清晰,加载也可控。 最后,还有一种“最后的手段”,就是字符串插值,比如用反引号 `` import(`./components/${name}`) ``。只有在组件名真的从元数据里来,完全不可提前知道的时候,才用这种方法。因为这也属于部分不可分析,但至少比完全拼接字符串要好一点。但要记住,这是权宜之计,能不用就不用。 好,总结一下:小包用静态导入,简单快;大包用可分析的动态导入,包成 thunk 函数;千万不要自己接字符串再来导入,把导入权交给父组件,保持代码可分析。这样做出来的组件,既好维护,又能享受框架的各种性能优化。今天就讲这么多,大家有空可以动手试一试。
好,同学们,今天我们来说说动态组件在打包方面的“脾气”。别看动态组件平时用着挺顺,一旦涉及到包的类型,它就会有点挑剔。 首先记住一个关键点:动态组件呢,在,托管包,里是能正常工作的,但在,解锁包,里是不行的。如果你打算把组件打包发布出去,要注意这个限制。另外,如果你是从托管包里导入动态组件,千万别用默认的“c”命名空间,一定要用托管包自己的命名空间。因为动态组件在查找时,认的是命名空间,弄错了它就找不到回家的路了,自然就歇菜了。 那调试的时候,怎么判断动态组件到底有没有真正换成功呢?最权威的办法就是用浏览器开发工具检查一下 DOM。当组件的构造函数发生变化时,页面上的元素标签应该是物理性替换掉的。如果你发现标签还是老样子,那就说明交换没发生,问题就出在路径或者配置上。 那怎么排查呢?我给大家总结了一个检查清单,遇到问题可以按这个顺序来: 第一,,确认 LWS 也就是 Lightning Web Security 已经启用,——这可是硬性要求,没它不行。 第二,检查 ,.js-meta.html 配置文件,是否设置正确,这个文件就是组件的“户口本”,信息得对。 第三,核实一下 ,API 版本,,是不是和动态组件兼容。 第四,在开发期间,建议,把持久缓存暂时关掉,,有时候缓存会让你误以为更新了,其实没生效。 第五,再仔细检查一下,导入路径的格式,,特别是命名空间,别写错。 第六,验证一下,构造函数是否有效,,比如是不是用正确的组件类来构造的。 第七,打开浏览器控制台,看看有没有,报错信息,,哪怕是一条小红字都可能藏着线索。 最后,再看一眼,网络标签页,,确认有没有真正发出导入组件的请求。如果请求都没发出去,那肯定没戏。 照着这个清单走,基本就能把动态组件不工作的问题给揪出来。动态组件虽然灵活,但就像个要带钥匙进门的家伙,少了一步都不行。好了,这部分就讲到这儿,大家实践中可以多试试。
各位同学,我们今天来看看LWC开发中的一个新变化——TypeScript支持。 从25年冬季版本开始,Salesforce给LWC引入了TypeScript支持,不过先提醒一下,这还只是一个,开发人员预览版,。也就是说,你可以去尝试它,做实验,提供反馈,但,还不能用在正式的生产环境里,,因为Salesforce可能会随时调整甚至拿掉一些功能,这点要心里有数。 那TypeScript到底给LWC开发带来什么好处呢?简单说就是三个方面: 第一,,提前发现错误,。在你写代码的时候,构建工具就会做类型检查,能抓到很多原本要等到程序跑起来才会暴露的错误。 第二,,写代码更快更准,。你的编辑器(比如VS Code)会自动提示有哪些属性、方法可以用,就像有了一个智能小助手。 第三,,代码更容易看懂,。静态类型分析让代码质量更高,而你写的那些明确的类型标注,其实就是活文档,以后自己或别人看代码一眼就明白。 如果你准备用TypeScript来开发LWC,先要准备好两样东西: 1. ,VS Code的Salesforce扩展包,——它会帮你自动装上需要的类型定义文件,省心很多。 2. 没装扩展包的话,你也可以手动安装`@salesforce/lightning-types`和`lwc`这个npm包。 整个工作流程其实也不复杂: 首先在你的项目里,开启TypeScript支持,,然后,添加相关的TypeScript包,,接下来你就可以用`.ts`文件来写组件了,最后在部署之前,这些`.ts`文件会被自动编译成浏览器能跑的`.js`文件。 好了,这就是关于LWC TypeScript支持的简要介绍,大家现在可以自己试一试,感受一下它带来的好处。但记住,它目前还是预览版,生产环境先别着急用哦。
大家好,今天我们来把 TypeScript 加到 Lightning Web Component 项目里,整个过程分三步,一点都不复杂。 第一步,先打开项目里的 settings.json 文件,找到叫做 salesforcedx-vscode-lwc.preview.typeWrittSupport 的功能标志,把它设成 true。这一步会让系统自动在 lwc 目录下生成一个 tsmix.json 文件。如果设完之后这个文件没立刻出现,别担心,重启一下 VS Code 就好了。 第二步,我们来配置 tsspread.json 里的编译器选项。这里面最关键的一个设置叫 experimentalDecorators,一定要设为 true。注意了,TypeScript 早期那种实验性的装饰器支持跟我们 LWC 的装饰器实现是不兼容的,所以必须关掉它。另外,配置文件里有个 extends 属性,它直接引用 SFDX 给我们生成好的基本配置,这个基本配置已经告诉了 TypeScript 该处理哪些文件,并且自动排除了测试文件,防止它们被编译。 第三步,我们要用 npm 把 TypeScript 作为开发依赖安装进来,版本最低要求是 5.4.5。记住,TypeScript 的版本需要我们自己来管理和更新,平台本身不提供它的运行时。 只要完成这三步,你的 Lightning Web Component 项目就能愉快地使用 TypeScript 啦。大家试一试,有问题随时问我。
大家好啊,咱们今天聊一个部署 Lightning Web Components 的时候很容易忽略,但又特别关键的一步——TypeScript 的编译。 很多同学可能已经用上 TypeScript 写 LWC 组件了,毕竟类型检查能帮我们提前发现好多低级错误。但你要记住,Salesforce 平台本身是不认识 TypeScript 的,LWC 的编译器也只认 JavaScript 文件,它压根儿没有内置 TypeScript 的编译功能。 所以怎么办?很简单,你在把代码部署到 Salesforce 之前,必须先用标准的 TypeScript 编译器,也就是咱们常说的 tsc,亲手把 .ts 文件转换成 .js 文件。常用命令就是 `npx tsc --Project`,它会把你项目里 lwc 目录下所有的 TypeScript 文件都编译一遍。编译完你会发现,每个 .ts 文件旁边都自动生成了一个同名的 .js 兄弟文件。最后真正上传到平台、被 Salesforce 运行的,就是这些编译后的 JavaScript,你的 TypeScript 源码平台看都不看一眼,也不会存起来。 那你的 TypeScript 源文件就只能靠自己管理了,通常就是放到 Git 这种源代码管理系统里。这也意味着你本地开发的时候,得维护好编译这个环节,每次改完 .ts 代码,先跑一遍 tsc,再用 Salesforce CLI 去部署生成的 .js 文件。 如果你已经有一些用 JavaScript 写的现成组件,想迁移到 TypeScript,流程也很简单粗暴:直接把 .js 文件重命名成 .ts,然后跑一遍编译器,看看爆出哪些类型错误,一条条修掉就行了。遇到不知道怎么改的问题,建议去查一下官方的 TypeScript 迁移指南,里面总结了各种常见转换场景的处理方法,能帮你省不少时间。 总之一句话,用 TypeScript 写 LWC,部署前千万别忘记手动编译,不然 Salesforce 可不认识你的 .ts 文件。好,这一节就讲这些,咱们下次见。
我们来看看怎么用Jest测试TypeScript组件。其实很简单,因为sfdx-lwc-jest这个测试运行器已经帮我们处理好了TypeScript的编译,所以你完全不用去改jest的配置文件。 你要做的就是,把你的测试文件写成以 .test.ts 结尾的文件,里面的结构和写JavaScript测试是一模一样的。不过,为了让编辑器能够识别Jest的那些全局变量,比如 describe、it、expect,还能给你自动补全和类型检查,你需要安装 @types/jest 作为开发依赖。 装好之后,再打开项目根目录的 tsconfig.json 文件,找到 types 这个数组,把 "jest" 加进去。这样TypeScript就知道这些测试全局变量了。 运行测试也很直接,就用我们常用的 npm run test:unit 命令就行。Jest在执行测试之前,会自动把TypeScript编译成JavaScript,所以整个过程是无缝的。 这里要特别提醒你一个TypeScript特有的问题,就是非空断言。你可能会习惯在访问 this.template 的时候用感叹号,因为你知道在 shadow DOM 模式下,它一定是存在的,所以写 this.template! 感觉很安全。但是,如果你启用了轻量级DOM模式,this.template 的值就是 null 了,这个时候再用非空断言,它会在运行时真的报错。所以记住,只有在你百分之百确定组件是 shadow DOM 渲染的时候,才可以用这种断言,否则就老老实实做判空处理,避免运行时出错。 简单总结一下:配置一次,写测试就和平常一样,跑起来也自动编译,但要注意 TypeScript 的断言陷阱,特别是跟渲染模式相关的。好,这一页就讲到这里。
同学们,今天我们来聊聊LWC TypeScript开发中的一个重要小伙伴——,@salesforce/lightning-types, 这个包。 你可以把它想象成一个,类型定义的中央图书馆,。我们在写LWC的时候,如果用TypeScript,就需要知道各种组件、模块长什么样,有哪些方法、属性。这个包就是专门为LWC准备的官方类型说明,就像一本详细的说明书,告诉TypeScript:“嘿,这个lightning-button组件有个variant属性,可以传‘brand’、‘neutral’这些值。” 现在这个包还在,开发人员预览阶段,,所以里面的类型还不是特别全,但团队正在不断地往里面加新的定义,就像图书馆在不停地进新书。 你可能会发现,用VS Code写LWC时,即使没装这个包,有些模块也有类型提示。这是因为,VS Code的Salesforce扩展包,自动帮你生成了临时的类型定义,比如`lwc`、`@salesforce/apex`、`@salesforce/schema`和`lightning/mailService`这些核心模块。但目前这些自动提示只是“临时借用”的,等预览结束,这些定义就会正式搬迁到`@salesforce/lightning-types`这个中央图书馆里。到时候扩展包就不再自己生成临时类型,而是都从图书馆里取。 如果你的开发环境没有用VS Code,或者你想自己完全掌控类型定义,那就需要手动设置。步骤很简单: 1. 先安装这个包,就像去图书馆领一张借书卡; 2. 然后创建一个声明文件,把图书馆的卡激活一下; 3. 最后在项目的`tsconfig.json`里配好路径映射,告诉TypeScript:“遇到这些模块名,请到图书馆里去找它们的说明。” 这里要特别提一下,,Lightning基础组件,,比如`lightning/button`,它们自己就带着类型定义,用起来就很方便,编辑器直接就能智能提示。但如果你的项目里用了一些,第三方组件,或者,自己封装的组件,,它们可能还没有类型定义,那该怎么办呢? 这种情况你可以用TypeScript的,环境模块声明,来自己描述这个组件长什么样。比如写一个`.d.ts`文件,用`declare module`来定义组件的类、公共方法、属性等。这样你就相当于给这些组件也画了一张“说明书”,TypeScript就能看懂了。 总结一下,`@salesforce/lightning-types`是LWC TypeScript类型定义的大本营,预览期间VS Code的自动提示只是临时方案,未来都会归到这儿来。用手动项目的话,记得自己安装和配置。遇到缺类型定义的情况,就自己动手写声明,让TypeScript也能明白你的自定义组件。 这样讲清楚了吗?有什么问题随时可以问我。
同学你好,今天我们来讲一个在LWC中用TypeScript开发时必须掌握的知识点,就是五个关键的考虑因素。这些东西如果你提前不知道,后续开发很容易踩坑,甚至造成生产事故。下面我就一个一个帮你拆解清楚。 先说第一点,,TypeScript版本的管理是完全由你自己负责的,。Salesforce平台不会帮你安装、配置,也不会自动升级TypeScript版本。也就是说,你的项目该用哪个版本、该怎么编译,全都得你自己在本地的开发环境中搞定。平台只认编译好的JavaScript代码。 第二点,既然平台不认TypeScript源文件,那就意味着,你必须使用源代码管理,。TypeScript源文件永远不会被部署到Salesforce平台上,也绝不会被存储在那里。能上生产只有编译后的JavaScript。所以你的源码版本控制非常重要,不然源文件丢了,就等于丢了原始设计。 接下来第三点,这个很多人都会犯错,就是,非空断言,代码里那个感叹号,一定要谨慎使用,。你平时可能会在`this.template`或者`Element.shadowRoot`后面加个感叹号,告诉编译器“这个肯定不是空的”。但要注意,这两个属性仅在组件运行在Shadow DOM模式下才能保证不空。一旦你组件改成Light DOM模式,它们就变成可空的了,你断言成非空就会直接导致运行时错误。所以用感叹号时一定要确认好自己组件的DOM模式。 再看第四点,关于装饰器。目前LWC的装饰器实现有个限制:,你在使用任何装饰器的时候,必须在这一行上面加`// @ts-ignore`或者`// @ts-expect-error`注释,。因为现在TypeScript的`experimentalDecorators`配置必须设为`false`,否则编译不过。这会带来一些麻烦,但Salesforce团队正在努力解决,会在正式发布版本之前把这个痛点处理掉,目前我们先这么用着。 最后第五点,我们要了解,当前不支持哪些用例,,以免白白浪费时间。首先,Salesforce CLI还没有集成好TypeScript的支持,很多自动化流程得自己搭。其次,TypeScript的源映射调试现在还无法直接进行,所以你调试的时候看到的其实是编译后的JavaScript,而不是你写的TypeScript代码。再有,非Salesforce相关的类型定义也不支持,想用第三方库的类型可能会碰壁。最后,自定义Salesforce对象和字段的类型定义目前也还没有现成的生成方式,需要自己手动维护。 这几个点都很关键,你可以在实际项目中记下来,我们后续课程还会结合实际代码来演示,确保你真的理解了。记住,用TypeScript写LWC,多留个心眼,开发体验就会顺畅很多。