Как встроить CSS в тег head проекта NextJS?
Мой NextJS-проект имеет следующую конфигурацию Webpack:
import path from 'path';
import glob from 'glob';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import webpack from 'webpack';
import dotenv from 'dotenv';
import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin';
import withSass from '@zeit/next-sass';
dotenv.config();
module.exports = withSass({
distDir: '.build',
webpack: (config, { dev, isServer }) => {
if (isServer) {
return config;
}
config.plugins.push(
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
);
config.optimization.minimizer.push(
new OptimizeCSSAssetsPlugin({}),
);
return config;
},
});
Это позволяет мне просто импортировать любое количество файлов scss на любой странице и объединять их все вместе, минимизировать как один файл и обслуживать следующим образом:
<link rel="stylesheet" href="/_next/static/css/styles.84a02761.chunk.css">
Однако вместо <link>
Я бы очень хотел, чтобы определения стилей были встроены в мой <head>
пометить как <style></style>
, Возможно ли это без накапливания тонны сторонних модулей?
Если нет, возможно ли хотя бы изменить <link>
"s rel
в preload
из stylesheet
а также добавить добавить as="style" crossorigin
к этому?
3 ответа
Мне удалось успешно встроить свой CSS, слегка подправив pages/_document.jsx
файл. Я продлил <Head>
компонент изначально поставляется с NextJS и добавлен в мою пользовательскую разметку документа. Вот частичное представление моих модификаций:
import { readFileSync } from 'fs';
import { join } from 'path';
class InlineStylesHead extends Head {
getCssLinks() {
return this.__getInlineStyles();
}
__getInlineStyles() {
const { assetPrefix, files } = this.context._documentProps;
if (!files || files.length === 0) return null;
return files.filter(file => /\.css$/.test(file)).map(file => (
<style
key={file}
data-href={`${assetPrefix}/_next/${file}`}
dangerouslySetInnerHTML={{
__html: readFileSync(join(process.cwd(), '.build', file), 'utf-8'),
}}
/>
));
}
}
class MyDocument extends Document {
render() {
return (
<Html lang="en" dir="ltr">
<InlineStylesHead>
<meta name="theme-color" content="#ffcc66" />
</InlineStylesHead>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
Я обязан этим решением https://github.com/zeit/next-plugins/issues/238.
Next.js теперь может автоматически встраивать критический CSS
Функция является экспериментальной и скрытой, но мы будем рады услышать ваши отзывы:
- Добавлять
experimental: { optimizeCss: true }
в next.config.js - Установите cristers@0.0.7 как зависимость
Вот и все!
Ссылка: https://twitter.com/hdjirdeh/status/1369709676271726599
Для NextJS 9.5.0+ просто используйте этот код:
import Document, {
Main,
NextScript,
Head,
Html
} from 'next/document'
import {readFileSync} from "fs"
import {join} from "path"
class InlineStylesHead extends Head {
getCssLinks(files) {
const {
assetPrefix,
devOnlyCacheBusterQueryString,
dynamicImports,
} = this.context
const cssFiles = files.allFiles.filter((f) => f.endsWith('.css'))
const sharedFiles = new Set(files.sharedFiles)
// Unmanaged files are CSS files that will be handled directly by the
// webpack runtime (`mini-css-extract-plugin`).
let dynamicCssFiles = dedupe(
dynamicImports.filter((f) => f.file.endsWith('.css'))
).map((f) => f.file)
if (dynamicCssFiles.length) {
const existing = new Set(cssFiles)
dynamicCssFiles = dynamicCssFiles.filter(
(f) => !(existing.has(f) || sharedFiles.has(f))
)
cssFiles.push(...dynamicCssFiles)
}
let cssLinkElements = []
cssFiles.forEach((file) => {
if (!process.env.__NEXT_OPTIMIZE_CSS) {
cssLinkElements.push(
<style
key={file}
data-href={`${assetPrefix}/_next/${encodeURI(
file
)}${devOnlyCacheBusterQueryString}`}
dangerouslySetInnerHTML={{
__html: readFileSync(join(process.cwd(), '.next', file), 'utf-8'),
}}
/>
)
}
cssLinkElements.push(
<style
key={file}
data-href={`${assetPrefix}/_next/${encodeURI(
file
)}${devOnlyCacheBusterQueryString}`}
dangerouslySetInnerHTML={{
__html: readFileSync(join(process.cwd(), '.next', file), 'utf-8'),
}}
/>
)
})
if (
process.env.NODE_ENV !== 'development' &&
process.env.__NEXT_OPTIMIZE_FONTS
) {
cssLinkElements = this.makeStylesheetInert(
cssLinkElements
)
}
return cssLinkElements.length === 0 ? null : cssLinkElements
}
}
function dedupe(bundles) {
const files = new Set()
const kept = []
for (const bundle of bundles) {
if (files.has(bundle.file)) continue
files.add(bundle.file)
kept.push(bundle)
}
return kept
}
export default class MyDocument extends Document {
render() {
return (
<Html lang="ru" dir="ltr">
<InlineStylesHead/>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}