背景:项目里引用了大量的SVG图片。正常情况下,我们直接 import 一个 svg 图片,作为 img 标签的 src 属性可以直接渲染。但是这样存在一个问题, 如果同一个 svg 图片需要在不同的容器下显示,怎么做? (所谓的不同容器, 就是宽度不同, 一个宽度100px, 一个宽度20px, 我们怎么做? 用两张图片来做?那有点浪费了,因为只是容器宽度不一样,但是图片内容一样。考虑到 svg 本身是矢量图,可以支持缩放,如果想用一张图来做,我们怎么做?)
最终的目标
- 只要图片内容一样,无论在什么宽度容器下展示,我们都希望用同一张图来展示,这张图可以支持放大与缩小
- 希望 SVG 图片 可以像 组件一样直接渲染,而不是作为 img 标签的 src 属性
- 我们如何在业务中如何把 SVG 当成一个组件来用的, 我们有一个
SvgWrapper组件,接受 SVG 作为children渲染,同时它也有自己的样式属性,直接设置 宽度就可以让渲染的 SVG 放大或者缩小
export default function SvgWrapper({ children, className = '' }) {
return (
<div className={`w-[20px] ${className}`}>
{
children
}
</div>
);
}
- 下面的案例可以让一张 SVG 在不同的宽度下渲染 (SvgWrapper 给的宽度不一样),并且保持图片不模糊
const Demo1 = () => {
return <SvgWrapper className="w-[20px]">
<SVGImg1/>
</SvgWrapper>
}
const Demo2 = () => {
return <SvgWrapper className="w-[100px]">
<SVGImg1/>
</SvgWrapper>
}
先看 Parcel 给的方案
- 官网给的配置
- 按照官网的配置打包结果 (直接报错)
如何解决把 SVG 图片当成组件来渲染
- 最终在 github 找到相关的办法, 参考链接 github.com/parcel-bund…
- 按照github 提供的方案 确实可以把一个 SVG 当作 组件来渲染,但是 SVG 的放大缩小没了(因为 svg ViewBox 属性丢失,这个是问题的根本)
新的问题 SVG 缩放功能没了
- 这次耶稣也没能救了,因为官方的 issues 上也有类似的问题,并且 大半年了也没人来解决 (看 issues 都是open,并且有一个 issues 时间有大半年了)
- 那我不一样,我喜欢尝试一点不一点的bug, 直接
debugger看源码, 查看源码发现 Parcel 解析 SVG 用的是@parcel/transformer-svg-react这个npm包 的源码大概是如下
- 发现了 其实用的是
@svgr/plugin-svgo@svgr/plugin-jsx, 并且默认读取了svgrrc.*配置文件 - 接着去看
@svgr/plugin-svgo@svgr/plugin-svgo这两个包,最终关键的源码如下
- 看到这里就很好解决了
最终方案
.parcelrc配置文件
{
"extends": "@parcel/config-default",
"transformers": {
"jsx:*.svg": ["@parcel/transformer-svg-react"],
"jsx:*": ["..."]
}
}
2. 在项目根目录建立文件 .svgrrc.js, 因为 Parcel 依赖了 svgr 这个仓库,这个仓库需要这个文件来处理 svg
module.exports = {
dimensions: false
}
- 最终解决问题