如何使用 NextJS 组件级别 CSS
How to use NextJS Component level CSS
我不太擅长CSS,事实上,由于像MUI
这样的库的兴起,我几乎不写CSS。我发现自己处于一个奇怪的境地,我正在尝试用打字稿和 NextJS 重写这个示例 Emabla caurousel。
我面临的挑战是 CSS,如果我将它全局导入,一切正常,但我不想这样做,我想在组件级别导入它。我的代码结构如下所示:
ProductSlider.tsx
就是示例工程中的EmblaCarousel.js
,ProductSlider.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)
但如果我有这样的东西--thumb
、some_name--thumb
、some_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)
我不太擅长CSS,事实上,由于像MUI
这样的库的兴起,我几乎不写CSS。我发现自己处于一个奇怪的境地,我正在尝试用打字稿和 NextJS 重写这个示例 Emabla caurousel。
我面临的挑战是 CSS,如果我将它全局导入,一切正常,但我不想这样做,我想在组件级别导入它。我的代码结构如下所示:
ProductSlider.tsx
就是示例工程中的EmblaCarousel.js
,ProductSlider.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)
但如果我有这样的东西--thumb
、some_name--thumb
、some_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)