即将到来的核心更改可能破坏一些主题/组件 (4月12日)

下周我将合并 这个 PR,它允许主题和组件包含 QUnit 测试。不过,该 PR 也改变了 Discourse 处理/转译主题 JavaScript 的方式。在不重构大量核心代码的情况下以向后兼容的方式实现这些变更非常困难(而这本身也可能引入其他不兼容的变更),因此升级站点时,这些变更可能会导致您的主题或组件的 JavaScript 代码失效。

在本文中,我将解释哪些变更可能影响您的主题或组件,以及您需要采取哪些措施来修复它们。

1. 位于 <script type="text/discourse-plugin"> 标签内的 JavaScript 将以启用 严格模式 的方式执行

此变更不会影响位于 <script type="text/discourse-plugin"> 标签之外的 JS 代码。如果您的代码位于普通的 <script> 标签中或独立的 .js 文件中,则完全不受此变更影响。

判断您的主题或组件是否受此变更影响的最简单方法,是将您的 JS 代码包裹在一个 立即执行函数表达式 (IIFE) 中,并在代码顶部添加 "use strict";。例如,假设您的主题代码目前如下所示:

<script type="text/discourse-plugin" version="0.8.11">
  a = 5;
  console.log(a);
</script>

将其包裹在 IIFE 中后,代码应如下所示("use strict"; 很重要,因为它启用了严格模式,我们需要测试代码在严格模式下的行为):

<script type="text/discourse-plugin" version="0.8.11">
  (function() {
    "use strict";
    a = 5;
    console.log(a);
  })();
</script>

如果您执行此操作后组件停止工作,那么升级站点时它将会失效。为了修复代码,我强烈建议您先阅读 MDN 文档 中关于 JavaScript 严格模式的内容,然后检查您的主题或组件是否执行了严格模式下禁止的操作。如果是,您需要重构代码以消除这些违规操作。

您最可能遇到的错误是:在声明变量时未使用 var(或 let/const)关键字导致的 ReferenceError。在上面的示例中,a = 5; 这一行在严格模式下会抛出 ReferenceError 异常,因为我们忘记添加 var。修复后的代码如下:

<script type="text/discourse-plugin" version="0.8.11">
  (function() {
    "use strict";
    var a = 5;
    console.log(a);
  })();
</script>

完成代码的测试和修复后,您可以随意移除 IIFE 和 "use strict"; 行。

2. 主题 JavaScript 模块的路径将添加主题 ID 作为前缀

之前我们添加了一个新功能,允许将主题的 JavaScript 拆分为多个文件。为了说明接下来的变更,我需要简要介绍一下该功能的背景。

当随附独立 JavaScript 文件的主题或组件安装在 Discourse 实例上时,Discourse 会遍历所有 JavaScript 文件,并为每个文件创建一个 JavaScript 模块。每个模块都需要一个唯一标识符(即路径),因此 Discourse 使用文件路径(经过少量修改)作为模块路径。

例如,如果您的主题或组件在 javascripts/discourse/helpers/my-helper.js 处有一个文件,Discourse 将为该文件创建一个模块,并将其路径指定为 discourse/helpers/my-helper,该模块将包含原始文件中 JavaScript 的转译版本。

模块的好处在于,您可以将一个模块中的类/函数/对象等导入到另一个模块中。例如,您可以使用如下 import 语句,将名为 xyz 的函数从 my-helper 导入到其他模块:

// javascripts/discourse/controllers/my-theme-controller.js

import { xyz } from "discourse/helpers/my-helper";

我下周将要合并的 PR 将为主题模块路径添加前缀。因此,在我们的示例中,my-helper 的路径将从 discourse/helpers/my-helper 变为 discourse/theme-<theme_id>/helpers/my-helper。这意味着我们的 import 语句将失效,因为模块路径已更改。要修复此问题,我们只需将 import 语句中的路径从绝对路径改为相对路径,如下所示:

// javascripts/discourse/controllers/my-theme-controller.js

import { xyz } from "../helpers/my-helper";

现在我们的 import 语句应该可以正常工作了。请参阅这些 PR 12,其中展示了受变更 (2) 影响的组件的实际示例及其修复方法。

再次强调,此变更仅影响从自身模块导入的主题或组件;导入核心模块不受此变更影响

希望这能帮到您。如果您有任何问题,请随时告诉我!

31 个赞

感谢您详细的说明 :slight_smile:

这会影响主题组件中使用的插件里的“绝对”文件路径吗?例如,与 layouts 插件 配合使用的主题组件,都需要像下面这样引用 layouts 插件本身的辅助文件:

requirejs('discourse/plugins/discourse-layouts/discourse/lib/layouts')

例如,请查看 layouts 分类列表小部件

看起来这里的路径变更使其与插件资源管道中的资源命名空间保持一致(使用主题 ID 而非插件名称),并且在主题组件中使用的插件资源路径将保持不变。像上面那样的 require 语句应该仍然有效。是这样吗?

7 个赞

是的,没错 :+1:

6 个赞

@loginerror,我觉得你会发现这个主题很有用 :wink:

7 个赞

@Terrapop
我认为你对此曾有过顾虑。

5 个赞

我们在此代码被限时推送之前就已经修复了。

5 个赞