将 SharePoint 托管的外接程序转换为SharePoint 框架解决方案

在 SharePoint 外接程序模型中,你用于创建所谓的 SharePoint 托管的外接程序,即 SharePoint Online 上托管的应用程序,以及 SharePoint 外接程序模型基础结构自动创建的专用应用程序网站。 在 SharePoint 托管的外接程序中,通过 JavaScript SharePoint 对象模型 (JSOM) 在应用程序网站本身或主机网站中使用 SharePoint Online 数据。

在 SharePoint Online 的新式开发环境中,只需使用SharePoint 框架创建客户端解决方案,并开始依赖 REST API 和 Microsoft Graph 使用 SharePoint Online 数据。

重要

本文指的是所谓的 PnP 组件、示例和/或工具,它们是由提供支持的活动社区支持的开源资产。 没有来自 Microsoft 的官方支持渠道的开放源代码工具支持的 SLA。 但是,这些组件或示例使用的是 Microsoft 支持的现成 API 和 Microsoft 支持的功能。

如果愿意,可以watch以下视频,而不是阅读整篇文章,你仍然可以将其视为更详细的参考。

将 SharePoint 托管的外接程序转换为SharePoint 框架解决方案

使用 SharePoint 框架 创建客户端解决方案

让我们创建一个新的 SharePoint 框架 解决方案,用于使用 SharePoint Online 数据。

首先,你需要搭建SharePoint 框架解决方案的基架,因此启动命令提示符或终端窗口,创建一个文件夹,并从新创建的文件夹内运行以下命令。

重要

为了能够遵循演示的过程,需要在开发环境中安装SharePoint 框架。 阅读文档设置SharePoint 框架开发环境,可以找到有关如何设置环境的详细说明。

yo @microsoft/sharepoint

PowerShell 窗口中基架工具的 UI,同时为SharePoint 框架新式 Web 部件创建新项目。

按照提示为新式 Web 部件搭建解决方案的基架。 具体而言,在工具提示时做出以下选择:

  • 解决方案名称是什么? spo-sp-fx-client
  • 要创建哪种类型的客户端组件? WebPart
  • Web 部件名称是什么? ConsumeSPOClientSide
  • 你想要使用哪个模板? React

根据上述答案,你决定创建一个名为 spo-sp-fx-client 的解决方案,其中将有一个名为 ConsumeSPOClientSide 的 Web 部件,该部件将基于React来呈现其用户体验。

基架工具将为你生成新的SharePoint 框架解决方案。 完成后,只需使用喜欢的代码编辑器打开当前文件夹即可。 如果你最喜欢的代码编辑器是 Microsoft Visual Studio Code,只需运行以下命令:

code .

要开始的main文件是 src\webparts\consumeSPOClientSide 文件夹下的 ConsumeSPOClientSideWebPart.ts 该文件基于 TypeScript 语法,这是SharePoint 框架使用的语法。

import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
  IPropertyPaneConfiguration,
  PropertyPaneTextField
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import { IReadonlyTheme } from '@microsoft/sp-component-base';

import * as strings from 'ConsumeSpoClientSideWebPartStrings';
import ConsumeSpoClientSide from './components/ConsumeSpoClientSide';
import { IConsumeSpoClientSideProps } from './components/IConsumeSpoClientSideProps';

export interface IConsumeSpoClientSideWebPartProps {
  description: string;
}

export default class ConsumeSpoClientSideWebPart extends BaseClientSideWebPart<IConsumeSpoClientSideWebPartProps> {

  private _isDarkTheme: boolean = false;
  private _environmentMessage: string = '';

  public render(): void {
    const element: React.ReactElement<IConsumeSpoClientSideProps> = React.createElement(
      ConsumeSpoClientSide,
      {
        description: this.properties.description,
        isDarkTheme: this._isDarkTheme,
        environmentMessage: this._environmentMessage,
        hasTeamsContext: !!this.context.sdks.microsoftTeams,
        userDisplayName: this.context.pageContext.user.displayName
      }
    );

    ReactDom.render(element, this.domElement);
  }

  protected onInit(): Promise<void> {
    return this._getEnvironmentMessage().then(message => {
      this._environmentMessage = message;
    });
  }

  // Here we intentionally removed some of the auto-generated code, for the sake of simplicity ...

  protected onDispose(): void {
    ReactDom.unmountComponentAtNode(this.domElement);
  }

  protected get dataVersion(): Version {
    return Version.parse('1.0');
  }

  protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
    return {
      pages: [
        {
          header: {
            description: strings.PropertyPaneDescription
          },
          groups: [
            {
              groupName: strings.BasicGroupName,
              groupFields: [
                PropertyPaneTextField('description', {
                  label: strings.DescriptionFieldLabel
                })
              ]
            }
          ]
        }
      ]
    };
  }
}

可以看到,在呈现方法中,Web 部件创建名为 ConsumeSpoClientSide 的 React 组件的实例,并为组件本身提供一组配置属性。

属性在文件 src\webparts\consumeSPOClientSide\components\IConsumeSpoClientSideProps.ts 中定义,而实际React组件在文件 src\webparts\consumeSPOClientSide\components\ConsumeSpoClientSide.tsx 中定义。

现在,假设要创建一个 Web 部件,以使用默认文档库中的文档列表, (使用 Web 部件的网站) 共享文档。 可以更新 IConsumeSpoClientSideProps.ts 以接受一些其他设置,如以下代码摘录所示。

import { SPHttpClient } from '@microsoft/sp-http';

export interface IConsumeSpoClientSideProps {
  description: string;
  isDarkTheme: boolean;
  environmentMessage: string;
  hasTeamsContext: boolean;
  userDisplayName: string;
  spHttpClient: SPHttpClient;
  webUrl: string;
}

类型为 SPHttpClientspHttpClient 属性(从 @microsoft/sp-http导入)表示一个客户端对象,可用于从 Web 部件内使用 SharePoint Online REST API。 webUrl 属性只是实例化 Web 部件的网站的绝对 URL。

因此,必须更新 Web 部件代码才能提供两个附加属性,如以下代码摘录所示。

import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
  IPropertyPaneConfiguration,
  PropertyPaneTextField
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import { IReadonlyTheme } from '@microsoft/sp-component-base';

import * as strings from 'ConsumeSpoClientSideWebPartStrings';
import ConsumeSpoClientSide from './components/ConsumeSpoClientSide';
import { IConsumeSpoClientSideProps } from './components/IConsumeSpoClientSideProps';

import { SPHttpClient } from '@microsoft/sp-http';

export interface IConsumeSpoClientSideWebPartProps {
  description: string;
}

export default class ConsumeSpoClientSideWebPart extends BaseClientSideWebPart<IConsumeSpoClientSideWebPartProps> {

  private _isDarkTheme: boolean = false;
  private _environmentMessage: string = '';
  private _spHttpClient: SPHttpClient;

  public render(): void {
    const element: React.ReactElement<IConsumeSpoClientSideProps> = React.createElement(
      ConsumeSpoClientSide,
      {
        description: this.properties.description,
        isDarkTheme: this._isDarkTheme,
        environmentMessage: this._environmentMessage,
        hasTeamsContext: !!this.context.sdks.microsoftTeams,
        userDisplayName: this.context.pageContext.user.displayName,
        spHttpClient: this._spHttpClient,
        webUrl: this.context.pageContext.web.absoluteUrl
      }
    );

    ReactDom.render(element, this.domElement);
  }

  protected onInit(): Promise<void> {

    this._spHttpClient = this.context.spHttpClient;

    return this._getEnvironmentMessage().then(message => {
      this._environmentMessage = message;
    });
  }

  // Here we intentionally removed some of the auto-generated code, for the sake of simplicity ...
}

在 Web 部件类的 onInit 方法中,可以获取对类型实例的SPHttpClient引用,以便将其提供给 render 方法中的 React 组件。 请注意,在SharePoint 框架中,任何组件中都有一个上下文属性,您可以使用该属性来访问常见的基础结构对象和服务,例如 SharePoint Online REST 客户端对象 (this.context.spHttpClient) 。

此外,在呈现方法中,还有使用 this.context.pageContext.web.absoluteUrl 语法从SharePoint 框架上下文中检索当前 Web 的绝对 URL 的逻辑。

现在,让我们重点介绍React组件。 首先,为了完整起见,React组件应依赖于自定义状态对象,该对象应定义创建名为 src\webparts\consumeSPOClientSide\components\IConsumeSpoClientSideState.ts 且包含以下内容的文件。

export interface IConsumeSpoClientSideState {
  documents: IDocument[];
}

export interface IDocument {
  Id: number;
  Title: string;
}

上述状态允许保存 IDocument 类型的项数组,这些项将提供目标文档库中每个文档的 ID标题

然后,在此处可以看到 React 组件的代码,以便通过 REST API 使用 SharePoint Online 并在 Web 部件输出中显示文档列表。

import * as React from 'react';
import styles from './ConsumeSpoClientSide.module.scss';
import { IConsumeSpoClientSideProps } from './IConsumeSpoClientSideProps';
import { IConsumeSpoClientSideState, IDocument } from './IConsumeSpoClientSideState';
import { escape } from '@microsoft/sp-lodash-subset';

import { SPHttpClient } from '@microsoft/sp-http';

export default class ConsumeSpoClientSide extends React.Component<IConsumeSpoClientSideProps, IConsumeSpoClientSideState> {

  constructor(props: IConsumeSpoClientSideProps) {
    super(props);
    
    this.state = {
      documents: []
    };
  }

  override async componentDidMount(): Promise<void> {
    await this._loadDocuments();
  }

  public render(): React.ReactElement<IConsumeSpoClientSideProps> {
    const {
      description,
      isDarkTheme,
      environmentMessage,
      hasTeamsContext,
      userDisplayName
    } = this.props;

    const {
      documents
    } = this.state;

    return (
      <section className={`${styles.consumeSpoClientSide} ${hasTeamsContext ? styles.teams : ''}`}>
        <div className={styles.welcome}>
          <img alt="" src={isDarkTheme ? require('../assets/welcome-dark.png') : require('../assets/welcome-light.png')} className={styles.welcomeImage} />
          <h2>Well done, {escape(userDisplayName)}!</h2>
          <div>{environmentMessage}</div>
          <div>Web part property value: <strong>{escape(description)}</strong></div>
        </div>
        <div>
          <h3>Here is the list of documents:</h3>
          <ul>
            { documents.map(d => d.Title ? <li key={d.Id}>{d.Title}</li> : null) }
          </ul>
        </div>
      </section>
    );
  }

  private _loadDocuments = async () => {
    const apiResult: { value: IDocument[] } = await this.props.spHttpClient
      .get(
        `${this.props.webUrl}/_api/web/lists/getbytitle('Documents')/items')`,
        SPHttpClient.configurations.v1
      )
      .then((response: any) => {
        return response.json();
      });

      console.log(apiResult.value);

      this.setState({
        documents: apiResult.value
      })
    }
}

请注意,React组件声明依赖于 IConsumeSpoClientSideState) (自定义状态定义。 然后,在 componentDidMount 方法中,它使用 _loadDocuments 异步函数从 SharePoint Online 加载文档的实际列表。

_loadDocuments 函数仅使用 Web 部件提供的 spHttpClient 对象实例向当前网站发布的 REST 终结点发出 HTTP GET 请求,以便检索所选文档库中的文档列表。 结果将转换为 IDocument 类型的对象数组,并存储在 React 组件的状态中。 因此,组件的 render 方法可以呈现检索到的文档的列表。

如果需要,还可以依赖 PnPjs 开放源代码库,而不是使用低级别 REST API 来使用 SharePoint Online 数据,这也在将代码从 SharePoint JavaScript 对象模型 (JSOM) 升级到客户端代码和 PnPjs 一文中进行了介绍。

有关本主题的其他信息,请参阅以下文档: