我有一个安装了
@svgr/webpack
库的 Next.js 站点。我已将 next.config.js
配置为与 @svgr/webpack
配合使用,现在想要导入 svg 图像并将其与新的 next/image
组件一起使用。
这是我设置
next.config.js
文件的方法:
module.exports = {
images: {
domains: ["images.vexels.com"],
},
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
use: ["@svgr/webpack"],
});
return config;
},
};
这就是我正在尝试做的:
import Image from 'next/image'
import Logo from '@/svg/logo.svg'
<Image src={Logo} width={174} height={84} />
但是,当我这样做时,出现以下错误:
Unhandled Runtime Error
TypeError: src.startsWith is not a function
Source
client\image.tsx (278:13) @ Image
276 | let isLazy =
277 | !priority && (loading === 'lazy' || typeof loading === 'undefined')
> 278 | if (src && src.startsWith('data:')) {
| ^
279 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
280 | unoptimized = true
281 | isLazy = false
我想也许我应该将 Logo 组件作为实际组件包含在内,如下所示:
<Image src={<Logo />} width={174} height={84} />
但是,这也行不通。
知道出了什么问题以及如何解决吗?
使用当前的 webpack 配置导入
@/svg/logo.svg
只会将 SVG 文件作为 React 组件导入。
要将其作为数据 URL 导入,您需要在 next.config.js
中进行以下 webpack 配置。
module.exports = {
images: {
domains: ['images.vexels.com']
},
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
use: ['@svgr/webpack', 'url-loader']
});
return config;
}
};
然后您就可以以两种方式使用它:作为 URL 或作为组件。
import Image from 'next/image'
import svgUrl, { ReactComponent as Logo } from '@/svg/logo.svg'
<Image src={svgUrl} width={174} height={84} />
// or as a component
<Logo />
所需用途
import MySVG from "./mySVG.svg?svgr"; // SVGR loader
<MySVG />
import Image from "next/image";
import mySVG from "./mySVG.svg"; // Default NextJS loader
<Image src={mySVG} alt="" /> // (width and height will be applied automatically)
需要next.config.jswebpack(config, { dev: isDev, isServer }) {
config.module.rules.push({
test: /\.svg$/i,
issuer: /\.[jt]sx?$/,
resourceQuery: /svgr/, // only use svgr to load svg if path ends with *.svg?svgr
use: ["@svgr/webpack"],
});
// Re-add default nextjs loader for svg
config.module.rules.push({
test: /\.svg$/i,
loader: "next-image-loader",
issuer: { not: /\.(css|scss|sass)$/ },
dependency: { not: ["url"] },
resourceQuery: { not: [/svgr/] }, // Ignore this rule if the path ends with *.svg?svgr
options: { isServer, isDev, basePath: "", assetPrefix: "" },
});
}
必需的打字稿声明(如果使用 ts)declare module "*.svg?svgr";
我是怎么想出来的webpack(config) {
const defaultSvgLoader = config.module.rules.find(
(rule) => typeof rule?.test?.test === "function" && rule.test.test(".svg")
);
console.log(defaultSvgLoader);
}
resourceQuery: { not: [/svgr/] }
添加到记录的输出对象中,以便忽略
*.svg?svgr
路径
// next.config.js
module.exports = {
webpack(config) {
// Grab the existing rule that handles SVG imports
const fileLoaderRule = config.module.rules.find(
(rule) => rule.test && rule.test.test?.(".svg")
);
config.module.rules.push({
oneOf: [
// Reapply the existing rule, but only for svg imports ending in ?url
{
...fileLoaderRule,
test: /\.svg$/i,
resourceQuery: /url/, // *.svg?url
},
// Convert all other *.svg imports to React components
{
test: /\.svg$/i,
issuer: /\.[jt]sx?$/,
resourceQuery: { not: /url/ }, // exclude if *.svg?url
use: ["@svgr/webpack"],
},
],
});
// Modify the file loader rule to ignore *.svg, since we have it handled now.
fileLoaderRule.exclude = /\.svg$/i;
return config;
},
// ...other config
};
TypeScript 声明(如果需要)// svg.d.ts
/** svg imports with a ?url suffix can be used as the src value in Image components */
declare module "*.svg?url" {
import { StaticImport } from "next/image";
const defaultExport: StaticImport | string;
export default defaultExport;
}
使用示例import Image from "next/image";
import Icon from "./my-icon.svg";
import iconUrl from "./my-icon.svg?url"
// ...
<Image src={iconUrl} />
<Icon />
svg
文件名指定一个特定模式,然后配置默认加载程序以忽略此模式并
svgr/webpack
加载此模式的匹配项
webpack(config) {
const fileLoaderRule = config.module.rules.find(
(rule) => rule.test && rule.test.test(".svg")
);
fileLoaderRule.exclude = /\.icon\.svg$/;
config.module.rules.push({
test: /\.icon\.svg$/,
loader: require.resolve("@svgr/webpack"),
});
return config;
},
这里我使用的是模式*.icon.svg
,所以任何以它结尾的svg图像都可以像这样使用
import Logo from "whatever/logo.icon.svg
const Whatever = () => <Logo />
对于其他图标,这也适用
import Image from "next/image";
import Logo from "whatever/logo.svg"
const Whatever = () => <Image src={Logo} alt="logo" width={100} height={100}/>
下一个.config.mjs
webpack(config) {
// Grab the existing rule that handles SVG imports
const fileLoaderRule = config.module.rules.find((rule) =>
rule.test?.test?.(".svg")
);
config.module.rules.push({
oneOf: [
// Reapply the existing rule, but only for svg imports ending in ?url
{
...fileLoaderRule,
test: /\.svg$/i,
resourceQuery: /url/, // *.svg?url
},
// Convert all other *.svg imports to React components
{
test: /\.svg$/i,
issuer: /\.[jt]sx?$/,
resourceQuery: { not: /url/ }, // exclude if *.svg?url
use: ["@svgr/webpack"],
},
],
});
// Modify the file loader rule to ignore *.svg, since we have it handled now.
fileLoaderRule.exclude = /\.svg$/i;
return config;
},
};
export default nextConfig;
> I have the following folders structure:
src
--app
--components
--assets
----images (png, gif, jpeg ...)
------images.d.ts
----icons (svg)
------svg.d.ts
>
> svg.d.ts
declare module "*.svg" {
const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
export default content;
}
/** svg imports with a ?url suffix can be used as the src value in Image components */
declare module "*.svg?url" {
import type { StaticImport } from "next/image";
const defaultExport: StaticImport | string;
export default defaultExport;
}
> images.d.ts
declare module '*.png';
declare module '*.jpg';
declare module '*.jpeg';
declare module '*.gif';
declare module '*.bmp';
declare module '*.tiff';
declare module '*.webp';
> You do not need to import svg.d.ts inside tsconfig.json. And inside my
> tsx component I can import my `logo.svg` as React Component or url
> (to paste this url into Next/image)
import Logo from "assets/icons/logo.svg";
import logo from "assets/icons/logo.svg?url";
<Link href="/" className="flex-auto">
{/* as data url */}
<Image src={logo} width={155} height={28} alt="logotype" />
</Link>
{/* as component */}
<Logo />
That is all. Good luck.