将 SPFx Webpart 与 IE11、SharePoint 2016 和 Pannellum 一起使用

Posted

技术标签:

【中文标题】将 SPFx Webpart 与 IE11、SharePoint 2016 和 Pannellum 一起使用【英文标题】:Using SPFx Webpart with IE11, SharePoint 2016 and Pannellum 【发布时间】:2019-11-22 01:44:53 【问题描述】:

我需要一些有关 SPFx Web 部件以及 SharePoint 2016 和 InternetExplorer 11 的专业知识。 这个想法是创建一个使用 Pannellum 来显示图像的 Web 部件。在 Firefox 中它已经可以工作了。但是,对于 IE11,错误消息会出现在控制台中:SCRIPT5022: TypeMismatchError BaseURL.ts (16,7)

SCRIPT5007: Die Eigenschaft "toString" eines undefinierten oder Nullverweises kann nicht abgerufen werden.英文留言The "toString" property of an undefined or null reference cannot be retrieved. LogEvent.js (26,1)

在开发人员工具中,我可以看到图像已下载。但是当 webpart 的代码尝试调用 createObjectURL 并将图像作为 blob 对象时,它会崩溃。

Webpart 已使用 Yeoman (3.1.0) 创建并使用 Gulp 进行了测试。 在 webpart 中执行 Pannellum 的脚本我使用了这个 repo 中的 executeScript 函数:https://github.com/SharePoint/sp-dev-fx-webparts/blob/dev/samples/react-script-editor/src/webparts/scriptEditor/ScriptEditorWebPart.ts

我想我的问题与此有关:IE + XMLHttp + CreateObjectURL Error。

有人有这方面的经验吗?

这里有一些示例代码:

import  Version  from '@microsoft/sp-core-library';
import 
  BaseClientSideWebPart,
  IPropertyPaneConfiguration,
  PropertyPaneTextField
 from '@microsoft/sp-webpart-base';
import  escape  from '@microsoft/sp-lodash-subset';
import  SPComponentLoader  from '@microsoft/sp-loader';
import styles from './TestWebPart.module.scss';
import * as strings from 'TestWebPartStrings';

require('../../../node_modules/pannellum/build/pannellum.css');
require('../../../node_modules/pannellum/build/pannellum.js');

export default class TestWebPart extends BaseClientSideWebPart<ITestWebPartProps> 
  public render(): void 
    let innerhtml: string = `
    <script src="http://localhost:4321/node_modules/pannellum/build/pannellum.js"></script>
    <script>
      pannellum.viewer('panorama', 
        "type": "equirectangular",
        "panorama": "test2.jpg",
        "autoLoad": true
      );
    </script>
    <style>
      #panorama 
        width: 600px;
        height: 400px;
      
    </style>
    <div id="panorama"></div>
    `;
    this.domElement.innerHTML = innerHTML;
    this.executeScript(this.domElement);
  

  private evalScript(elem) 

    const data = (elem.text || elem.textContent || elem.innerHTML || '');
    const headTag = document.getElementsByTagName('head')[0] || document.documentElement;
    const scriptTag = document.createElement('script');

    scriptTag.type = 'text/javascript';
    if (elem.src && elem.src.length > 0) 
      return;
    
    if (elem.onload && elem.onload.length > 0) 
      scriptTag.onload = elem.onload;
    

    try 
      // doesn't work on ie...
      scriptTag.appendChild(document.createTextNode(data));
     catch (e) 
      // IE has funky script nodes
      scriptTag.text = data;
    

    headTag.insertBefore(scriptTag, headTag.firstChild);
    headTag.removeChild(scriptTag);
  

  private nodeName(elem, name) 
    return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
  

  // Finds and executes scripts in a newly added element's body.
  // Needed since innerHTML does not run scripts.
  //
  // Argument element is an element in the dom.
  private async executeScript(element: HTMLElement) 
    // Define global name to tack scripts on in case script to be loaded is not AMD/UMD

    if (!window['_spPageContextInfo']) 
      window['_spPageContextInfo'] = this.context.pageContext.legacyPageContext;
    

    (<any>window).ScriptGlobal = ;

    // main section of function
    const scripts = [];
    const childrenNodes = element.childNodes;

    for (let i: number = 0; childrenNodes[i]; i++) 
      const child: any = childrenNodes[i];
      if (this.nodeName(child, 'script') &&
        (!child.type || child.type.toLowerCase() === 'text/javascript')) 
        scripts.push(child);
      
    

    const urls = [];
    const onLoads = [];
    for (let i: number = 0; scripts[i]; i++) 
      const scriptTag = scripts[i];
      if (scriptTag.src && scriptTag.src.length > 0) 
        urls.push(scriptTag.src);
      
      if (scriptTag.onload && scriptTag.onload.length > 0) 
        onLoads.push(scriptTag.onload);
      
    

    let oldamd = undefined;
    if (window['define'] && window['define'].amd) 
      oldamd = window['define'].amd;
      window['define'].amd = undefined;
    

    for (let i: number = 0; i < urls.length; i++) 
      try 
        let scriptUrl = urls[i];
        const prefix = scriptUrl.indexOf('?') === -1 ? '?' : '&';
        scriptUrl += prefix + 'cow=' + new Date().getTime();
        await SPComponentLoader.loadScript(scriptUrl,  globalExportsName: 'ScriptGlobal' );
       catch (error) 
        if (console.error) 
          console.error(error);
        
      
    
    if (oldamd) 
      window['define'].amd = oldamd;
    

    for (let i: number = 0; scripts[i]; i++) 
      const scriptTag = scripts[i];
      if (scriptTag.parentNode)  scriptTag.parentNode.removeChild(scriptTag); 
      this.evalScript(scripts[i]);
    
    // execute any onload people have added
    for (let i: number = 0; onLoads[i]; i++) 
      onLoads[i]();
    
  

【问题讨论】:

您能否提供一些小的示例代码,我们可以尝试使用 IE 11 和其他浏览器进行测试以检查结果?它可以帮助我们了解这个问题。代码图像不会帮助我们找到问题。 我添加了一个示例代码。我希望它有所帮助。 IE 不能很好地支持 URL.createObjectURL()。最好使用window.navigator.msSaveOrOpenBlob,对于非ie,我们可以使用URL.createObjectURL() Ref: ***.com/questions/24007073/… and developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL 非常感谢您的提示。你知道我必须修改哪个文件吗?很难找到这方面的细节。 您需要找到所有使用 URL.createObjectURL() 的位置,并且需要在那里使用 window.navigator.msSaveOrOpenBlob。您可以尝试使用JS代码识别浏览器,并尝试针对特定浏览器执行特定代码。 【参考方案1】:

我还没有找到任何解决这个问题的方法。即使是在 IE11 中作为独立解决方案工作的其他全景查看器,也无法在 IE11、SharePoint 2016 和 SPFx Webpart 的组合中工作。

每次它在带有 iframe 的 Sharepoint 中工作。将其部署到 SharePoint 后,由于异常而停止工作。大多数例外都与安全有关。

最有前途的方法是将 Photo Sphere Viewer 作为 iframe。问题是图像必须与其他 js 和 HTML 文件一起进入 CDN 目录。否则会因为安全性和 CORS(跨域资源共享)而出现异常。

【讨论】:

【参考方案2】:

IE 不支持 URLSearchParams 。在您的代码(Polyfill)中添加以下 sn-p,最好在构造函数()中:

(function (w) 

    w.URLSearchParams = w.URLSearchParams || function (searchString) 
        var self = this;
        self.searchString = searchString;
        self.get = function (name) 
            var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(self.searchString);
            if (results == null) 
                return null;
            
            else 
                return decodeURI(results[1]) || 0;
            
        ;
        self.has = function (name) 
            var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
            if (results == null) 
                return false;
            
            else 
                return true;
            
        
    

)(window);

【讨论】:

以上是关于将 SPFx Webpart 与 IE11、SharePoint 2016 和 Pannellum 一起使用的主要内容,如果未能解决你的问题,请参考以下文章

如何在单个 spfx 解决方案中创建两个 webpart?

无法通过 App Catalog 将 SPFx webpart 部署到 sharepoint 2019

使用 ngrok 将 SPFX WebPart 隧道传输到 BrowserStack

在现代站点中添加 SPFx webpart 时的 Javascript

MSTeams 桌面客户端中的 SPFx webpart 引发 UnauthorizedAccessException

如何使用 SharePoint SPFX webpart 构建团队应用程序