如何在 Next.js 中设置没有 {styles.red} 的 className [英] How to set className without {styles.red} in Next.js

查看:19
本文介绍了如何在 Next.js 中设置没有 {styles.red} 的 className的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我只想在 Next.js 中使用没有 {styles.class-name} 约定的类的纯名称,我谷歌它并发现我需要配置 next.config.js 文件.那么,有人对此有很好的参考吗?

I want to use only the pure name of the class without {styles.class-name} convention in Next.js, I google it and find I need to configure the next.config.js file. So, have someone good references for this?

它在 Next.js 中运行良好

And it works fine in Next.js

这在 Next.js 中默认不起作用

This is not working by default in Next.js

推荐答案

更新:

我之前没有检查过这个.可以直接使用 babel-plugin-react-css-modules.

我之前为此编写了一个 Babel 插件.它不健壮,可以改进,但可以完成工作:

I had earlier wrote a Babel plugin for this. It is not robust, can be improved, but does the job:

// babel-plugin-cx.js

const __path = require('path');

const plugin = ({ types: t }) => {
  const cloneNode = t.cloneNode || t.cloneDeep;

  return {
    name: 'babel-plugin-cx',
    visitor: {
      Program: {
        enter(path, state) {
          state.stylesIdentifier = path.scope.generateUidIdentifier('styles');
        },

        exit(path, state) {
          if (state.hasProp) {
            const importDeclaration = t.importDeclaration(
              [t.importDefaultSpecifier(state.stylesIdentifier)],
              t.stringLiteral(
                __path
                  .parse(state.file.opts.filename)
                  .name.toLowerCase()
                  .replace(/^([^]*)$/, state.opts.pathReplace),
              ),
            );

            path.node.body.unshift(importDeclaration);
          }
        },
      },

      JSXAttribute(path, state) {
        if (path.node.name.name !== state.opts.propName) return;

        if (
          state.opts.ignoredElements.includes(
            path.findParent((p) => p.isJSXOpeningElement()).node.name.name,
          )
        )
          return;

        path.node.name.name = 'className';
        const value = path.get('value');

        if (value.isLiteral()) {
          value.replaceWith(
            t.jsxExpressionContainer(
              t.memberExpression(
                cloneNode(state.stylesIdentifier),
                cloneNode(value.node),
                true,
                false,
              ),
            ),
          );

          state.hasProp = true;
        } else if (value.isJSXExpressionContainer()) {
          const expression = value.get('expression');
          expression.replaceWith(
            t.memberExpression(
              cloneNode(state.stylesIdentifier),
              cloneNode(expression.node),
              true,
              false,
            ),
          );

          state.hasProp = true;
        }
      },

      JSXSpreadAttribute(path, state) {
        if (
          state.opts.ignoredElements.includes(
            path.findParent((p) => p.isJSXOpeningElement()).node.name.name,
          )
        )
          return;

        const argument = path.get('argument');
        if (!argument.isObjectExpression()) return;
        const properties = argument.get('properties');

        for (const property of properties) {
          if (property.node.key.name === state.opts.propName) {
            property.node.key.name = 'className';
            const value = property.get('value');
            value.replaceWith(
              t.memberExpression(
                cloneNode(state.stylesIdentifier),
                cloneNode(value.node),
                true,
                false,
              ),
            );

            state.hasProp = true;
          }
        }
      },
    },
  };
};

module.exports = plugin;

// .babelrc

{
  "presets": ["next/babel"],
  "plugins": [
    [
      "./babel-plugin-cx",
      {
        "pathReplace": "./$1.module.css",
        "propName": "cx",
        "ignoredElements": ["circle", "ellipse", "radialGradient"]
      }
    ]
  ]
}

如果使用Typescript,你需要添加这个(也将这个文件添加到tsconfig.json中的includes数组中:

If using Typescript, you need to add this (also add this file to includes array in tsconfig.json:

// custom.d.ts

import type * as React from 'react';

declare module 'react' {
  // eslint-disable-next-line @typescript-eslint/consistent-type-definitions, @typescript-eslint/no-unnecessary-qualifier
  interface HTMLAttributes<T> extends React.DOMAttributes<T> {
    cx?: string;
  }
}

在这之后,你可以简单地写:

After this, you can simply write:

<div cx="red">Red text!</div>

无需导入模块文件.如果需要,它将从指定的 pathReplace" 选项中自动导入.($1 表示使用 cx 属性的文件名.

There is no need to import the module file. It will be automatically imported if necessary from the specified "pathReplace" option. ($1 indicates the file name using the cx attribute).

pathReplace 示例:

@styles/_$1.module.scss : 如果文件使用 cxComponent.tsx 那么样式将从模块导入@styles/_component.module.scss.

@styles/_$1.module.scss : if file using cx is Component.tsx then the styles will be imported from module @styles/_component.module.scss.

您可以使用提供的选项配置 propName.您还需要相应地更改 next-env.d.ts.并且,相应地更改 ignoredElements 选项,因为您的属性名称可能与 JSX/HTML 标准中定义的某些属性相同.

You can configure the propName using the provided option. You will also need to change next-env.d.ts accordingly. And, change the ignoredElements option accordingly, as your attribute name may be same as some attribute defined in the JSX/HTML standard.

注意事项:

  • 如果在元素/组件上设置了cx,则插件目前完全忽略className属性,即,如果您需要与一些全局类名合并则使用 className={`global ${styles.red}`}.

  • The plugin currently completely ignores className attribute if cx is set on the element/component, i.e., if you need to merge with some global class name then use className={`global ${styles.red}`}.

cx 中不支持多个类.可以很容易实现,但是我比较懒.

More than one class in cx is not supported. Can be implemented easily, but I was quite lazy.

仅部分支持传播属性:

// supported:
<div {...{cx: "red", other: "attribute"}}>Red text!</div>

// not supported:
const attrs = {cx: "red", other: "attribute"};
<div {...attrs}>Red text!</div>

您可能希望在 ESLint 中配置此规则 react/jsx-props-no-spreading.

You may like to configure this rule in ESLint react/jsx-props-no-spreading.

这篇关于如何在 Next.js 中设置没有 {styles.red} 的 className的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆