(Gatsby) 如何在 MDX 组件中将图像源作为道具传递
(Gatsby) How to pass image source as a prop in MDX component
我正在尝试创建一个 Figure
组件,我在其中传递 img src
和其他数据。
我意识到这并不简单——例如,——但我认为它可以与普通的 HTML img
标签一起工作。
图像未显示。这是否意味着 this limitation 也适用于组件内的 HTML img
标签?
如果是这样的话,我想我确实应该使用Gatsby的动态图像。我将如何在静态查询(非页面组件)中执行此操作? This thread 让我相信这是不可能的——或者至少是 hack?
MDX文档中的组件:
<Figure
image=""
size=""
caption=""
credits=""
/>
Figure.js
:
import * as React from "react"
const Figure = ({ image, size, caption, credits }) => {
return (
<figure className={size}>
<img src={image} alt={caption} />
<figcaption>
<span>{caption}</span>
<span>{credits}</span>
</figcaption>
</figure>
)
}
export default Figure
articlePostTemplate.js
import * as React from "react"
import { graphql } from "gatsby"
import { MDXRenderer } from "gatsby-plugin-mdx"
import Layout from "../components/layout.js"
import Seo from "../components/seo.js"
const PostTemplate = ({ data, location }) => {
let post = data.mdx
return (
<Layout location={location}>
<Seo
title={post.frontmatter.title}
description={post.frontmatter.lead}
date={post.frontmatter.computerDate}
/>
<article className="post">
<header className="post" id="intro">
<p className="date">
<time dateTime={post.frontmatter.computerDate}>{post.frontmatter.humanDate}</time>
</p>
<h1 itemprop="headline">{post.frontmatter.title}</h1>
<p className="lead">{post.frontmatter.lead}</p>
</header>
<section className="post" id="body-text">
<MDXRenderer data={data}>{post.body}</MDXRenderer>
</section>
</article>
</Layout>
)
}
export default PostTemplate
export const pageQuery = graphql`
query PostBySlug(
$id: String!
) {
site {
siteMetadata {
title
}
}
mdx(id: { eq: $id }) {
id
excerpt(pruneLength: 160)
body
frontmatter {
title
computerDate: date(formatString: "YYYY-MM-DD")
humanDate: date(formatString: "D. MMMM YYYY", locale: "nb")
hook
type
lead
featuredImage {
childImageSharp {
fluid(maxWidth: 800) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
`
gatsby-config.js
module.exports = {
…
},
plugins: [
`gatsby-plugin-image`,
`gatsby-plugin-sitemap`,
{
resolve: `gatsby-plugin-mdx`,
options: {
extensions: [`.md`, `.mdx`],
gatsbyRemarkPlugins: [
{
resolve: `gatsby-remark-images`,
options: {
maxWidth: `900000000000`,
linkImagesToOriginal: false,
backgroundColor: `none`,
},
},
{
resolve: `gatsby-remark-responsive-iframe`,
options: {
wrapperStyle: `margin-bottom: 1.07var(--line-length)`,
},
},
`gatsby-remark-prismjs`,
`gatsby-remark-copy-linked-files`,
`gatsby-remark-smartypants`,
{
resolve: `gatsby-remark-autolink-headers`,
options: {
icon: false,
itemprop: `heading`,
maintainCase: false,
removeAccents: true,
elements: [`h2`, `h3`, `h4`],
},
}
],
},
},
…
{
resolve: `gatsby-source-filesystem`,
options: {
name: `content`,
path: `${__dirname}/content/`,
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/data`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/images`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/pages/`,
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `journalistikk`,
path: `${__dirname}/content/journalism/`,
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `discussion`,
path: `${__dirname}/content/discussion/`,
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `photography`,
path: `${__dirname}/content/photography/`,
}
},
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `Gatsby Starter Blog`,
short_name: `GatsbyJS`,
start_url: `/`,
background_color: `#ffffff`,
display: `minimal-ui`,
icon: `src/images/gatsby-icon.png`,
},
},
`gatsby-plugin-react-helmet`,
],
}
gatsby-node.js
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const articlePostTemplate = path.resolve(`./src/templates/articlePostTemplate.js`)
const result = await graphql(
`
{
allMdx(
sort: { fields: [frontmatter___date], order: ASC }
limit: 1000
) {
nodes {
id
frontmatter {
title
computerDate: date(formatString: "YYYY-MM-DD")
humanDate: date(formatString: "D. MMMM YYYY", locale: "nb")
}
fields {
slug
}
}
}
}
`
)
if (result.errors) {
reporter.panicOnBuild(
`There was an error loading your blog posts`,
result.errors
)
return
}
const posts = result.data.allMdx.nodes
if (posts.length > 0) {
posts.forEach((post, index) => {
[index + 1].id
createPage({
path: post.fields.slug,
component: articlePostTemplate,
context: {
id: post.id
},
})
})
}
}
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
if (node.internal.type === `Mdx`) {
const value = createFilePath({ node, getNode })
createNodeField({
name: `slug`,
node,
value,
})
}
}
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
createTypes(`
type SiteSiteMetadata {
author: Author
siteUrl: String
social: Social
}
type Author {
name: String
summary: String
}
type Social {
twitter: String
instagram: String
mail: String
}
type MarkdownRemark implements Node {
frontmatter: Frontmatter
fields: Fields
}
type Frontmatter {
title: String
description: String
date: Date @dateformat
}
type Fields {
slug: String
}
`)
}
layout.js
import * as React from "react"
import { MDXProvider } from "@mdx-js/react"
import { Link } from "gatsby"
import DWChart from "react-datawrapper-chart"
import Header from "./Header"
import Footer from "./Footer"
import Figure from "./Figure"
const shortcodes = { Link, DWChart, Figure }
export default function Layout({ children }) {
return (
<div className="layout-wrapper">
<Header />
<main>
<MDXProvider components={shortcodes}>{children}</MDXProvider>
</main>
<Footer />
</div>
)
}
您在 中提到的限制仅适用于 StaticImage
+ 动态 props
数据(具有动态源),这意味着 returns StaticImage
无法接收 src
作为 props
,因为 StaticImage
的所有 props
都需要静态分析。
换句话说,您的 MDX 可以接收一个 src
用于 <img>
标签,或者如果配置正确,您可以使用 GatsbyImage
组件。
请记住,将获取 GatsbyImage
数据(childImageSharp
、gatsbyImageData
等)的查询必须放在 top-level 组件(页面)中,如果如果在别处使用,则使用 page query or in a useStaticQuery
钩子。
两种情况下的方法(使用 <img>
或 GatsbyImage
)是相似的。您需要:
在 markdown 文件中提供图片来源
使用页面查询或 useStaticQuery
查询这些图像以向您的 MDXProvider
提供查询图像 props
。使用博客示例:
<MDXProvider>
<MDXRenderer data={data}/>
</MDXProvider>
data
代表你在GraphQL页面查询中查询的数据或者useStaticQuery
。在不知道您的数据结构的情况下,我没有添加 GraphQL 部分,因为我不知道可用的节点。
此时,您的 MDXProvider
保存了图像数据(全部来自您的 markdown 文件),您只需将其提供给您的 GatsbyImage
或 Figure
组成部分:
// some.mdx
import Figure from 'path/to/figure/component';
## Post Body
Lorem ipsum dolor...
<Figure img={props.data} />
如您所见,我将所有道具提升到 Figure
只是为了允许调试(例如使用 console.logs())以查看那里可用的道具,例如:
const Figure = (props) => {
console.log(props);
return (
<div>I will be a figure in a future</div>
)
}
export default Figure
这样,您将能够传递给 Figure
类似 img={props.someNode.frontmatter.imageNodeSource}
的内容。
再一次,不知道你的数据结构就像天上掉馅饼,但明白了。
向 Ferran 致敬以获得有用的指导。
经过更多研究,我修改了我的解决方案—
articleTemplate.js
/* shortcodes */
const ArticleTemplate = ({ data, location }) => {
let post = data.mdx
return (
<Layout location={location}>
<Seo
title={post.frontmatter.title}
description={post.frontmatter.lead}
date={post.frontmatter.computerDate}
/>
<article className="article">
<p className="date">
<time dateTime={post.frontmatter.computerDate}>{post.frontmatter.humanDate}</time>
</p>
<h1 itemprop="headline">{post.frontmatter.title}</h1>
<p className="lead" itemprop="introduction">{post.frontmatter.lead}</p>
<MDXProvider components={shortcodes}>
<MDXRenderer
data={post.frontmatter.thumbnail}
localImages={post.frontmatter.embeddedImagesLocal}
>
{post.body}
</MDXRenderer>
</MDXProvider>
</article>
</Layout>
)
}
export default ArticleTemplate
export const pageQuery = graphql`
query ArticleBySlug($id: String!) {
site {
siteMetadata {
title
}
}
mdx(id: {eq: $id}) {
id
excerpt(pruneLength: 160)
body
frontmatter {
title
computerDate: date(formatString: "YYYY-MM-DD")
humanDate: date(formatString: "D. MMMM YYYY", locale: "nb")
hook
type
lead
thumbnail {
childImageSharp {
gatsbyImageData(
layout: FULL_WIDTH
)
}
}
embeddedImagesLocal {
childImageSharp {
gatsbyImageData
}
}
}
}
}
`
figure.js
import * as React from "react"
import { GatsbyImage, getImage } from 'gatsby-plugin-image'
const Figure = ({ source, size, caption, credit }) => {
return (
<figure className={size}>
<GatsbyImage image={getImage(source)} alt={caption} />
<figcaption>
<span>{caption}</span>
<span>{credit}</span>
</figcaption>
</figure>
);
}
export default Figure
index.mdx
---
…
thumbnail: "thumb.jpeg"
embeddedImagesLocal:
- "first.jpeg"
- "second.jpeg"
---
<Figure
source={(props.localImages [0])} <!-- first image; second image would be `[1]` -->
size="I'm a `className`"
caption="I'm a caption"
photographer="I'm a name."
/>
(将此标记为解决方案,因为它对将来希望这样做的人最有帮助。它还展示了如何同时查询嵌入图像和特色图像。)
我正在尝试创建一个 Figure
组件,我在其中传递 img src
和其他数据。
我意识到这并不简单——例如,img
标签一起工作。
图像未显示。这是否意味着 this limitation 也适用于组件内的 HTML img
标签?
如果是这样的话,我想我确实应该使用Gatsby的动态图像。我将如何在静态查询(非页面组件)中执行此操作? This thread 让我相信这是不可能的——或者至少是 hack?
MDX文档中的组件:
<Figure
image=""
size=""
caption=""
credits=""
/>
Figure.js
:
import * as React from "react"
const Figure = ({ image, size, caption, credits }) => {
return (
<figure className={size}>
<img src={image} alt={caption} />
<figcaption>
<span>{caption}</span>
<span>{credits}</span>
</figcaption>
</figure>
)
}
export default Figure
articlePostTemplate.js
import * as React from "react"
import { graphql } from "gatsby"
import { MDXRenderer } from "gatsby-plugin-mdx"
import Layout from "../components/layout.js"
import Seo from "../components/seo.js"
const PostTemplate = ({ data, location }) => {
let post = data.mdx
return (
<Layout location={location}>
<Seo
title={post.frontmatter.title}
description={post.frontmatter.lead}
date={post.frontmatter.computerDate}
/>
<article className="post">
<header className="post" id="intro">
<p className="date">
<time dateTime={post.frontmatter.computerDate}>{post.frontmatter.humanDate}</time>
</p>
<h1 itemprop="headline">{post.frontmatter.title}</h1>
<p className="lead">{post.frontmatter.lead}</p>
</header>
<section className="post" id="body-text">
<MDXRenderer data={data}>{post.body}</MDXRenderer>
</section>
</article>
</Layout>
)
}
export default PostTemplate
export const pageQuery = graphql`
query PostBySlug(
$id: String!
) {
site {
siteMetadata {
title
}
}
mdx(id: { eq: $id }) {
id
excerpt(pruneLength: 160)
body
frontmatter {
title
computerDate: date(formatString: "YYYY-MM-DD")
humanDate: date(formatString: "D. MMMM YYYY", locale: "nb")
hook
type
lead
featuredImage {
childImageSharp {
fluid(maxWidth: 800) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
`
gatsby-config.js
module.exports = {
…
},
plugins: [
`gatsby-plugin-image`,
`gatsby-plugin-sitemap`,
{
resolve: `gatsby-plugin-mdx`,
options: {
extensions: [`.md`, `.mdx`],
gatsbyRemarkPlugins: [
{
resolve: `gatsby-remark-images`,
options: {
maxWidth: `900000000000`,
linkImagesToOriginal: false,
backgroundColor: `none`,
},
},
{
resolve: `gatsby-remark-responsive-iframe`,
options: {
wrapperStyle: `margin-bottom: 1.07var(--line-length)`,
},
},
`gatsby-remark-prismjs`,
`gatsby-remark-copy-linked-files`,
`gatsby-remark-smartypants`,
{
resolve: `gatsby-remark-autolink-headers`,
options: {
icon: false,
itemprop: `heading`,
maintainCase: false,
removeAccents: true,
elements: [`h2`, `h3`, `h4`],
},
}
],
},
},
…
{
resolve: `gatsby-source-filesystem`,
options: {
name: `content`,
path: `${__dirname}/content/`,
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/data`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/images`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/pages/`,
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `journalistikk`,
path: `${__dirname}/content/journalism/`,
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `discussion`,
path: `${__dirname}/content/discussion/`,
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `photography`,
path: `${__dirname}/content/photography/`,
}
},
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `Gatsby Starter Blog`,
short_name: `GatsbyJS`,
start_url: `/`,
background_color: `#ffffff`,
display: `minimal-ui`,
icon: `src/images/gatsby-icon.png`,
},
},
`gatsby-plugin-react-helmet`,
],
}
gatsby-node.js
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const articlePostTemplate = path.resolve(`./src/templates/articlePostTemplate.js`)
const result = await graphql(
`
{
allMdx(
sort: { fields: [frontmatter___date], order: ASC }
limit: 1000
) {
nodes {
id
frontmatter {
title
computerDate: date(formatString: "YYYY-MM-DD")
humanDate: date(formatString: "D. MMMM YYYY", locale: "nb")
}
fields {
slug
}
}
}
}
`
)
if (result.errors) {
reporter.panicOnBuild(
`There was an error loading your blog posts`,
result.errors
)
return
}
const posts = result.data.allMdx.nodes
if (posts.length > 0) {
posts.forEach((post, index) => {
[index + 1].id
createPage({
path: post.fields.slug,
component: articlePostTemplate,
context: {
id: post.id
},
})
})
}
}
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
if (node.internal.type === `Mdx`) {
const value = createFilePath({ node, getNode })
createNodeField({
name: `slug`,
node,
value,
})
}
}
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
createTypes(`
type SiteSiteMetadata {
author: Author
siteUrl: String
social: Social
}
type Author {
name: String
summary: String
}
type Social {
twitter: String
instagram: String
mail: String
}
type MarkdownRemark implements Node {
frontmatter: Frontmatter
fields: Fields
}
type Frontmatter {
title: String
description: String
date: Date @dateformat
}
type Fields {
slug: String
}
`)
}
layout.js
import * as React from "react"
import { MDXProvider } from "@mdx-js/react"
import { Link } from "gatsby"
import DWChart from "react-datawrapper-chart"
import Header from "./Header"
import Footer from "./Footer"
import Figure from "./Figure"
const shortcodes = { Link, DWChart, Figure }
export default function Layout({ children }) {
return (
<div className="layout-wrapper">
<Header />
<main>
<MDXProvider components={shortcodes}>{children}</MDXProvider>
</main>
<Footer />
</div>
)
}
您在 StaticImage
+ 动态 props
数据(具有动态源),这意味着 returns StaticImage
无法接收 src
作为 props
,因为 StaticImage
的所有 props
都需要静态分析。
换句话说,您的 MDX 可以接收一个 src
用于 <img>
标签,或者如果配置正确,您可以使用 GatsbyImage
组件。
请记住,将获取 GatsbyImage
数据(childImageSharp
、gatsbyImageData
等)的查询必须放在 top-level 组件(页面)中,如果如果在别处使用,则使用 page query or in a useStaticQuery
钩子。
两种情况下的方法(使用 <img>
或 GatsbyImage
)是相似的。您需要:
在 markdown 文件中提供图片来源
使用页面查询或
useStaticQuery
查询这些图像以向您的MDXProvider
提供查询图像props
。使用博客示例:<MDXProvider> <MDXRenderer data={data}/> </MDXProvider>
data
代表你在GraphQL页面查询中查询的数据或者useStaticQuery
。在不知道您的数据结构的情况下,我没有添加 GraphQL 部分,因为我不知道可用的节点。此时,您的
MDXProvider
保存了图像数据(全部来自您的 markdown 文件),您只需将其提供给您的GatsbyImage
或Figure
组成部分:// some.mdx import Figure from 'path/to/figure/component'; ## Post Body Lorem ipsum dolor... <Figure img={props.data} />
如您所见,我将所有道具提升到
Figure
只是为了允许调试(例如使用 console.logs())以查看那里可用的道具,例如:const Figure = (props) => { console.log(props); return ( <div>I will be a figure in a future</div> ) } export default Figure
这样,您将能够传递给
Figure
类似img={props.someNode.frontmatter.imageNodeSource}
的内容。再一次,不知道你的数据结构就像天上掉馅饼,但明白了。
向 Ferran 致敬以获得有用的指导。
经过更多研究,我修改了我的解决方案—
articleTemplate.js
/* shortcodes */
const ArticleTemplate = ({ data, location }) => {
let post = data.mdx
return (
<Layout location={location}>
<Seo
title={post.frontmatter.title}
description={post.frontmatter.lead}
date={post.frontmatter.computerDate}
/>
<article className="article">
<p className="date">
<time dateTime={post.frontmatter.computerDate}>{post.frontmatter.humanDate}</time>
</p>
<h1 itemprop="headline">{post.frontmatter.title}</h1>
<p className="lead" itemprop="introduction">{post.frontmatter.lead}</p>
<MDXProvider components={shortcodes}>
<MDXRenderer
data={post.frontmatter.thumbnail}
localImages={post.frontmatter.embeddedImagesLocal}
>
{post.body}
</MDXRenderer>
</MDXProvider>
</article>
</Layout>
)
}
export default ArticleTemplate
export const pageQuery = graphql`
query ArticleBySlug($id: String!) {
site {
siteMetadata {
title
}
}
mdx(id: {eq: $id}) {
id
excerpt(pruneLength: 160)
body
frontmatter {
title
computerDate: date(formatString: "YYYY-MM-DD")
humanDate: date(formatString: "D. MMMM YYYY", locale: "nb")
hook
type
lead
thumbnail {
childImageSharp {
gatsbyImageData(
layout: FULL_WIDTH
)
}
}
embeddedImagesLocal {
childImageSharp {
gatsbyImageData
}
}
}
}
}
`
figure.js
import * as React from "react"
import { GatsbyImage, getImage } from 'gatsby-plugin-image'
const Figure = ({ source, size, caption, credit }) => {
return (
<figure className={size}>
<GatsbyImage image={getImage(source)} alt={caption} />
<figcaption>
<span>{caption}</span>
<span>{credit}</span>
</figcaption>
</figure>
);
}
export default Figure
index.mdx
---
…
thumbnail: "thumb.jpeg"
embeddedImagesLocal:
- "first.jpeg"
- "second.jpeg"
---
<Figure
source={(props.localImages [0])} <!-- first image; second image would be `[1]` -->
size="I'm a `className`"
caption="I'm a caption"
photographer="I'm a name."
/>
(将此标记为解决方案,因为它对将来希望这样做的人最有帮助。它还展示了如何同时查询嵌入图像和特色图像。)