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

时间:2019-07-16 13:05:15

标签: webpack next.js

我的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>的{​​{1}}从<link>更改为rel并添加添加preload

3 个答案:

答案 0 :(得分:2)

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

}

答案 1 :(得分:1)

Next.js 现在可以自动内联关键 CSS

功能是实验性的,并带有标志,但我们很乐意听到您的反馈:

  1. experimental: { optimizeCss: true } 添加到 next.config.js
  2. 将 critters@0.0.7 安装为依赖项

就是这样!

参考:https://twitter.com/hdjirdeh/status/1369709676271726599

答案 2 :(得分:0)

我通过稍微调整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