如何在 Nextjs 中对 CSS 类名进行哈希处理?

问题描述 投票:0回答:4

如何在 Nextjs 的 Webpack 配置中编辑

localIdentName
css-loader
字段,以便可以散列/隐藏/混淆 css 类名?

下面的例子来自《纽约时报》。注意类名:

reactjs webpack next.js css-modules css-loader
4个回答
24
投票

此线程上的所有答案都不适用于较新的 Next.js 版本(

v11.1.3-canary.2
及以上版本),因为在 PR#28529 中,Next.js 团队已切换到
css-loader
的自定义版本没有
defaultGetLocalIdent
,也不检查
getLocalIndent
是否为
null
undefined

任何最终从加载器配置中删除

getLocalIdent
的解决方案都将导致
TypeError: getLocalIdent is not a function
。换句话说,现在必须提供
getLocalIdent
这样的功能。这是一个例子:

const path = require('path');
const loaderUtils = require('loader-utils');

// based on https://github.com/vercel/next.js/blob/992c46e63bef20d7ab7e40131667ed3debaf67de/packages/next/build/webpack/config/blocks/css/loaders/getCssModuleLocalIdent.ts
const hashOnlyIdent = (context, _, exportName) =>
  loaderUtils
    .getHashDigest(
      Buffer.from(
        `filePath:${path
          .relative(context.rootContext, context.resourcePath)
          .replace(/\\+/g, '/')}#className:${exportName}`,
      ),
      'md4',
      'base64',
      6,
    )
    .replace(/[^a-zA-Z0-9-_]/g, '_')
    .replace(/^(-?\d|--)/, '_$1');

module.exports = {
  webpack(config, { dev }) {
    const rules = config.module.rules
      .find((rule) => typeof rule.oneOf === 'object')
      .oneOf.filter((rule) => Array.isArray(rule.use));

    if (!dev)
      rules.forEach((rule) => {
        rule.use.forEach((moduleLoader) => {
          if (
            moduleLoader.loader?.includes('css-loader') &&
            !moduleLoader.loader?.includes('postcss-loader')
          )
            moduleLoader.options.modules.getLocalIdent = hashOnlyIdent;

            // earlier below statements were sufficient:
            // delete moduleLoader.options.modules.getLocalIdent;
            // moduleLoader.options.modules.localIdentName = '[hash:base64:6]';
        });
      });

    return config;
  },
};

这也适用于 Next v12 和 v13(不带 TurboPack)。

演示

开发中-

生产中 -

带有实验应用程序目录的演示


4
投票

不幸的是,

Nextjs
中没有内置支持将自定义配置传递给
Webpack
加载器。但我们可以通过使用
next.config.js
来解决这个问题。

首先,在项目目录的根目录中创建

next.config.js

对于 Nextjs 11

module.exports = {
  webpack(config, { buildId, dev, isServer, defaultLoaders, webpack }) {
    config.module.rules[3].oneOf.forEach((moduleLoader, i) => {
      Array.isArray(moduleLoader.use) &&
        moduleLoader.use.forEach((l) => {
          if (
            l.loader.includes("\\css-loader") &&
            !l.loader.includes("postcss-loader")
          ) {
            const { getLocalIdent, ...others } = l.options.modules;

            l.options = {
              ...l.options,
              modules: {
                ...others,
                localIdentName: "[hash:base64:6]",
              },
            };
          }
        });
    });
    return config;
  },
};

对于 Next.js 10.2 或更高版本:

module.exports = {
  webpack(config, { buildId, dev, isServer, defaultLoaders, webpack }) {
    config.module.rules[3].oneOf.forEach((moduleLoader, i) => {
      Array.isArray(moduleLoader.use) &&
        moduleLoader.use.forEach((l) => {
          if (
            l.loader.includes("\\css-loader") &&
            !l.loader.includes("postcss-loader")
          ) {
            const { getLocalIdent, ...others } = l.options.modules;

            l.options = {
              ...l.options,
              modules: {
                ...others,
                localIdentName: "[hash:base64:6]",
              },
            };
          }
        });
    });
    return config;
  },
};

否则使用这个:

module.exports = {
  webpack(config, { buildId, dev, isServer, defaultLoaders, webpack }) {
    config.module.rules[1].oneOf.forEach((moduleLoader, i) => {
      Array.isArray(moduleLoader.use) &&
        moduleLoader.use.forEach((l) => {
          if (
            l.loader.includes('\\css-loader') &&
            !l.loader.includes('postcss-loader')
          ) {
            const { getLocalIdent, ...others } = l.options.modules;

            l.options = {
              ...l.options,
              modules: {
                ...others,
                localIdentName: '[hash:base64:6]',
              },
            };
          }
        });
    });
    return config;
  },
};

如果您想仅在生产中对类名进行哈希处理,您可以将

process.env.NODE_ENV
if
语句一起使用。像这样:

module.exports = {
  webpack(config, { buildId, dev, isServer, defaultLoaders, webpack }) {
    if (process.env.NODE_ENV === "production") {
      ...
      ...

      return config;
    } else {
      return config;
    }
  },
};

0
投票

这对我使用 Next.js 11 有用:

module.exports = {
        webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
        config.module.rules[3].oneOf.forEach((moduleLoader, i) => {
          Array.isArray(moduleLoader.use) &&
            moduleLoader.use.forEach((l) => {
              if (
                l.loader.includes('\\css-loader') &&
                !l.loader.includes('postcss-loader')
              ) {
                const { getLocalIdent, ...others } = l.options.modules;
    
                l.options = {
                  ...l.options,
                  modules: {
                    ...others,
                    localIdentName: '[hash:base64:6]',
                  },
                };
              }
            });
        });
          return config;
      },
    };

0
投票

对于那些使用 Next.js v13 并且接受的答案不起作用的人。你必须稍微改变一下 moduleLoader 条件:

if (
    moduleLoader.loader?.includes('css-loader') &&
    !moduleLoader.loader?.includes('postcss-loader') &&
    moduleLoader.options !== undefined &&
    moduleLoader.options.modules !== undefined
) {
© www.soinside.com 2019 - 2024. All rights reserved.