对 Docusaurus 进行上游操作,确保加载文件的顺序正确

Undertake operations upstream to Docusaurus and ensure the correct order of loading files

我要接手 a website by V2 Docusaurus

我们网站的一个特殊性是我们需要加载office.js and css-vars-ponyfill.min.js,并在一开始就进行一些操作。所以之前的开发者决定使用下面的方法。

在每个.mdx.md页面中,他用一个组件包裹内容MainWrapper:

<MainWrapper>
    ... ...
    Real content
    ... ...
</MainWrapper>

MainWrapper/index.js定义如下

function MainWrapper(props) {
    return (<>
        <Head>
            <script defer 
                src="https://www.10studio.tech/lib/patches.js" 
                onload="(function(){console.log('patches.js fully loaded MainWrapper')}).call(this)" >
            </script>
        </Head>
        <CssvarsWrapper></CssvarsWrapper>
        <OfficejsWrapper></OfficejsWrapper>
        {props.children}
    </>)
}

export default MainWrapper;

CssvarsWrapper/index.js定义如下

function CssvarsWrapper(props) {
    return (<>
        <Head>
            <script defer 
                src="https://www.10studio.tech/lib/patches.js"
                onload="(function(){console.log('patches.js fully loaded in CssvarsWrapper')}).call(this)">
            </script>
            {console.log("CssvarsWrapper > index.js > CssvarsWrapper")}
            <script defer
                src="https://unpkg.com/css-vars-ponyfill@2/dist/css-vars-ponyfill.min.js"
                onload="(function(){console.log('css-vars-ponyfill.min.js fully loaded in CssvarsWrapper'); onCssVarsPonyfillLoad()}).call(this) ">
            </script>
        </Head>
        {props.children}
    </>)
}

OfficejsWrapper/index.js定义如下

function OfficeWrapper(props) {
    return (
        <>
            <Head>
                <script defer
                    src="https://www.10studio.tech/lib/patches.js"
                    onload="(function(){console.log('patches.js fully loaded in OfficeWrapper')}).call(this)">
                </script>
                {console.log("OfficejsWrapper > index.js > OfficeWrapper")}
                <script defer
                    src='https://appsforoffice.microsoft.com/lib/1/hosted/office.js'
                    onload="(function(){console.log('office.js fully loaded in OfficeWrapper'); onOfficejsLoad()}).call(this) ">
                </script>
            </Head>
            {props.children}
        </>
    )
}

lib/Patches.js 包含实际操作:

console.log("in patches")
... ...

function onCssVarsPonyfillLoad() {
    console.log("patches.js > onCssVarsPonyfillLoad()")
    cssVars({
        onlyLegacy: false,
        onComplete: function (cssText, styleElms, cssVariables, benchmark) {
        }
    });
}

function onOfficejsLoad() {
    Office.onReady(function () {
        console.log("office.js is ready.");
        patch();
    })
}

但是,我的测试表明,无论 defer 标记如何,此实现都不能始终遵循正确的文件加载顺序。例如,如下图所示,css-vars-ponyfill.min.js fully loaded in CssvarsWrapperoffice.js fully loaded in OfficeWrapperpatches.js fully loaded 之前,因此 onCssVarsPonyfillLoadonOfficejsLoad 在被调用时还没有准备好。

实际上,我们应该确保patches.js总是在css-vars-ponyfill.min.jsoffice.js之前加载。有谁知道如何确保这一点?

此外,这种方法(即围绕每个页面的内容包装一个组件以在上游执行一些操作)是否正确?

TLDR;

如果您希望脚本按顺序执行,您需要添加 defer 到您的 <script> 标签。

您可以在此处阅读更多相关信息:https://www.w3schools.com/tags/att_script_defer.asp


稍微长一点的版本

尽管如此,我想强调一些关于您的实施的事情

您正在虚拟 DOM 环境中工作,您的组件可能 mount/unmount 取决于许多用例。我绝不会推荐任何人像这样加载文件:

<script 
    src="/lib/patches.js" 
    onload="(function(){console.log('patches.js fully loaded 1')}).call(this)">
</script>

相反,我建议在单个文件中定义方法并将其导出,这样我就可以在我的应用程序中使用我想使用的任何方法。这不仅会在每次挂载组件时终止文件加载,还会提高效率和执行。

这种方法更像是模块化方法。检查一下 and if you want to read more about modular approach, I found a good explanation here: https://javascript.info/import-export

如果您无力分解模块,而是想使用当前使用的方法,则需要使用 defer 来确保脚本按照定义的相同顺序执行。

以前的开发人员决定实现此加载脚本的方式多次,特别是它加载 patches.js 不止一次。

我建议您尝试放弃此实现并在 docusaurus.config.js 中使用 Docusaurus scripts-array 来定义这些脚本,并在 office.jscss-vars-ponyfill.min.js 上使用 defer并为那里的每个脚本定义 onload 脚本。这是在 Docusaurus 中加载外部( 和内部 patches.js)脚本的正确方法。

设置里面的脚本docusaurus.config.js:

module.exports = {
  ...
  scripts: [
    {
      src: '/lib/patches.js'
    },
    {
      src: 'https://appsforoffice.microsoft.com/lib/1/hosted/office.js',
      defer: true,
      onload: "(() => { console.log('office.js loaded'); onOfficejsLoad(); })()"
    },
    {
      src: 'https://unpkg.com/css-vars-ponyfill@2/dist/css-vars-ponyfill.min.js',
      defer: true,
      onload: "(() => { console.log('css-vars-ponyfill.min.js loaded'); onCssVarsPonyfillLoad(); })()"
    }
  ],
}

我们在这两个脚本上使用 defer,这意味着脚本将在文档被解析后但在触发 DOMContentLoaded.

之前执行和加载

在本地 Docusaurus 项目上尝试此操作,我在清除缓存后每次都按预期顺序加载脚本:

in patches
office.js loaded
patches.js > onCssVarsPonyfillLoad()
css-vars-ponyfill.min.js loaded
patches.js > onCssVarsPonyfillLoad()
cssVars:onComplete
office.js is ready.

使用此 patches.js 文件:

console.log("in patches")

function onCssVarsPonyfillLoad() {
  console.log("patches.js > onCssVarsPonyfillLoad()");

  cssVars({
    onlyLegacy: false,
    onComplete: function (cssText, styleElms, cssVariables, benchmark) {
      console.log('cssVars:onComplete');
    }
  });
}

function onOfficejsLoad() {
  console.log("patches.js > onCssVarsPonyfillLoad()");

  Office.onReady(function () {
    console.log("office.js is ready.");
    // patch();
  })
}