如何在 Next.js 中设置没有 {styles.red} 的 className
IT技术
javascript
reactjs
next.js
2021-04-27 06:27:25
2个回答
Next.js 有 globals.css 文件,它在样式文件夹中,因此您可以:
className="red"
然后在 globals.css 文件中:
.red{
//...
}
但我建议您使用.module.css文件,因为它更干净并且不会造成混乱。
更新:
我之前没有检查过这个。可以直接使用babel-plugin-react-css-modules
。
无视以下内容
我之前为此编写了一个 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),
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,则需要添加此内容(还将此文件添加到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>
无需导入module文件。如有必要,它将从指定"pathReplace"
选项自动导入。($1
使用cx
属性表示文件名)。
pathReplace
例子:
@styles/_$1.module.scss
: 如果文件使用的cx
是Component.tsx
那么样式将从module导入@styles/_component.module.scss
。
您可以propName
使用提供的选项进行配置。您还需要相应地进行更改next-env.d.ts
。并且,相应地更改ignoredElements
选项,因为您的属性名称可能与 JSX/HTML 标准中定义的某些属性相同。
注意事项:
该插件目前完全忽略在元素/组件上设置的
className
属性 ifcx
,即,如果您需要与某些全局类名合并,则使用className={`global ${styles.red}`}
.cx
不支持超过一个class。可以很容易地实现,但我很懒惰。仅部分支持 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
。
其它你可能感兴趣的问题