如何使用 NextJS 组件级别 CSS

How to use NextJS Component level CSS

我不太擅长CSS,事实上,由于像MUI这样的库的兴起,我几乎不写CSS。我发现自己处于一个奇怪的境地,我正在尝试用打字稿和 NextJS 重写这个示例 Emabla caurousel

我面临的挑战是 CSS,如果我将它全局导入,一切正常,但我不想这样做,我想在组件级别导入它。我的代码结构如下所示:

ProductSlider.tsx就是示例工程中的EmblaCarousel.jsProductSlider.module.css就是embla.css

我的 ProductSlider 看起来像这样:

import s from './ProductSlider.module.css'
import React, { useState, useEffect, useCallback, memo } from 'react'
import useEmblaCarousel from 'embla-carousel-react'
import Thumbnail from './Thumbnail'
import Image from 'next/image'
import { Box } from '@mui/material'

type ProductSliderProps = {
items: {
    url: string
    alt?: string
}[]
}

const ProductSlider = ({ items }: ProductSliderProps) => {
const [selectedIndex, setSelectedIndex] = useState(0)
const [mainViewportRef, embla] = useEmblaCarousel({ skipSnaps: false })
const [thumbViewportRef, emblaThumbs] = useEmblaCarousel({
    containScroll: 'keepSnaps',
    dragFree: true,
})

const onThumbClick = useCallback(
    (index) => {
    if (!embla || !emblaThumbs) return
    if (emblaThumbs.clickAllowed()) embla.scrollTo(index)
    },
    [embla, emblaThumbs]
)

const onSelect = useCallback(() => {
    if (!embla || !emblaThumbs) return
    setSelectedIndex(embla.selectedScrollSnap())
    emblaThumbs.scrollTo(embla.selectedScrollSnap())
}, [embla, emblaThumbs, setSelectedIndex])

useEffect(() => {
    if (!embla) return
    onSelect()
    embla.on('select', onSelect)
}, [embla, onSelect])

return (
    <>
    <Box className={s.embla}>
        <Box className={s.embla__viewport} ref={mainViewportRef}>
        <Box className={s.embla__container}>
            {items.map(({ url, alt }, idx) => (
            <Box className={s.embla__slide} key={idx}>
                <Box className={s.embla__slide__inner}>
                <Image
                    src={url}
                    alt={alt}
                    layout="fill"
                    objectFit="contain"
                    quality="85"
                />
                </Box>
            </Box>
            ))}
        </Box>
        </Box>
    </Box>

    <Box className="embla embla--thumb">
        <Box className={s.embla__viewport} ref={thumbViewportRef}>
        <Box className="embla__container embla__container--thumb">
            {items.map(({ url, alt }, idx) => (
            <Thumbnail
                onClick={() => onThumbClick(idx)}
                selected={idx === selectedIndex}
                imgSrc={url}
                key={idx}
                alt={alt}
                s={s}
            />
            ))}
        </Box>
        </Box>
    </Box>
    </>
)
}

export default memo(ProductSlider)

如您所见,我已经设法使直接的 CSS 工作,挑战是,我如何使用命名的导入调用这样的东西 embla__container embla__container--thumb

或这个

<div
className={`embla__slide embla__slide--thumb ${
selected ? 'is-selected' : ''
}`}

我理解如果我有两个这样的 类 embla__container embla__container 我可以像这样使用 clsx cn(s.embla__container, s.embla__container) 但如果我有这样的东西--thumbsome_name--thumbsome_name-is-selected 或这个 is-selected 我不知道该怎么做。

尝试这样做

import s from './ProductSlider.module.css'
import React, { useState, useEffect, useCallback, memo } from 'react'
import useEmblaCarousel from 'embla-carousel-react'
import Thumbnail from './Thumbnail'
import Image from 'next/image'
import { Box } from '@mui/material'

type ProductSliderProps = {
items: {
    url: string
    alt?: string
}[]
}

const ProductSlider = ({ items }: ProductSliderProps) => {
const [selectedIndex, setSelectedIndex] = useState(0)
const [mainViewportRef, embla] = useEmblaCarousel({ skipSnaps: false })
const [thumbViewportRef, emblaThumbs] = useEmblaCarousel({
    containScroll: 'keepSnaps',
    dragFree: true,
})

const onThumbClick = useCallback(
    (index) => {
    if (!embla || !emblaThumbs) return
    if (emblaThumbs.clickAllowed()) embla.scrollTo(index)
    },
    [embla, emblaThumbs]
)

const onSelect = useCallback(() => {
    if (!embla || !emblaThumbs) return
    setSelectedIndex(embla.selectedScrollSnap())
    emblaThumbs.scrollTo(embla.selectedScrollSnap())
}, [embla, emblaThumbs, setSelectedIndex])

useEffect(() => {
    if (!embla) return
    onSelect()
    embla.on('select', onSelect)
}, [embla, onSelect])

return (
    <>
    <Box className={s.embla}>
        <Box className={s.embla__viewport} ref={mainViewportRef}>
        <Box className={s.embla__container}>
            {items.map(({ url, alt }, idx) => (
            <Box className={s.embla__slide} key={idx}>
                <Box className={s.embla__slide__inner}>
                <Image
                    src={url}
                    alt={alt}
                    layout="fill"
                    objectFit="contain"
                    quality="85"
                />
                </Box>
            </Box>
            ))}
        </Box>
        </Box>
    </Box>

    <Box className="embla embla--thumb">
        <Box className={s.embla__viewport} ref={thumbViewportRef}>
        <Box className="embla__container embla__container--thumb">
            {items.map(({ url, alt }, idx) => (
            <Thumbnail
                onClick={() => onThumbClick(idx)}
                selected={idx === selectedIndex}
                imgSrc={url}
                key={idx}
                alt={alt}
                s={s}
            />
            ))}
        </Box>
        </Box>
    </Box>




    <style>
        {
        `
        // Your Css Here     
        
        
        `
        }
    </style>

    </>
)
}

export default memo(ProductSlider)

基于 的回答,我最终这样做了并且成功了:

import s from './ProductSlider.module.css'
import React, { useState, useEffect, useCallback, memo } from 'react'
import useEmblaCarousel from 'embla-carousel-react'
import Thumbnail from './Thumbnail'
import Image from 'next/image'
import { Box } from '@mui/material'

type ProductSliderProps = {
items: {
    url: string
    alt?: string
}[]
}

const ProductSlider = ({ items }: ProductSliderProps) => {
const [selectedIndex, setSelectedIndex] = useState(0)
const [mainViewportRef, embla] = useEmblaCarousel({ skipSnaps: false })
const [thumbViewportRef, emblaThumbs] = useEmblaCarousel({
    containScroll: 'keepSnaps',
    dragFree: true,
})

const onThumbClick = useCallback(
    (index) => {
    if (!embla || !emblaThumbs) return
    if (emblaThumbs.clickAllowed()) embla.scrollTo(index)
    },
    [embla, emblaThumbs]
)

const onSelect = useCallback(() => {
    if (!embla || !emblaThumbs) return
    setSelectedIndex(embla.selectedScrollSnap())
    emblaThumbs.scrollTo(embla.selectedScrollSnap())
}, [embla, emblaThumbs, setSelectedIndex])

useEffect(() => {
    if (!embla) return
    onSelect()
    embla.on('select', onSelect)
}, [embla, onSelect])

return (
    <>
    <Box className={s.embla}>
        <Box className={s.embla__viewport} ref={mainViewportRef}>
        <Box className={s.embla__container}>
            {items.map(({ url, alt }, idx) => (
            <Box className={s.embla__slide} key={idx}>
                <Box className={s.embla__slide__inner}>
                <Image
                    src={url}
                    alt={alt}
                    layout="fill"
                    objectFit="contain"
                    quality="85"
                />
                </Box>
            </Box>
            ))}
        </Box>
        </Box>
    </Box>

    <Box className={`${s.embla} ${s['embla--thumb']}`}>
        <Box className={s.embla__viewport} ref={thumbViewportRef}>
        <Box
            className={`${s.embla__container} ${s['embla__container--thumb']}`}
        >
            {items.map(({ url, alt }, idx) => (
            <Thumbnail
                onClick={() => onThumbClick(idx)}
                selected={idx === selectedIndex}
                imgSrc={url}
                key={idx}
                alt={alt}
                s={s}
            />
            ))}
        </Box>
        </Box>
    </Box>
    </>
)
}

export default memo(ProductSlider)