让 NextJS 图像组件和 @svgr/webpack 完美配合

问题描述 投票:0回答:5

我有一个安装了

@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} />

但是,这也行不通。

知道出了什么问题以及如何解决吗?

javascript reactjs webpack next.js nextjs-image
5个回答
9
投票

使用当前的 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 />
    

4
投票
其他答案牺牲了 NextJS 提供的默认宽度+高度导入行为。我下面的答案保留了这种行为,这样您就不需要手动检查文件的尺寸

所需用途

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.js

webpack(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";
我是怎么想出来的

    阅读这些文档:
  1. https://react-svgr.com/docs/webpack/
  2. 使用此代码片段获取适用于 svgs 的默认规则
webpack(config) { const defaultSvgLoader = config.module.rules.find( (rule) => typeof rule?.test?.test === "function" && rule.test.test(".svg") ); console.log(defaultSvgLoader); }

  1. resourceQuery: { not: [/svgr/] }
     添加到记录的输出对象中,以便忽略 
    *.svg?svgr
     路径

2
投票
这与之前的答案类似,但对我来说唯一有效的方法是允许仅 url 导入以及 React 组件导入:

// 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 />
    

1
投票
解决此问题的方法可能是为

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}/>
    

0
投票
我也在研究这个问题,只是想分享我的解决方案并触及所有细微差别。 我正在使用 next js (新的应用程序路由器)和 tailwind css。 我有从上面的答案中获取的 next.config.mjs 。

下一个.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.
    
© www.soinside.com 2019 - 2024. All rights reserved.