加载主题组件中的 js 资源时遇到问题

您好,这是我第一次在 Discourse 上进行开发,所以很抱歉提出一些愚蠢的问题。
我正在构建一个主题组件来显示 PGN(国际象棋棋盘和比赛)。
该组件理论上并不难,有一个 JS 组件(PGNViewer.js),我已经能够将 [wrap] 标签转换为正确的 \u003cscript\u003evar x='pgn here'; ...\u003c/script\u003e\u003cdiv class='pgn-blahblah'\u003e\u003c/div\u003e 块。
到目前为止一切顺利,但现在我需要加载 JS。
我发现我可以使用 import loadScript from \"discourse/lib/load-script\";await loadScript(settings.theme_uploads_local.pgnviewer_js); 在初始化时从 /assets/ 目录加载它。
但在这里我遇到了一些问题。
尽管我能够在开发安装中加载该组件,但在我的生产服务器上却不行,因为它说资产太大。有什么方法可以解决这个问题吗?
其次,在开发安装中,我看到一个警告,提示它无法加载 /theme-javascripts/dist.js.map。我不知道它来自哪里。我需要提供什么吗?
总的来说,这种方法是正确的,还是最好实现一个插件?
谢谢
此致
FF

2 个赞

欢迎来到 Meta,@happycactus :wave:

是的,可能吧。你只需要插件的情况是,如果你试图更改 API(即后端)。

我过去也遇到过类似的问题。

是的,你需要更改 app.yml 以允许大文件通过反向代理(nginx)传输。

值得庆幸的是,这是一个简单的设置,upload_size:,这里是上下文:

params:
  ## 此容器应使用哪个 Git 版本? (默认值:tests-passed)
  version: tests-passed

  ## 最大上传大小 (默认值:10m)
  upload_size: 20m

更改此设置后,你需要重建才能生效:

./launcher rebuild app

当然,这假设你对服务器有适当的访问权限…

2 个赞

谢谢你,罗伯特!

我有些疑问,可能是因为我在这类平台上没什么经验。
我的疑问是:

我的方法是“嵌入”所需的少量 JavaScript 代码来填充 div 块的内容,但我看到也许可以从一个名为“cookPgn”的函数中执行它,就像在 marmaid 组件 中那样:

async function applyMermaid(element, key = "composer") {
  const mermaids = element.querySelectorAll("pre[data-code-wrap=mermaid]");
  ...
  await loadScript(settings.theme_uploads_local.mermaid_js);
  ...
  mermaids.forEach((mermaid) => {
    if (mermaid.dataset.processed) {
      return;
    }

    const spinner = document.createElement("div");
    spinner.classList.add("spinner");
    ...
    mermaid.append(spinner);
  });

这样的话,如果我理解得没错,我就不需要从页面加载脚本(也许使用 loadScript??),而是可以直接在主题 JS 中导入它?或者我可能完全搞错了!
我的意思是,loadScript 和简单的 import 有什么区别?
你能给我推荐一些可以更好地理解这个问题的网络资源吗?
如果我的问题很幼稚或愚蠢,再次抱歉!

还有一件事,默认限制是 10M,但我的资源大约是 300kb,整个 zip 文件是 337kb,但在上传过程中它显示超过 512kb。

谢谢,

FF

你试过 import 吗?我认为你不能 import 一个外部脚本。

Loadscript 是正确的方法,它能确保脚本在你的代码继续执行并使用它之前完全加载(这就是为什么它返回一个 promise)。

你能分享确切的错误信息吗?是在浏览器控制台中吗?

您真正的问题是尝试使用内联 JS,这被 CSP 禁止。

将此代码放入主题组件代码的辅助函数中,不要将其注入帖子内。

将您当前的主题上传到 GitHub,我们也可以查看一下。

5 个赞

看看 Discourse Mermaid 组件,它会加载一个库并对帖子执行一些操作。

2 个赞

谢谢,@Falco,这也是我的疑问之一。

当然,在这里:GitHub - studiofuga/discourse-pgn-component: A theme component for Discourse to display PGN blocks
还有一个插件版本,但我认为我不会走这条路,而是坚持使用主题组件。

谢谢 @pfaffman,确实我从中获得了灵感,并且我注意到它正是我想做的,并且是以正确的方式完成的,而不是像上面建议的那样注入代码。

谢谢大家,我会尝试你们的建议并带着结果回来。当然我的代码是开源的,所以任何贡献或批评都非常欢迎。

1 个赞

是我的错,我忘了我将附件大小限制为 512kB,将其提高到默认值 (4096) 后效果很好。谢谢,也很抱歉问了这么愚蠢的问题。

2 个赞

好的,我已经修复了所有问题,组件也能正常工作了!但我们遇到了一个问题,“不安全求值”(unsafe eval)出现在我正在使用的库中。我猜我应该修复上游库,但我不知道该如何打包它。
有兴趣的人可以看看,我把我所有的更改都推到了上面的仓库里,这是错误的详细信息。

这是原始 JS 模块中的这一行(在原始 js 模块上)

let isBrowser=new Function("try {return this===window;}catch(e){ return false;}")

这里

1 个赞

您的脚本在生产环境中因内容安全策略而中断。

您需要在站点设置 content security policy script src 中添加 'unsafe-eval' 作为一项。

谢谢,我正在浏览文档,看看如何做到这一点。 :slight_smile: 我学到了很多东西,谢谢。

1 个赞

好的,我做到了,只是我需要使用 ’ … 哈哈… 无论如何,我的脚本几乎成功了,我把所有东西都推到了 main 分支,而且它不知何故奏效了,似乎我在渲染方面有问题,但至少主组件被调用了(我怀疑问题就在这里)。
谢谢大家……我很快会带着更多问题回来的,哈哈。谢谢!

1 个赞

请记住,在 CSP 中添加 unsafe-eval 基本上消除了 CSP 的全部意义,并因此降低了您网站的安全性。

使用 eval 是一种代码异味,正如您在上面指出的那样,应该在上游修复。

4 个赞

是的,绝对可以。我目前无法修复它,但我的计划是先修复我的组件,然后修复上游库并删除 CSP 覆盖。\n感谢您的建议!

我快完成了。我能够显示“something”,但它是随机工作的。
我正在使用的 JS 库的工作方式如下:

首先,我们需要定义一个具有唯一 ID 的 <div id='board'> 块。然后,我们需要调用一个函数来填充该 div,类似这样:PGNV.pgnBoard('board', {});。脚本需要相同的 ID 来填充它。

如果我理解正确,在装饰已完成的块时,我无法做到这一点,因为文档尚未填充,并且 PGNV 对象无法找到该块。所以我先使用了一个小部件(不起作用,因为 HTML 函数必须返回块,而我没有它),然后我切换到一个防抖函数:debounceFunction(this, renderPgn, attrs, 200);

但它“有时”有效。

我错过了什么?

谢谢。

debounceFunction 会延迟调用目标方法,直到在没有其他 debounce 调用时过去 debounce 周期,它有时可能仍然因为 \n\n[quote=“HappyCactus, post:15, topic:279792, username:happycactus”]\n文档尚未填充,PGNV 对象无法找到块\n[/quote]\n\ndebounce 可能不是您用例的解决方案\n\n\n[quote=“HappyCactus, post:15, topic:279792, username:happycactus”]\n在装饰已渲染的块时我无法做到这一点,因为文档尚未填充,PGNV 对象无法找到块\n[/quote]\n\n\n尝试使用 afterAdopt 选项\n\ndiscourse/app/assets/javascripts/discourse/app/lib/plugin-api.js at 2a10ea0e3f858f243330edce5a07015df72d7f88 · discourse/discourse · GitHub

1 个赞

嗨 Hawn,感谢你的提示!
这似乎正是我想要的。我尝试了两种不同的方法:

  • 首先使用普通的“decorateCooked”来解析并附加 div 块,然后使用 afterAdopt 来调用 PGNV.pgnviewer() 进行渲染,或者
  • 只使用 afterAdopt 选项来同时附加和渲染

但由于另一个我无法完全理解的问题,它们都还没有奏效,似乎内部库因错误而崩溃,据称在 DOM 元素中找不到 className 属性。我有点迷茫 :slight_smile: 。我看到有人使用了 loadScript,但我只是导入了库,并且 debounce 函数就能正常工作。这两种方法有什么区别?

谢谢!

您好 @hawm,我找到了原因,但卡住了。
当然,我对 Ember 和最近的 JavaScript 都很陌生。所以我研究了一下,找到了原因。
PgnViewerJs 期望在真实 DOM 上工作,而不是在虚拟 DOM 上工作。因此,即使在 cooked 元素被采用后,它也不存在于真实 DOM 中,而 PGNV 使用 document.getElementById 来搜索 ID。
我研究了我能找到的一切:Ember 组件似乎符合我的需求,但我不太确定如何处理它。
或者也许是 API.onAppEvent

谢谢。

我使用了 ember 的 later(),虽然这是一个粗糙的技巧,但它有效。
有什么更好的解决方案吗?

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.