问题描述
说明
我想在Next.js中只使用没有{styles.class-name}约定的类的纯名称,我google了一下,发现我需要配置next.config.js文件。那么,有没有人为此提供好的参考资料?
我有这个:
它在 Next.js 中运行良好
需要这个:
这在 Next.js 中默认不起作用
解决方法
Next.js 有 globals.css 文件,它在样式文件夹中,因此您可以:
By maximizing the browser size
然后在 globals.css 文件中:
className="red"
但我建议你使用 .module.css 文件,因为它更干净,不会弄乱。
,我之前为此编写了一个 Babel 插件。它不健壮,可以改进,但可以完成工作:
// 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),);
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),);
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,则需要添加此文件(也将此文件添加到 includes
中的 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;
}
}
在此之后,您可以简单地编写:
<div cx="red">Red text!</div>
无需导入模块文件。如有必要,它将从指定的 "pathReplace"
选项自动导入。 ($1
使用 cx
属性表示文件名)。
pathReplace
示例:
@styles/_$1.module.scss
:如果使用 cx
的文件是 Component.tsx
,则样式将从模块 @styles/_component.module.scss
导入。
您可以使用提供的选项配置 propName
。您还需要相应地更改 next-env.d.ts
。并且,相应地更改 ignoredElements
选项,因为您的属性名称可能与 JSX/HTML 标准中定义的某些属性相同。
注意事项:
-
如果在元素/组件上设置了
className
,则插件当前完全忽略cx
属性,即,如果您需要与某些全局类名合并,则使用className={`global ${styles.red}`}
. -
不支持
cx
中的多个类。可以轻松实现,但我很懒。 -
Spread 属性仅部分支持:
// 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
中配置此规则。