我有一个nextjs(v7)构建的应用程序,我们正在尝试向其添加一些 Edge Side Include 标签:
<esi:include src="https://example.com/head" />
<esi:include src="https://example.com/menu" />
<esi:include src="https://example.com/footer" />
我们已将这些添加到我们的自定义 _document.js(所有 nextjs/react 编译页面都使用)中,如下所示:
import Document, { Head } from 'next/document'
import React from 'react'
export default class MyDocument extends Document {
render () {
const { html } = this.props
return (
<html>
<Head>
React.createElement('esi:include', { src: 'https://example.com/head }, null)}
</Head>
<body>
React.createElement('esi:include', { src: 'https://example.com/menu }, null)}
<div id='__next' dangerouslySetInnerHTML={{ __html: html }} />
React.createElement('esi:include', { src: 'https://example.com/footer }, null)}
</body>
</html>
)
}
}
问题是正在编译/转换的内容是将结束标记添加到 esi:include 元素中,而不是将它们保留为 void 样式的自结束标记元素:
<html>
<head>
... metas, titles
<esi:include src="https://example.com/head" class="next-head"></esi:include>
</head>
<body>
... stylesheets and preload links
<div>
<esi:include src="https://example.com/menu"></esi:include>
</div>
<div id="__next">
... main app container
<esi:include src="https://example.com/footer"></esi:include>
</div>
</body>
</html>
我认为这可能没问题,边缘服务器应该希望能同样识别它,但是不行,因为边缘服务器必须有一个自结束标签(根据规范中的第 3.1 节)才能找到它并执行代码注入。
我们尝试使用一些危险的SetInnerHTML 解决方案,但同样的事情发生了,结束标签被添加,而不是保持自我结束标签原样。因此,我怀疑这与我们在 JSX 代码中的操作方式无关,而是发生在 webpack 中的某个地方,babel 编译/转译以使其有效 html5,但我不确定如何解决它。似乎要求包含标签自关闭的 ESI 规范和只允许一个小的指定集合为无效(自关闭)元素的HTML5规范是不兼容的。
关于如何配置 Webpack 或 Babel 以保留自闭合标签的任何想法?提前感谢您的帮助!
----- 更新的部分工作 -----
通过将 3 个静态 html 文件添加到为 nextjs/react 应用程序提供服务的 express 服务器的指定静态文件夹中,我们已成功实现了此问题的部分解决方法。这 3 个文件(head.html、menu.html 和 foot.html)然后由添加到自定义 _document.js 的一些客户端 javascript 注入,如下所示:
import Document, { Head } from 'next/document'
import React from 'react'
export default class MyDocument extends Document {
render () {
const { html } = this.props
return (
<html>
<Head>
</Head>
<body>
<div id='__next' dangerouslySetInnerHTML={{ __html: html }} />
<script>
var esiHeadReq = new XMLHttpRequest();
esiHeadReq.open('GET', '/static/esi-head.html', true);
esiHeadReq.send(null);
esiHeadReq.onreadystatechange = function () {
if (esiHeadReq.readyState === 4) {
document.head.insertAdjacentHTML('beforeend', esiHeadReq.responseText);
}
}
var esiMenuReq = new XMLHttpRequest();
esiMenuReq.open('GET', '/static/esi-menu.html', true);
esiMenuReq.send(null);
esiMenuReq.onreadystatechange = function () {
if (esiMenuReq.readyState === 4) {
document.body.insertAdjacentHTML('afterbegin', esiMenuReq.responseText);
}
}
var esiFootReq = new XMLHttpRequest();
esiFootReq.open('GET', '/static/esi-foot.html', true);
esiFootReq.send(null);
esiFootReq.onreadystatechange = function () {
if (esiFootReq.readyState === 4) {
document.body.insertAdjacentHTML('beforeend', esiFootReq.responseText);
}
}
</script>
</body>
</html>
)
}
}
这样做的原因是边缘服务器现在可以将页眉/菜单/页脚代码正确地注入到未干扰(没有 webpack 或 babel 转译)的 html 文件中,但是当我们添加的脚本将代码插入到反应页面使用的主 DOM 中时它没有在某些插入的 javascript 中正确绑定某些行为,因此菜单实际上不起作用。这可能是由于在完成所有其他渲染和 JS 绑定之后使用 insertAdjacentHTML 将其添加到 DOM。所以还是没有解决办法...