Javascript : 在定义的命名空间内评估外部脚本

Javascript : Eval external script within defined namespace

抱歉这篇文章,找不到简短的方法来解释这个:(

上下文

我目前正在为 Office 365 构建一个 WebPart,它非常像一个客户端应用程序,用户可以将其添加到他们的页面并通过 UI.

配置

WebPart 呈现一些内容,我希望用户能够提供一个外部脚本,该脚本可以 运行 在呈现过程之后。到目前为止这很容易,我可以简单地在 WebPart 代码中做这样的事情:

// Render the WebPart
this.render();

// Load external script
this.loadExternalScript(this.props.externalScriptUrl);

问题是我希望外部脚本像回调一样工作,我的 WebPart 可以调用它来为其提供一些上下文。

解决方案 1

我找到的第一个解决方案是指导用户如何使用基于文件名的特定命名空间创建他们的外部脚本,以及一个精确的函数,这样我的 WebPart 就可以:

效果很好,看起来像这样:

MyExternalScript.js

MyNamespace.ExternalScripts.MyExternalScript = {

    onPostRender: function(wpContext) {
        console.log(wpContext);
    }
}

WebPart

// Render the WebPart
this.render();

// Load external script
this.loadExternalScript(this.props.externalScriptUrl);

// Calls the script callback if available
var scriptNamespace = MyNamespace.ExternalScripts.MyExternalScript;
var scriptCallback = scriptNamespace  ? scriptNamespace.onPostRender : null;

if(scriptCallback) {
    scriptCallback(this.wpContext);
}

这很好用,但要求用户根据文件名构建命名空间是一件很粗略的事情,我很想找到比这个 hacky 解决方案更直接的东西。

解决方案 2

我想到的另一个解决方案是执行以下操作:

这会沿着这些方向看:

MyExternalScript.js

onPostRender: function(wpContext) {
    console.log(wpContext);
}

WebPart

// Render the WebPart
this.render();

// Load external script content
$scriptContent = this.readExternalScript(this.props.externalScriptUrl);

// Append unique namespace
$scriptContent = "MyNamespace.ExternalScripts.MyExternalScript = {" + $scriptContent + "}";

// Eval everything within that namespace
eval($scriptContent);

// Calls the script callback if available
var scriptNamespace = MyNamespace.ExternalScripts.MyExternalScript;
var scriptCallback = scriptNamespace  ? scriptNamespace.onPostRender : null;

if(scriptCallback) {
    scriptCallback(this.wpContext);
}

我做了一些快速测试,看起来它在工作,事实上 WebPart 动态生成命名空间比要求用户遵守复杂的命名空间要好得多,但是我不确定是否有更好的方法解决方案比使用 eval().

一天结束时我需要做的就是找到一种方法使我的 WebPart "aware" 成为它需要调用的回调。我还必须确保命名空间是 WebPart 唯一和脚本唯一的,因为同一页面上可能有 4 个 WebPart,加载不同的脚本,所以我必须不惜一切代价避免命名空间冲突。

有人有更好的主意吗?

谢谢!

我不太明白这里的上下文,但是将 post 渲染函数作为回调传入怎么样?

var runExternalScript = Function('onPostRender', `
// your external script
  console.log('rendering...');
  console.log('rendering finished');
  if (onPostRender) onPostRender();
`)

function postCallback(){ console.log('finished!') }

runExternalScript(postCallback)

因此,正如我在评论中所解释的那样,主要目标是能够执行外部脚本,该脚本可以访问 WebPart 提供的 "magic" 变量 "wpContext"。

外部脚本called/evaluated的时机并不重要,因为由 WebPart 决定何时调用它。

按照你的例子,我认为它看起来更像这样:

WebPart

// Render the WebPart
this.render();

// Load the external script
var runExternalScript = Function('wpContext', `
  // External script content
  console.log('External script is able to use the WebPart context!');
  console.log(wpContext);
`);

// Run the external script while providing the current WebPart's context
runExternalScript(this.wpcontext)

这比我的 eval() 优雅得多,因为我没有要生成的任何命名空间,而且外部脚本甚至不必遵守特定的函数签名,WebPart 就可以检索它,它可以直接在其内容中直接使用 "wpContext" 变量。

因此,即使这看起来是更好的解决方案,我是否还缺少另一个不需要使用 eval/Function 的解决方案,或者这就是可行的方法?

谢谢!