如何在NextJS项目的head标签中内联CSS?

p4tfgftt  于 2023-05-28  发布在  其他
关注(0)|答案(3)|浏览(212)

我的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">

但是,我更喜欢将样式定义作为<style></style>内联到我的<head>标记中,而不是<link>。有没有可能不堆积大量的第三方模块?
如果没有,是否可以至少将结果<link>relstylesheet更改为preload,并向其添加add as="style" crossorigin

o2rvlv0m

o2rvlv0m1#

我通过稍微调整pages/_document.jsx文件成功地内联了我的CSS。我扩展了NextJS原生提供的<Head>组件,并将其添加到自定义文档标记中。以下是我修改的部分表示:

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#issuecomment-432211871。

k4aesqcs

k4aesqcs2#

Next.js现在可以自动内联Critical CSS
功能是实验性的,背后的旗帜,但我们很乐意听到你的反馈:
1.将experimental: { optimizeCss: true }添加到next.config.js
1.安装critters@0.0.7作为依赖项
就是这样!
参考:https://twitter.com/hdjirdeh/status/1369709676271726599

au9on6nz

au9on6nz3#

对于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>
    );
  }

}

相关问题