import Image from 'next/image'
基于 Tailwind 的响应式布局(含图片深入理解)
响应式布局
如上篇文章所说的,响应式布局和自适应布局的区别,这里不再赘述
响应式布局:
- 根据屏幕大小自动调整布局
- 使用媒体查询,单位基本只需要使用 px
- 使用 flex,grid,百分比布局
- 需要特别注意的是 tailwind 中需要移动端优先
移动端优先
无前缀的类名(如 uppercase)对所有屏幕大小都有效,而带前缀的类名(如 md:uppercase)仅在指定的断点及以上处生效。
开发的时候应该先做移动端,然后做 PC 端,因为 PC 端会覆盖移动端样式
Tailwind 常用响应式布局的类名
sm、md、lg、xl、2xl 是 Tailwind 的响应式断点,分别对应 640px、768px、1024px、1280px、1536px
min-[320px]:text-center 使用断点任意值,表示在 320px 及以上的屏幕上,text-center 类名生效
container 类名表示内容区域被限制在容器内,不会超出容器边界, 它表示容器,跟 padding 一起用断点的时候不会很生硬,外层也加一层元素会控制样式更加灵活

.container {
width: 100%;
@media (width >= 40rem /* 640px */) {
max-width: 40rem /* 640px */;
}
@media (width >= 48rem /* 768px */) {
max-width: 48rem /* 768px */;
}
@media (width >= 64rem /* 1024px */) {
max-width: 64rem /* 1024px */;
}
@media (width >= 80rem /* 1280px */) {
max-width: 80rem /* 1280px */;
}
@media (width >= 96rem /* 1536px */) {
max-width: 96rem /* 1536px */;
}
}.container {
width: 100%;
@media (width >= 40rem /* 640px */) {
max-width: 40rem /* 640px */;
}
@media (width >= 48rem /* 768px */) {
max-width: 48rem /* 768px */;
}
@media (width >= 64rem /* 1024px */) {
max-width: 64rem /* 1024px */;
}
@media (width >= 80rem /* 1280px */) {
max-width: 80rem /* 1280px */;
}
@media (width >= 96rem /* 1536px */) {
max-width: 96rem /* 1536px */;
}
}Nextjs 图片 🔥
Nextjs 中图片有三种存放的方式(Nextjs 官网谈到两种,但还是区分三种比较好)
1、 第一种本地图片:放到 public 文件夹,使用的时候 src='/demo.png'
<Image src="/avatar.jpg" alt="test" width={100} height={100} /><Image src="/avatar.jpg" alt="test" width={100} height={100} />
- 必须要给 width 和 height(或者 fill),width 和 height 和图片真实宽高无关,所以只有比例关系有用,但是还是每个图片外都给一个 div 设置宽高,防止意外情况,比如 gsap 动画计算就一定需要宽高,(如果父级不加宽高,图片的显示大小就是设置的 width 和 height,但不要这样做)
- 图片会自动转 webp 格式

- 自动设置 loading="lazy",支持图片懒加载; decoding="async",支持图片异步解码
- 如果图片在首屏可见,nextjs 会提示添加 priority 属性,和不加的区别是没有 loading="lazy" 属性


- 图片的 srcset 属性,可以设置图片的响应式,在电脑上清晰度比较低的显示器才是 1,手机屏幕往往比较高,都会大于 2 ,这里的 w=256 跟我设置的 width={100} height={100} 有关,可以理解为二倍图,跟 devicesizes 配置有关;w=256 表示图片的宽度是 256px,q=75 表示图片的质量是 75%,跟 quality 配置有关
srcset="/_next/image?url=%2Favatar.jpg&w=128&q=75 1x,
/_next/image?url=%2Favatar.jpg&w=256&q=75 2x"srcset="/_next/image?url=%2Favatar.jpg&w=128&q=75 1x,
/_next/image?url=%2Favatar.jpg&w=256&q=75 2x"
2、 第二种本地图片:放到 app 文件夹,通过模块导入使用,这种方式和第一种差不多,只是不需要给 width 和 height,nextjs 会获取原图的尺寸来自动设置
import DemoImage from './images/avatar.jpg'
const Test = () => {
return <Image src={DemoImage} alt="test" />
}
export default Testimport DemoImage from './images/avatar.jpg'
const Test = () => {
return <Image src={DemoImage} alt="test" />
}
export default Test

这种方案还适合做图片渐进式加载,也就是可以加 placeholder="blur" 属性,在图片加载之前显示一个模糊的占位符,加载完成之后再显示真实的图片
<Image src={DemoImage} alt="test" placeholder="blur" /><Image src={DemoImage} alt="test" placeholder="blur" />
3、 第三种是远程图片链接,使用的时候 src='https://example.com/demo.png',为了安全,需要 remotePatterns 配置,也是必须设置 width 和 height(或者 fill),功能同第一种,远程图片也可以使用 placeholder="blur", blurDataURL=base64 来做一个渐进加载,这种方案只会多加载一张图,不会像小红书一样多加载很多图片请求,因为小红书是把每张图都做对应渐进加载,而不是同一张图

远程图片很常见的还有在不定宽高的时候,使用 fill 结合 sizes 属性,来实现 图片的响应式
思考:那什么时候用第一种,第二种,第三种呢?
- 第一种:如果图片是本地图片,图片是静态资源,不需要经过打包处理,直接通过 URL 访问即可;适合做静态资源托管,比如 logo、icon、banner 等不会频繁变动的图片;适合需要 CDN 加速的场景,因为 public 下的文件会原样输出到最终的静态资源目录,便于 CDN 缓存和分发;适合 SEO 相关的图片,路径固定,容易被搜索引擎收录。
- 第二种:需要利用 Next.js 的图片优化能力(如自动尺寸、渐进式加载、自动格式转换等);图片和组件/页面强关联,属于组件私有资源(如头像、局部装饰图);需要自动获取图片原始尺寸,减少手动维护宽高;需要用到 Next.js 的图片占位符(blurDataURL)等高级特性;
- 如果图片是远程图片,使用第三种
拓展:超有用的图片属性
const nextConfig = {
images: {
// 设置图片的下载方式,也就是只能下载,无法通过 url 打开,这样不会浪费自己服务器资源
contentDispositionType: 'attachment',
// 设置图片的 CSP,可以避免图片携带脚本攻击
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;"
}
}const nextConfig = {
images: {
// 设置图片的下载方式,也就是只能下载,无法通过 url 打开,这样不会浪费自己服务器资源
contentDispositionType: 'attachment',
// 设置图片的 CSP,可以避免图片携带脚本攻击
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;"
}
}因为本篇文章多数是用 md 语法,没有上面两个功能,所以下面这张图片使用的 nextjs 的 Image 组件来测试,右键在新标签中打开图片就可以看到效果,会直接下载图片
记录遇到的坑:做营销活动海报的库 html2canvas 和 Image 标签一起使用可能会有兼容性问题
Webp 的兼容性
方案一:在有服务端能力的情况下,比如阿里云 OSS,Nextjs,它们会根据 Accept 这个请求头来判断是否返回 webp(推荐)

方案二:原生 picture 标签
<picture>
<source srcset="image.webp" type="image/webp" />
<source srcset="image.png" type="image/png" />
<img src="image.png" alt="image" />
</picture><picture>
<source srcset="image.webp" type="image/webp" />
<source srcset="image.png" type="image/png" />
<img src="image.png" alt="image" />
</picture>方案三:JS 脚本 (不推荐)
function checkWebPSupport() {
let canvas = document.createElement('canvas')
if (canvas.toDataURL) {
return canvas.toDataURL('image/webp').indexOf('data:image/webp') === 0
}
return false
}function checkWebPSupport() {
let canvas = document.createElement('canvas')
if (canvas.toDataURL) {
return canvas.toDataURL('image/webp').indexOf('data:image/webp') === 0
}
return false
}图片响应式 🔥
背景:响应式的项目,在大屏的时候应该加载大图,小屏的时候应该加载小图。如果在小屏幕加载了一张很大的图,那会浪费资源;如果在大屏幕加载了一张很小的图,那看起来有颗粒感;更复杂的是,某些设备具有高分辨率屏幕,需要比预期的更大的图像才能很好地显示。下面只讨论 html 方案,背景图方案参考 Responsive images 101 - part 8: CSS images
1、同一图片的响应式(sizes srcset)
对于同一图片来说,可以使用 sizes 和 fill 属性(fill 属于 nextjs 的 Image 组件的属性,nextjs 做同一图片的响应式更加方便),只需要提供一张大图,然后这两个属性可以控制图片的显示方式,通过这种方式就能实现图片的响应式
当远程图片(或者本地图片) width height 不确定的时候,可以使用图片的 fill 属性,它使图像扩展到父元素的大小,并且可以看到图片会有个 absolute 属性,所以父元素必须 “relative”, “fixed”, “absolute”,然后图片可以设置 objectFit 属性,来控制图片的显示方式。
<Image
src="https://img.res.meizu.com/img/download/uc/17/35/38/56/70/173538567/w200h200"
alt="test"
fill
sizes="100vw"
objectFit="cover"
/><Image
src="https://img.res.meizu.com/img/download/uc/17/35/38/56/70/173538567/w200h200"
alt="test"
fill
sizes="100vw"
objectFit="cover"
/><div className={'relative size-10'}>
<Image
src="/avatar.jpg"
fill
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
alt="test"
objectFit="cover"
/>
</div><div className={'relative size-10'}>
<Image
src="/avatar.jpg"
fill
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
alt="test"
objectFit="cover"
/>
</div>对于 sizes 和 srcset 属性,sizes 属性是用来设置图片的尺寸,srcset 属性是用来设置图片的源,这两个属性是用来控制图片的显示方式。
注意 Nextjs 对图片处理缩小是可以的,但是放大不行,所以下面的比如匹配上了 3840,但是原图最大宽度只有 200,那也只是显示 200 的图,不会放大
100vw 计算规则大约为视窗宽度(如 800px) * DPR(如 2) → 需要 1600px 物理宽度的图片,所以会匹配到 1920 的图片;(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw 表示在 768px 及以下的屏幕上,图片的宽度为 100vw,在 1200px 及以下的屏幕上,图片的宽度为 50vw,在 1200px 及以上的屏幕上,图片的宽度为 33vw,也就是平板的时候要 * 1/2 ,pc 的是 * 1/3
sizes="100vw"
srcset="
/_next/image?url=...&w=640&q=75 640w,
/_next/image?url=...&w=750&q=75 750w,
/_next/image?url=...&w=828&q=75 828w,
/_next/image?url=...&w=1080&q=75 1080w,
/_next/image?url=...&w=1200&q=75 1200w,
/_next/image?url=...&w=1920&q=75 1920w,
/_next/image?url=...&w=2048&q=75 2048w,
/_next/image?url=...&w=3840&q=75 3840w
"sizes="100vw"
srcset="
/_next/image?url=...&w=640&q=75 640w,
/_next/image?url=...&w=750&q=75 750w,
/_next/image?url=...&w=828&q=75 828w,
/_next/image?url=...&w=1080&q=75 1080w,
/_next/image?url=...&w=1200&q=75 1200w,
/_next/image?url=...&w=1920&q=75 1920w,
/_next/image?url=...&w=2048&q=75 2048w,
/_next/image?url=...&w=3840&q=75 3840w
"
2、不同图片(为不同布局提供裁剪图像)的响应式(picture)
通过 media 属性,来判断屏幕大小,然后提供不同的图片,来实现图片的响应式
响应式布局
1、规则的响应式布局
对于规则的布局,可以使用 flex,grid 布局,来实现图片的响应式,上面的代码就是 grid 布局,注意的是外层有一个 container,里面的图片需要定好宽高比
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3 xl:gap-6"></div>
// 或者
<div className="grid grid-cols-[repeat(auto-fill,minmax(400px,1fr))] gap-4 xl:gap-6"></div><div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3 xl:gap-6"></div>
// 或者
<div className="grid grid-cols-[repeat(auto-fill,minmax(400px,1fr))] gap-4 xl:gap-6"></div>2、不规则的响应式布局
有时候,h5 和 pc 相差太大,基本无法改变布局来实现,所以最好的方案是使用自适应布局 + 媒体查询,低于 768 或者 1024 或者 1280 使用自适应布局,大于 1280px 使用 pc 布局