为什么带有 beforeInteractive 策略的 next.js Script 标签不加载第三方脚本?

Why does the next.js Script tag with the beforeInteractive strategy don't load thirdParty script?

我试图了解带有策略 beforeInteractive 的 next.js Script 标签是如何工作的。为了进行测试,我只使用了 lodash。但是我一直收到 ReferenceError: _ is not defined。我认为当脚本加载 beforeInteractive 时,它​​应该在我的页面组件中全局可用,因为它从服务器注入到初始 Html 中,我可以在 useEffect 挂钩中使用它来改变 div. 有人可以向我解释为什么它不起作用或我做错了什么吗? 我没有通过 npm 安装它,因为我试图弄清楚它是如何工作的。

我有一个简单的 _document.js 并且我添加了一个 Next.js 脚本标签和这个 _document.js 的 beforeInteractive 策略。 next.js 文档说: 此策略仅在 _document.js 内部有效,旨在加载整个站点所需的脚本(即当应用程序中的任何页面在服务器端加载时将加载脚本)。

import { Html, Head, Main, NextScript } from 'next/document'
import Script from 'next/script'

export default function Document() {
  return (
    <Html>
      <Head />
      <body>
        <Main />
        <NextScript />
        <Script
          src="https://unpkg.com/lodash@4.17.20"
          strategy="beforeInteractive"
        ></Script>
      </body>
    </Html>
  )
}

然后我在 pages 文件夹中有一个简单的页面组件。我添加了 getServerSideProps 函数以使用 ServerSideRendering。

如果您从页面导出名为 getServerSideProps(服务器端呈现)的函数,Next.js 将使用 getServerSideProps 返回的数据在每次请求时预呈现此页面。

import Head from 'next/head';
import {useEffect, useState} from 'react';

const TestComponent = () => {
    const [change,setChange] = useState('not changed');

    useEffect(()=> {
        console.log(_);
        setChange(_.join(['one','two'],' - '));
    });

    return (
        <>
            <Head>
                <title>Test</title>
            </Head>
            <div>{change}</div>
        </>
    );
};

export async function getServerSideProps(context) {
    return {
      props: {},
    }
  }

export default TestComponent;

更新

看来确实是个bug,已经修复但还没有发布 https://github.com/vercel/next.js/discussions/37098

首先,当您可以(并且应该)简单地使用将其安装到 node_modules 时,我几乎没有看到您想要这样做的任何理由。如果库类型不是模块并且下一个配置需要模块,您还可能 运行 捆绑包出现问题的风险。

基于问题的解决方案:

有两种方法。 首先,请参阅关于这件事的 docs

请使用文档中提到的上述方法

如果出于某种原因不能选择...

第二个是不太理想但可行的解决方案。

为您的静态文件创建一个文件夹。例如:<root>/static/js/hello.js。然后在你的 _document 文件中,

<script type="text/javascript" src="/static/hello.js"></script>

抛开您应该将 Lodash 作为节点模块导入这一事实,在 _document 中使用 next/script 似乎确实存在问题(无论外部脚本实际上是什么) .

事实证明这是一个 Next.js 错误,已在 PR in pre-release version v12.1.7-canary.8 中解决。要解决项目中的问题,只需更新到最新的金丝雀版本 (npm install next@canary),或等待稳定的 v12.1.7 版本。


作为替代方法,您可以直接在 _document<Head>defer 属性 中使用 <script> 标签。这与 next/script 的输出非常匹配。

import { Html, Head, Main, NextScript } from 'next/document'

export default function Document() {
    return (
        <Html>
            <Head>
                <script
                    type="text/javascript"
                    src="https://unpkg.com/lodash@4.17.20/lodash.js"
                    defer
                ></script>
            </Head>
            <body>
                <Main />
                <NextScript />
            </body>
        </Html>
    )
}