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>
标记中,这会阻止页面呈现,直到加载这些样式。我找不到任何关于如何更改它的文档。
更新:图片!
所以,我设法手动模拟了两个变体:
- 样式注入
<head>
(默认)
- 样式已注入
<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)
;
// ...
}
// ...
};
备注
归根结底,这与Vue.js无关。人们可以很容易地将它与其他框架或裸 webpack 一起使用。
如果 HtmlWebpackPlugin
有一些 inject-css
样式选项,就像 has 脚本一样。
参见:https://github.com/jantimon/html-webpack-plugin/blob/e2c6990e94b298ff66bcd885c9a03a78221479f6/index.js#L548
基本上,我想实现这个 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>
标记中,这会阻止页面呈现,直到加载这些样式。我找不到任何关于如何更改它的文档。
更新:图片!
所以,我设法手动模拟了两个变体:
- 样式注入
<head>
(默认) - 样式已注入
<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)
;
// ...
}
// ...
};
备注
归根结底,这与Vue.js无关。人们可以很容易地将它与其他框架或裸 webpack 一起使用。
如果
HtmlWebpackPlugin
有一些inject-css
样式选项,就像 has 脚本一样。
参见:https://github.com/jantimon/html-webpack-plugin/blob/e2c6990e94b298ff66bcd885c9a03a78221479f6/index.js#L548