首页 > 解决方案 > 边缘侧包括自闭合标签

问题描述

我有一个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。所以还是没有解决办法...

标签: reactjswebpackbabeljsnext.jsedge-side-includes

解决方案


推荐阅读