js.Batch
语法
js.Batch [ID]
返回值
js.Batcher
Batch ID
用于为此批次创建基础目录。允许使用正斜杠。js.Batch
返回一个对象,该对象具有以下结构的 API
Group
Group
方法接受一个 ID
(string
) 作为参数。没有斜杠。它返回一个具有这些方法的对象
Script
Script
方法接受一个 ID
(string
) 作为参数。没有斜杠。它返回一个OptionsSetter,可用于为此脚本设置脚本选项。
{{ with js.Batch "js/mybatch" }}
{{ with .Group "mygroup" }}
{{ with .Script "myscript" }}
{{ .SetOptions (dict "resource" (resources.Get "myscript.js")) }}
{{ end }}
{{ end }}
{{ end }}
SetOptions
接受一个脚本选项映射。 请注意,如果您希望脚本由运行器处理,则需要设置 export
选项以匹配您想要传递给运行器的内容(默认值为 *
)。
Instance
Instance
方法接受两个 string
参数 SCRIPT_ID
和 INSTANCE_ID
。没有斜杠。它返回一个OptionsSetter,可用于为此实例设置参数选项。
{{ with js.Batch "js/mybatch" }}
{{ with .Group "mygroup" }}
{{ with .Instance "myscript" "myinstance" }}
{{ .SetOptions (dict "params" (dict "param1" "value1")) }}
{{ end }}
{{ end }}
{{ end }}
SetOptions
接受一个参数选项映射。实例选项将作为 JSON 传递给同一组中的任何运行器脚本。
Runner
Runner
方法接受一个 ID
(string
) 作为参数。没有斜杠。它返回一个OptionsSetter,可用于为此运行器设置脚本选项。
{{ with js.Batch "js/mybatch" }}
{{ with .Group "mygroup" }}
{{ with .Runner "myrunner" }}
{{ .SetOptions (dict "resource" (resources.Get "myrunner.js")) }}
{{ end }}
{{ end }}
{{ end }}
SetOptions
接受一个脚本选项映射。
运行器将接收一个数据结构,其中包含该组的所有实例,并且对已定义的 export
的JavaScript 导入具有实时绑定。
运行器脚本的导出必须是一个接受一个参数(组数据结构)的函数。JSON 格式的组数据结构示例如下
{
"id": "leaflet",
"scripts": [
{
"id": "mapjsx",
"binding": JAVASCRIPT_BINDING,
"instances": [
{
"id": "0",
"params": {
"c": "h-64",
"lat": 48.8533173846729,
"lon": 2.3497416090232535,
"r": "map.jsx",
"title": "Cathédrale Notre-Dame de Paris",
"zoom": 23
}
},
{
"id": "1",
"params": {
"c": "h-64",
"lat": 59.96300872062237,
"lon": 10.663529183196863,
"r": "map.jsx",
"title": "Holmenkollen",
"zoom": 3
}
}
]
}
]
}
以下是使用 React 渲染元素的运行器脚本的示例。请注意,导出 (default
) 必须与脚本选项中的 export
选项匹配(default
是运行器脚本的默认值)(此页面上示例的可运行版本可以在js.Batch 演示存储库中找到)
import * as ReactDOM from 'react-dom/client';
import * as React from 'react';
export default function Run(group) {
console.log('Running react-create-elements.js', group);
const scripts = group.scripts;
for (const script of scripts) {
for (const instance of script.instances) {
/* This is a convention in this project. */
let elId = `${script.id}-${instance.id}`;
let el = document.getElementById(elId);
if (!el) {
console.warn(`Element with id ${elId} not found`);
continue;
}
const root = ReactDOM.createRoot(el);
const reactEl = React.createElement(script.binding, instance.params);
root.render(reactEl);
}
}
}
Config
返回一个OptionsSetter,可用于为批处理设置构建选项。
这些选项与js.Build 的选项基本相同,但请注意
targetPath
会自动设置(可能存在多个输出)。format
必须为esm
,目前唯一支持代码拆分的格式。params
将在脚本中的@params/config
命名空间中可用。通过这种方式,您可以同时导入脚本或运行器参数和配置参数,使用
import * as params from "@params";
import * as config from "@params/config";
设置批处理的 Config
可以从任何模板(包括短代码模板)完成,但只会设置一次(第一个设置会生效)
{{ with js.Batch "js/mybatch" }}
{{ with .Config }}
{{ .SetOptions (dict
"target" "es2023"
"format" "esm"
"jsx" "automatic"
"loaders" (dict ".png" "dataurl")
"minify" true
"params" (dict "param1" "value1")
)
}}
{{ end }}
{{ end }}
选项
构建选项
- format
- (
string
)目前,ESBuild 的代码拆分中仅支持esm
。
- params
- (
map
或slice
)可以在 JS 文件中作为 JSON 导入的参数,例如
{{ $js := resources.Get "js/main.js" | js.Build (dict "params" (dict "api" "https://example.org/api")) }}
然后在您的 JS 文件中
import * as params from '@params';
请注意,这适用于小型数据集,例如配置设置。对于较大的数据,请将文件放入/挂载到 assets
并直接导入它们。
- minify
- (
bool
)让js.Build
处理压缩。 - loaders
- (
map
)v0.140.0 新增 为给定的文件类型配置加载器,可以使用 import 语句或 require 调用加载该文件类型。例如,将 .png 文件扩展名配置为使用 data URL 加载器,意味着导入 .png 文件会得到一个包含该图像内容的 data URL。可用的加载器有none
、base64
、binary
、copy
、css
、dataurl
、default
、empty
、file
、global-css
、js
、json
、jsx
、local-css
、text
、ts
、tsx
。请参阅 https://esbuild.org.cn/api/#loader - inject
- (
slice
)此选项允许您自动将全局变量替换为从另一个文件导入的内容。路径名必须相对于assets
。请参阅 https://esbuild.org.cn/api/#inject - shims
- (
map
)此选项允许您将一个组件替换为另一个组件。一个常见的用例是在生产环境中从 CDN 加载 React 等依赖项(使用 *shims*),但在开发期间使用完整的捆绑node_modules
依赖项运行。
{{ $shims := dict "react" "js/shims/react.js" "react-dom" "js/shims/react-dom.js" }}
{{ $js = $js | js.Build dict "shims" $shims }}
shim 文件可能如下所示:
// js/shims/react.js
module.exports = window.React;
// js/shims/react-dom.js
module.exports = window.ReactDOM;
使用以上配置,这些导入在两种情况下都应该有效:
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
- target
- (
string
)语言目标。以下选项之一:es5
、es2015
、es2016
、es2017
、es2018
、es2019
、es2020
或esnext
。默认值为esnext
。 - platform v0.140.0 新增
- (
string
)以下选项之一:browser
、node
、neutral
。默认值为browser
。请参阅 https://esbuild.org.cn/api/#platform - externals
- (
slice
)外部依赖项。使用此选项来修剪您知道永远不会执行的依赖项。请参阅 https://esbuild.org.cn/api/#external - defines
- (
map
)允许定义一组在构建时执行的字符串替换。应为一个 map,其中每个键都将被其值替换。
{{ $defines := dict "process.env.NODE_ENV" `"development"` }}
- sourceMap
- (
string
)是否从 esbuild 生成inline
、linked
或external
源代码映射。链接和外部源代码映射将被写入目标文件,文件名加上 “.map”。当使用linked
时,sourceMappingURL
也会被写入输出文件。默认情况下,不会创建源代码映射。请注意,linked
选项是在 Hugo 0.140.0 中添加的。 - sourcesContent v0.140.0 新增
- (
bool
)是否在源代码映射中包含源文件的内容。默认情况下,此值为true
。 - JSX v0.124.0 新增
- (
string
)如何处理/转换 JSX 语法。以下选项之一:transform
、preserve
、automatic
。默认值为transform
。值得注意的是,automatic
转换是在 React 17+ 中引入的,它将导致自动导入必要的 JSX 辅助函数。请参阅 https://esbuild.org.cn/api/#jsx - JSXImportSource v0.124.0 新增
- (
string
)要使用哪个库自动从中导入其 JSX 辅助函数。仅当JSX
设置为automatic
时,此选项才有效。指定的库需要通过 npm 安装,并公开某些导出。请参阅 https://esbuild.org.cn/api/#jsx-import-source
如果您想使用非 React JSX 库(如 Preact),则 JSX
和 JSXImportSource
的组合很有用,例如:
{{ $js := resources.Get "js/main.jsx" | js.Build (dict "JSX" "automatic" "JSXImportSource" "preact") }}
通过以上配置,您可以使用 Preact 组件和 JSX,而无需每次都手动导入 h
和 Fragment
。
import { render } from 'preact';
const App = () => <>Hello world!</>;
const container = document.getElementById('app');
if (container) render(<App />, container);
脚本选项
- resource
- 要构建的资源。这可以是文件资源或虚拟资源。
- export
- 要将运行器绑定到的导出。将其设置为
*
以导出整个命名空间。对于运行器脚本,默认值为default
;对于其他脚本,默认值为*
。 - importContext
- 用于解析导入的附加上下文。Hugo 将始终首先检查此上下文,然后再回退到
assets
和node_modules
。此选项的常见用法是解析页面包内的导入。请参阅导入上下文。 - params
- 将作为 JSON 传递给脚本的参数映射。这些参数将绑定到
@params
命名空间。
import * as params from '@params';
脚本选项
参数选项
- params
- 将作为 JSON 传递给脚本的参数映射。
导入上下文
默认情况下,Hugo 将首先尝试解析assets中的任何导入,如果找不到,则让 ESBuild 解析它(例如,从 node_modules
)。importContext
选项可用于设置解析导入的第一个上下文。此选项的常见用法是解析页面包内的导入。
{{ $common := resources.Match "/js/headlessui/*.*" }}
{{ $importContext := (slice $.Page ($common.Mount "/js/headlessui" ".")) }}
您可以传递任何实现 Resource.Get 的对象。传递一个 slice 以设置多个上下文。
上面的示例使用 Resources.Mount
来解析相对于页面包的 assets
内的目录。
OptionsSetter
OptionsSetter
是一个只返回一次的特殊对象。这意味着您应该使用 with 将其包裹起来。
{{ with .Script "myscript" }}
{{ .SetOptions (dict "resource" (resources.Get "myscript.js"))}}
{{ end }}
Build
Build
方法返回一个具有以下结构的对象:
- Groups (map)
每个 Resource
的媒体类型都将为 application/javascript
或 text/css
。
在模板中,您通常会使用给定的 ID
(例如,当前部分的脚本)处理一个组。由于并发构建,这需要在 templates.Defer
块中完成。
{{ $group := .group }}
{{ with (templates.Defer (dict "key" $group "data" $group )) }}
{{ with (js.Batch "js/mybatch") }}
{{ with .Build }}
{{ with index .Groups $ }}
{{ range . }}
{{ $s := . }}
{{ if eq $s.MediaType.SubType "css" }}
<link href="{{ $s.RelPermalink }}" rel="stylesheet" />
{{ else }}
<script src="{{ $s.RelPermalink }}" type="module"></script>
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
已知问题
在 ESBuild 代码拆分的官方文档中,标题中有一个警告注释。这两个问题是:
esm
当前是唯一实现的输出格式。这意味着它不适用于非常旧的浏览器。请参阅 caniuse。- 存在已知的导入排序问题。
在我们对这项新功能使用不同库进行的广泛测试中,我们没有发现排序问题。主要有两种情况:
许多人会说以上两者都是代码异味。第一个问题在 Hugo 中有一个简单的解决方法。在其自己的脚本中定义导入顺序,并确保它尽早传递给 ESBuild,例如,将其放入一个名称在字母表中靠前的脚本组中。
import './lib2.js';
import './lib1.js';
console.log('entrypoints-workaround.js');