Vue.js 在 <body> 中注入样式而不是 index.html 中的 <head> (webpack、HtmlWebpackPlugin)

Vue.js inject styles in <body> instead of <head> of index.html (webpack, HtmlWebpackPlugin)

基本上,我想实现这个 index.html 结构:

<html>
<head>
    <!-- titles, metas and other "static" stuff -->
    <link rel="preload/prefetch" ...> <!-- injected by webpack (ok) -->
    <!-- By default compiled styles are injected here, in head, I want them in body -->

</head>
<body>
<div>
    My static loading animation
    All possible styling is inlined
    It doesn't depend on anything!
    It also could be anything, even just a plain "Loading..." text.
    You still WON'T see it until all style's in head are loaded.
</div>

<div id="app">Vue application goes here</div>

<link rel="stylesheet" href="..."> <!-- Styles injected by webpack (WANTED!) -->
<script src="..."></script> <!-- Scripts injected by webpack (by default, OK) -->

</body>

我想要这个的原因是,我的 html 完全能够向用户显示初始加载动画,我希望它 index.html 后立即呈现已加载 并且不依赖于任何其他资源。 真的,我想这是每个人都想要的,只是说...

但 Vue 默认配置为将其编译 styles 包含到 <head> 标记中,这会阻止页面呈现,直到加载这些样式。我找不到任何关于如何更改它的文档。


更新:图片!

所以,我设法手动模拟了两个变体:

  1. 样式注入 <head>(默认)
  2. 样式已注入 <body>(需要)

这里是视觉区别的图片:

1) 样式被注入 <head> (默认):

2) 样式被注入 <body> (想要):

图片上的标签 "html rendering starts" 意味着用户 实际看到 加载动画,完全在 html 中定义(我的小块 svg 和样式情况下,可以是一般情况下的任何情况)并且不依赖于任何其他外部资源来呈现。

解决方案

vue.config.js

class InjectStylesInBody {
    apply(compiler) {
        compiler.hooks.compilation.tap('inject-styles-in-body', (compilation) => {
            if (!compilation.hooks.htmlWebpackPluginAlterAssetTags) return;
            compilation.hooks.htmlWebpackPluginAlterAssetTags.tap('inject-styles-in-body', function(pluginArgs) {
                const { head, body } = pluginArgs;
                head
                    .filter(asset => asset.tagName === 'link' && asset.attributes && asset.attributes.rel === 'stylesheet')
                    .forEach(asset => {
                        head.splice(head.indexOf(asset), 1);
                        body.push(asset);
                    });
            });
        });
    }
}

module.exports = {
    // ...
    chainWebpack: config => {
        // ...
        config
            .plugin('inject-styles-in-body')
            .use(InjectStylesInBody)
        ;
        // ...
    }
    // ...
};

备注