相关文章推荐

在构建 SharePoint 框架组件时,通常会引用第三方库,如将 Office UI Fabric React 用于用户界面控件,或将 Moment.js 用于时间处理。 这些库中的每个库都将增加组件捆绑的 JavaScript 文件的大小。 例如,Moment.js 将对生成的捆绑包增加 ~250 KB!

JavaScript 捆绑包 是一个 JavaScript 文件,它组合了一个或多个 JavaScript 文件或样式表。 打包 SPFx 解决方案时,默认情况下,所有代码以及导入到项目中的库都捆绑到一个 *.js 文件中。 拆分捆绑包的操作是生成多个 *.js 文件而不是一个文件,以便可以单独加载它们。

根据组件的不同,这些第三方库可能在组件生命周期的所有部分中使用,也可能不使用。 也许它们只在编辑模式或属性窗格中使用,也可能在用户单击组件中的链接或按钮时需要用到。

如果 SPFx 组件更像是一个单页应用程序 (SPA),优化大小可能并不重要,但如果它是添加到页面或者甚至在页面上多次使用的 Web 部件,比如搜索 Web 部件,那么减少加载的脚本数量和在页面上执行的脚本数量将关系到页面的加载时间。 这会对移动体验产生重大影响。

为了提高 SPFx 组件加载页面的速度,可以利用将捆绑包拆分为多个 JavaScript 部分,并根据需要动态加载这些单独捆绑的包。

拆分多个 Web 部件以单独加载

具有多个 Web 部件或扩展的 SPFx 项目包含文件 ./config/config.json ,类似于以下内容:

"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json", "version": "2.0", "bundles": { "my-spfx": { "components": [ "entrypoint": "./lib/webparts/part1/part1.js", "manifest": "./src/webparts/part1/part1.manifest.json" "entrypoint": "./lib/webparts/part2/part2.js", "manifest": "./src/webparts/part2/part2.manifest.json" "externals": {}, "localizedResources": {}

在上面的配置中,两个 Web 部件都包含在同一个 JavaScript 捆绑包中。 这意味着,如果用户向页面添加其中一个 Web 部件,则将同时加载这两个部件。 对于两个 Web 部件同时位于同一页面的情况,这是可以的,因为它缩短了下载一个文件而不是两个文件的下载时间。

但是,如果 Web 部件是单独使用的,则最好将它们拆分成两个文件,以减少页面下载所需的内容,从而只运行其中一个。

这可以通过修改 config/config.json 来实现,以便每个 Web 部件作为单独的 JavaScript 文件捆绑:

"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json", "version": "2.0", "bundles": { "my-spfx-1": { "components": [ "entrypoint": "./lib/webparts/part1/part1.js", "manifest": "./src/webparts/part1/part1.manifest.json" "my-spfx-2": { "components": [ "entrypoint": "./lib/webparts/part2/part2.js", "manifest": "./src/webparts/part2/part2.manifest.json" "externals": {}, "localizedResources": {}

分析捆绑包

若要大致了解从何处开始和优化,请参阅 针对生产优化SharePoint 框架生成 一文,其中演示了如何获取代码或第三方库的哪个部分占用了捆绑包空间的可视地图。

动态加载第三方组件

打包 SharePoint 框架解决方案时,构建工具链将使用 webpack 生成捆绑包。 webpack 的一个功能是能够动态导入应用程序的各个部分。 可在具有次要代码重构的 SharePoint 框架项目中实现此操作。

在以下代码中,Moment.js 库将包括在解决方案的 JavaScript 捆绑包中。 即使永远不会调用方法 GetTime() ,此代码也将始终加载到页面上。

import * as moment from moment
export default class MyClass {
  public GetTime(dateString:string){
    return moment(dateString).format("LL");

但是,在以下代码中,调用 GetTime() 方法时将异步加载 Moment.js 库,这将减少初始加载和执行时间。 请注意其他 webpackChuckName 代码注释

export default class MyClass {
  public async GetTime(dateString:string) {
    const moment = await import(
      /* webpackChunkName: 'my-moment' */
      'moment'
    return moment(dateString).format("LL");

如果你的 config/tsconfig.json 文件具有 "module": "commonjs" 而不是 "module": "esnext",则需要使用下面的方法将动态库从捆绑包中拆分出来。

declare var System: any;
export default class MyClass {
  public async GetTime(dateString:string){
    const moment = await System.import(
      /* webpackChunkName: 'my-moment' */
      'moment'
    return moment(dateString).format("LL");

验证代码拆分和动态导入

若要验证是否正在进行动态导入,请在执行 gulp bundle 任务之后打开 ./dist 文件夹,并查找作为单独 JavaScript 文件动态加载的库。 在下图中,Moment.js 库已拆分为自身的文件。

不是每个文件或库都应动态导入。 相反,请考虑将某些方案或功能的类似代码块组合到一个捆绑包中。

例如,创建一个引用多个库的文件 **MyStuff.ts,然后动态加载 MyStuff。 在这种情况下,库 third-partyleft-party 将捆绑到 my-stuff.js 中。

// MyStuff.ts
import { Something } from 'third-party;'
import * as Foo from 'left-party';
// Other file
await import(
  /* webpackChunkName: 'my-stuff' */
  './MyStuff'

特殊属性窗格动态加载

此功能的另一个用例是动态导入仅在属性窗格中使用的代码。 在这种情况下,你只能在 Web 部件的属性窗格处于活动状态时将此代码下载到浏览器中。

在主 Web 部件文件中,创建名为 loadPropertyPaneResources() 的方法。 此方法将在显示 Web 部件的属性窗格之前执行。 这只允许动态加载属性窗格所需的资源。

  • 创建新文件 HelloWorldWebPartPropertyPaneStuff.ts
  • 将所有与属性窗格相关的代码移动到该文件中
  • 在主 Web 部件类中创建以下方法
  • protected loadPropertyPaneResources(): Promise<void> {
      return import(
        /* webpackChunkName: 'HelloWorldWebPartPropertyPaneStuff' */
        './HelloWorldWebPartPropertyPaneStuff'
      ).then(component => {
        this._propertyPaneHelper = new component.HelloWorldWebPartPropertyPaneStuff(this);
    

    构建由多个组件组成的 SPFx 解决方案时,或者如果你使用的是第三方库,请考虑动态导入。 首先分析生成的捆绑包大小,并使用本页中概述的策略将代码拆分为多个捆绑包,其中每个捆绑包仅在需要时加载。 这将减少最终用户加载和执行页面所需的时间。