跳转至

元素扩展开发人员指南

扩展是一种向现有元素添加自定义逻辑和额外复杂性的方法。每个元素都能够加载相关扩展,因此,扩展应该针对一个特定元素进行定制。

伸展解剖

扩展只能在课程中定义,位于 [course directory]/elementExtensions 中。该文件夹的组织方式使得元素包含为子文件夹,然后扩展包含在元素文件夹内。例如,如果您的扩展名 exampleExtension 扩展了 exampleElement,则文件夹结构将为:[course directory]/elementExtensions/exampleElement/exampleExtension

每个扩展只需要一个包含有关元素的元数据的 info.json,并且可以包含可选信息,例如 Python 脚本、CSS 样式和客户端 JavaScript 文件。

info.json 文件在结构上与元素信息文件类似,可能包含以下字段:

info.json
{
  "controller": "Python script",
  "dependencies": {
    "nodeModulesStyles": ["style_file_path"],
    "nodeModulesScripts": ["script_file_path"],
    "clientFilesCourseStyles": ["style_file_path"],
    "clientFilesCourseScripts": ["script_file_path"],
    "extensionStyles": ["style_file_path"],
    "extensionScripts": ["script_file_path"]
  },
  "dynamicDependencies": {
    "nodeModulesScripts": { "module_name": "module_path" },
    "clientFilesCourseScripts": { "file_name": "file_path" },
    "extensionScripts": { "file_name": "file_path" }
  }
}

有关可以添加到此文件的内容的更多信息,请参阅infoElementExtension.json 的参考

Python控制器

主 Python 控制器脚本没有通用结构,而是由正在扩展的元素定义。任何全局函数和变量都可供宿主元素使用。

主机元素可以调用 load_extension() 函数来加载一个特定的扩展,或者调用 load_all_extensions() 来加载所有可用的内容。这些在自由格式 prairielearn 模块中定义。

元素及其扩展之间存在双向的逻辑和信息流。

从元素导入扩展

加载扩展 Python 脚本会返回所有全局定义的函数和变量的命名元组。加载所有扩展将返回一个将扩展名称映射到其命名元组的字典。例如,如果扩展要在其控制器中定义以下内容:

def my_cool_function():
    return "hello, world!"

然后,宿主元素可以通过运行以下命令来调用它:

import prairielearn as pl

def render(element_html, data):
    extension = pl.load_extension(data, "extension_name")
    contents = extension.my_cool_function()
    return contents

上面的这个小例子会将 "hello world!" 渲染到问题页面。请注意,当使用 load_all_extensions() 加载所有扩展时,模块按字母升序返回。

从扩展导入主机元素

扩展还可以使用 load_host_script() 函数从其主机元素导入文件。这可用于获取辅助函数、类定义、常量变量等。 例如,如果宿主元素包含:

import prairielearn as pl

STATIC_VARIABLE = "hello"

def render(element_html, data):
    extension = pl.load_extension(data, "extension_name")
    contents = extension.my_cool_function()
    return contents

然后,扩展程序可以通过导入主机脚本来访问 STATIC_VARIABLE

import prairielearn as pl

host_element = pl.load_host_script("pl-host-element.py")

def my_cool_function():
    return host_element.STATIC_VARIABLE

扩展依赖项

与问题和元素可能需要客户端资产(如元素开发人员指南 中所述)类似,扩展也可能需要客户端 JavaScript 和 CSS。这里总结了不同的属性。请注意,脚本依赖项可以设置为静态或动态依赖项,而样式只能设置为静态依赖项。

物业 描述
nodeModulesStyles 此扩展所需的样式,相对于 [ZJUI-Learn directory]/node_modules
nodeModulesScripts 此扩展所需的脚本,相对于 [ZJUI-Learn directory]/node_modules
extensionStyles 该元素所需的样式相对于扩展的目录 [course directory]/elementExtensions/element-name/extension-name
extensionScripts 该元素所需的脚本相对于扩展的目录 [course directory]/elementExtensions/element-name/extension-name
clientFilesCourseStyles 此扩展所需的样式相对于[course directory]/clientFilesCourse
clientFilesCourseScripts 此扩展所需的脚本相对于 [course directory]/clientFilesCourse

请注意,在 dependencies 中声明的任何元素扩展资源都将始终被加载,无论其 Python 控制器是否已加载。因此,建议在合适的情况下,根据元素的上下文/用法,扩展仅在必要时才使用 dynamicDependencies 加载脚本。

Warning

请记住,应避免节点模块依赖项,因为它们可能会在没有警告的情况下进行更新,这在某些情况下可能会破坏您的扩展。更多信息可以在元素开发者指南中找到。

其他客户端文件

还可以加载客户端可用的其他文件,例如图像或任何可下载内容。这些客户端文件应放置在扩展目录中的 clientFilesExtension 中,并且该文件夹的完整 URL 会提供给 data["options"]["client_files_extensions_url"][extension_name] 中的主机扩展。如果扩展本身需要此路径,则可以将其作为参数传递给已定义的扩展函数。