【问题标题】:How to inline CSS in the head tag of a NextJS project?如何在 NextJS 项目的 head 标签中内联 CSS?
【发布时间】:2019-11-25 05:32:18
【问题描述】:

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

但是,我非常希望将样式定义作为&lt;style&gt;&lt;/style&gt; 内联到我的&lt;head&gt; 标记中,而不是&lt;link&gt;。是否可以不堆积大量第三方模块?

如果不是,是否可以至少将生成的&lt;link&gt;relstylesheet 更改为preload 并添加添加as="style" crossorigin 到它?

【问题讨论】:

    标签: webpack next.js


    【解决方案1】:

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

    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

    【讨论】:

    • 仅供参考,由于 _documentProps 在 Next.js 9.5.0+ 中不再可用,这将不再有效
    • 你需要做const {assetPrefix, files } = this.context
    • 术语挑剔:&lt;style&gt;...&lt;/style&gt; 元素不是“内联样式”,它是一个嵌入式样式表。内联样式是通过 style 属性直接应用于元素的 CSS 属性。
    • 升级到 NextJS 9.5+ 后,我在 this.context 中得到未定义的文件
    • 我必须将 .build 更改为 .next 才能使其在生产中工作。
    【解决方案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>
        );
      }
    
    }
    

    【讨论】:

      【解决方案3】:

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

      功能是实验性的并且在标记之后,但我们很乐意听到您的反馈:

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

      就是这样!

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-07-18
        • 2021-11-03
        • 1970-01-01
        • 2015-07-07
        • 1970-01-01
        • 1970-01-01
        • 2012-07-20
        相关资源
        最近更新 更多