Gatsby / GraphQL 应用程序 - 部署时的渲染和样式问题(Netlify 和 Surge)

Gatsby / GraphQL App - Rendering and Style Issues When Deployed (Netlify and Surge)

使用 styled components 使用 Gatsby / GraphQL 构建了一个投资组合页面。在我的本地机器上,它的外观和功能完全符合我的要求。部署后,似乎存在按钮样式问题,并且我的其中一张图片根本没有呈现。我尝试在 NetlifySurge.sh 上部署,但两次的结果相同。我做错了什么? 编辑: 我的按钮样式会受到如何使用 Link 的影响吗?

以下是我的按钮样式在本地的外观:

这是部署后的样子('View Repo' 甚至无法部署):

我的 'About Me' 本地部分(2 列)

我的 'About Me' 部分已部署(仅显示第 1 列)

盖茨比-config.js

module.exports = {
  siteMetadata: {
    title: `Portfolio`,
    description: `Lorem ipsum.`,
    author: `@jordanwhunter`,
  },
  plugins: [
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-image`,
    `gatsby-plugin-styled-components`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/assets/images`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `videos`,
        path: `${__dirname}/src/assets/videos`,
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `gatsby-starter-default`,
        short_name: `starter`,
        start_url: `/`,
        background_color: `#663399`,
        theme_color: `#663399`,
        display: `minimal-ui`,
        icon: `src/assets/images/memoji.jpeg`, // This path is relative to the root of the site.
      },
    },
    `gatsby-plugin-gatsby-cloud`,
    `gatsby-transformer-json`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `./src/data/`,
      },
    },
  ],
}

package.json

{
  "name": "gatsby-starter-default",
  "private": true,
  "description": "A simple starter to get up and developing quickly with Gatsby",
  "version": "0.1.0",
  "author": "Kyle Mathews <mathews.kyle@gmail.com>",
  "dependencies": {
    "babel-plugin-styled-components": "^1.12.0",
    "gatsby": "^3.0.1",
    "gatsby-image": "^3.0.0",
    "gatsby-plugin-gatsby-cloud": "^2.0.0",
    "gatsby-plugin-image": "^1.0.0",
    "gatsby-plugin-manifest": "^3.0.0",
    "gatsby-plugin-offline": "^4.0.0",
    "gatsby-plugin-react-helmet": "^4.0.0",
    "gatsby-plugin-sharp": "^3.0.0",
    "gatsby-plugin-styled-components": "^4.0.0",
    "gatsby-source-filesystem": "^3.0.0",
    "gatsby-transformer-json": "^3.0.0",
    "gatsby-transformer-sharp": "^3.0.0",
    "gh-pages": "^3.1.0",
    "prop-types": "^15.7.2",
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-helmet": "^6.1.0",
    "react-icons": "^4.2.0",
    "styled-components": "^5.2.1",
    "surge": "^0.22.1"
  },
  "devDependencies": {
    "prettier": "2.2.1"
  },
  "keywords": [
    "gatsby"
  ],
  "license": "0BSD",
  "scripts": {
    "build": "gatsby build",
    "develop": "gatsby develop",
    "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
    "start": "npm run develop",
    "serve": "gatsby serve",
    "clean": "gatsby clean",
    "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/gatsbyjs/gatsby-starter-default"
  },
  "bugs": {
    "url": "https://github.com/gatsbyjs/gatsby/issues"
  }
}

Portfolio.js(按钮代码从第 226 行开始)

import React from 'react'
import { useStaticQuery, graphql } from "gatsby"
import { Button } from "./Button"
import Img from "gatsby-image"
import styled from "styled-components"

const PortfolioContainer = styled.div`
  min-height: 100vh;
  padding: 5rem calc((100vw - 1300px) / 2);
  background: #fff;
  color: #fff;
`
const PortfolioHeading = styled.div`
  font-size: clamp(1.5rem, 5vw, 2.5rem);
  text-align: center;
  margin-bottom: 5rem;
  color: #000;
`
const PortfolioImg = styled(Img)`
  height: 100%;
  max-width: 100%;
  position: absolute;
  border-radius: 10px;
  filter: brightness(100%);
  transition: 0.4s cubic-bezier(0.075, 0.82, 0.165, 1);
`
const PortfolioWrapper = styled.div`
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 10px;
  justify-items: center;
  padding: 0 2rem;

  @media screen and (max-width: 1200px) {
    grid-template-columns: 1fr 1fr;
  }
  
  @media screen and (max-width: 868px) {
    grid-template-columns: 1fr;
  }

`
const PortfolioInfo = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-content: center;
  align-items: center;
  justify-items: center;
  padding: 0 2rem;
  visibility: hidden;
  opacity: 0;
  transition: opacity .2s, visibility .2s;
  
  @media screen and (max-width: 280px) {
    padding: 0 1rem;
    justify-content: center;
    align-content: center;
  }
`
const PortfolioCard = styled.div`
  line-height: 2;
  width: 100%;
  height: 500px;
  position: relative;
  border-radius: 10px;
  transition: 0.2s ease;

  &:hover ${PortfolioInfo}{
    visibility: visible;
    opacity: 1;
  }
  &:hover ${PortfolioImg}{
    filter: brightness(50%)
  }
`
const TextWrap = styled.div`
  display: flex;
  align-items: center;
  text-align: center;
  position: absolute;
  top: 375px;
  flex-wrap: wrap;
  justify-content: center;
  align-content: center;
  width: 87%;
`
const PortfolioTitle = styled.div`
  font-weight: 400;
  font-size: 1rem;
  margin-left: 0.5rem;

  @media screen and (max-width: 280px) {
    font-size: 12px;
  }
`
const PortfolioDescription = styled.div`
  font-size: 1rem;

  @media screen and (max-width: 280px) {
    font-size: 12px;
  }
`
const PortfolioTechnologies = styled.div`
  font-size: 1rem;

  @media screen and (max-width: 280px) {
    font-size: 12px;
  }
`
const ButtonLink = styled.a`
  text-decoration: none;
  cursor: pointer;
`
const ButtonWrap = styled.div`
  display: flex;
  flex-direction: row;
  position: absolute;
  justify-content: center;
  align-content: center;
  align-items: center;
  width: 100%;
  height: -400px;
  z-index: 1;
  gap: 10px;

  @media screen and (max-width: 280px) {
    padding: 0 1rem;
    justify-content: center;
    align-content: center;
  }
`
const CustomButton = styled(Button)`
  display: flex;
  align-items: center;
  position: relative;
  font-size: 14px;
  width: 100%;
  cursor: pointer;
  top: -60px;

  @media screen and (max-width: 480px) {
    background: none;
    border: none;
    padding: 0 !important;
    font-family: arial, sans-serif;
    color: #fff;
    text-decoration: underline;
    cursor: pointer;
    width: 100%;
    font-size: 12px;
    justify-content: center;
  }
`
const CustomP = styled.p`
  font-size: 12px;
`

export default function Portfolio({ heading }) {
  const data = useStaticQuery(graphql`
    query PortfolioQuery {
      allPortfolioJson {
        edges {
          node {
            alt
            button1
            button2
            description
            name
            technologies
            demo
            repo
            img {
              childImageSharp {
                fluid {
                  ...GatsbyImageSharpFluid
                }
              }
            }
          }
        } 
      }
    }
  `)

  function getPortfolio(data) {
    const portfolioArray = []
    data.allPortfolioJson.edges.forEach((item, index) => {
      portfolioArray.push(
        <PortfolioCard key={index}>
          <PortfolioImg 
            src={item.node.img.childImageSharp.fluid.src}
            fluid={item.node.img.childImageSharp.fluid}
            alt={item.node.alt}
          />
          <PortfolioInfo>
            <TextWrap>
              <PortfolioTitle
                css={`
                  margin-top: -500px;
                `}
              >
                <strong><u>Project:</u></strong> <br />
                {item.node.name}
              </PortfolioTitle>

              <PortfolioDescription
                css={`
                  margin-top: -300px;
                  
                `}
              >
                <strong><u>Description:</u></strong> <br />
                {item.node.description}
              </PortfolioDescription>
              <PortfolioTechnologies
                css={`
                  margin-top: -100px;
                 
                `}
              >
                <strong><u>Technologies:</u></strong> <br />
                {item.node.technologies}
              </PortfolioTechnologies>
            </TextWrap>
            <ButtonWrap>
              <ButtonLink
                href={`${item.node.demo}`}
                target="_blank"
              >
                <CustomButton
                  primary="true"
                  round="true"
                >
                  {item.node.button1}
                </CustomButton>
              </ButtonLink>

              <ButtonLink
                href={`${item.node.repo}`}
                target="_blank"
              >
                <CustomButton
                  primary="true"
                  round="true"
                >
                  {item.node.button2}
                </CustomButton>
              </ButtonLink>
            </ButtonWrap>
          </PortfolioInfo>
        </PortfolioCard>
      )
    })

    return portfolioArray;
  }

  return (
    <PortfolioContainer  id="portfolio">
      <PortfolioHeading>
        {heading}
        <CustomP>(Tap on Mobile)</CustomP>
      </PortfolioHeading>
      <PortfolioWrapper>
        {getPortfolio(data)}
      </PortfolioWrapper>
    </PortfolioContainer>
  )
};

About.js(第 2 列代码从第 137 行开始):

import React from 'react'
import { useStaticQuery, graphql } from 'gatsby';
import { GrCircleInformation, GrCode, GrDocumentImage } from "react-icons/gr";
import styled from "styled-components"
import Img from "gatsby-image"


const AboutContainer = styled.div`
  width: 100%;
  background: #fcfcfc;
  color: #000;
  padding: 5rem calc((100vw - 1300px) / 2);
  height: 100%;
  border-top: 1px solid gray;
  border-bottom: 1px solid gray;
`
const TopLine = styled.div`
  color: #077bf1;
  font-size: 1rem;
  padding-left: 2rem;
  margin-bottom: 0.75rem;
`
const Description = styled.p`
  text-align: start;
  padding-left: 2rem;
  margin-bottom: 4rem;
  font-size: clamp(1.5rem, 5vw, 2rem);
  font-weight: bold;
`
const ContentWrapper = styled.div`
  display: grid;
  grid-template-columns: 1fr 1fr;
  padding: 0 2rem;

  @media screen and (max-width: 768px) {
    grid-template-columns: 1fr;
  }
`
const ColumnOne = styled.div`
  display: grid;
  grid-template-rows: 1fr 1fr;
`
const Biography = styled.div`
  padding-top: 1rem;
  padding-right: 2rem;

  h3 {
    margin-bottom: 1rem;
    font-size: 1.5rem;
    font-style: italic;
  }

  p {
    color: #3b3b3b;
  }
`
const ColumnTwo = styled.div`
  display: grid;
  grid-template-columns: 1fr;
  margin-top: 2rem;
  grid-gap: 10px;

  @media screen and (max-width: 500px) {
    grid-template-columns: 1fr;
  }
`
const Image = styled(Img)`
  border-radius: 10px;
  height: 100%;
  margin-top: -50px;

  @media screen and (max-width: 375px) {
    margin-top: 0;
  }
`
export default function About() {
  const data = useStaticQuery(graphql`
    query MyQuery {
      allFile(filter: {ext: {regex: "/(jpg)|(png)|(jpeg)/"}, name: {in: ["profile-photo"]}}) {
        edges {
          node {
            childImageSharp {
              fluid {
                ...GatsbyImageSharpFluid
              }
            }
          }
        }
      }
    }
  `)

  return (
    <AboutContainer id="about">
      <TopLine>
        About Me
      </TopLine>
      <Description>
        <div css={`display: flex;`}>
          <GrDocumentImage /><h4 css={`font-style: italic;`}>Resumes</h4>
        </div> 
        <div 
          css={`
            display: flex;
            font-size: 14px;
            margin-bottom: -40px;
          `}
        >
          <p>
            <a href="https://docs.google.com/document/d/162ZfqqYxwYgeaY4fk7pf3GdtVn_Kp-ONqWOVCWzIhqo/edit?usp=sharing" target="_blank">Resume 2021 (ATS Version)</a><br />
            <a href="https://docs.google.com/document/d/1u87owpbLG2uoPAqvoVmkuh_LEGSJuRa69OVAYm3hELU/edit?usp=sharing" target="_blank">Resume 2021 (Styled Version)</a>
          </p>
        </div>   
      </Description>
      <ContentWrapper>
        <ColumnOne>
          <Biography>
            <div css={`display: flex;`}>
              <GrCircleInformation /><h3>Brand Statement</h3>
            </div>
            
            <p>
              Full stack web/software developer with an entrepreneurial spirit, and keen sense of efficiency and time management. A passionate, goal-oriented team player that strives to always write clean, precise code focused on mobile responsive themes. Maintains a problem solving, can-do attitude and exhibits consistent eagerness to learn new technologies/techniques.
            </p>
          </Biography>
          <Biography>
            <div css={`display: flex;`}>
              <GrCode />
              <h3>Technologies</h3>
            </div>
            
            <p>
              JavaScript, React, Preact, Next, Gatsby, Svelte, Node, Express, Firebase, Vercel, MongoDB, MySQL, Handlebars, jQuery, D3, GraphQL, Material-UI, CSS3, Bootstrap, Materialize, Bulma, HTML5 
            </p>
          </Biography>
        </ColumnOne>
        <ColumnTwo>
          {data.allFile.edges.map((image, key) => (
            <Image 
              key={key}
              src={image.node.childImageSharp.src}
              fluid={image.node.childImageSharp.fluid}
            />
          ))}
        </ColumnTwo>
      </ContentWrapper>
    </AboutContainer>
  )
};

检查生成的 html:

<div class="Portfolio__ButtonWrap-zwd7jb-14 boxtca">
  <a href="https://filmapi.vercel.app/" target="_blank" class="Portfolio__ButtonLink-zwd7jb-13 dHdqal">
    <a primary="true" round="true" class="Button-sc-1t76fnu-0 Portfolio__CustomButton-zwd7jb-15 iA-DhcJ cycuPQ">View App</a>
  </a>
  <a primary="true" round="true" class="Button-sc-1t76fnu-0 Portfolio__CustomButton-zwd7jb-15 iA-DhcJ cycuPQ">
    <a primary="true" round="true" class="Button-sc-1t76fnu-0 Portfolio__CustomButton-zwd7jb-15 iA-DhcJ cycuPQ">View Repo</a>
  </a>
</div>

观察 #1:

  • 第二个按钮的结构错误(不是 ButtonLink);

为什么:

  • 混乱的虚拟 DOM [结果是真实的 DOM];

可能原因:

  • 多个,同类 children rendered without key prop(最常见的不良 React 渲染原因?);

解决方案 #1:始终对 lists/arrays/groups!

的 [元素] 使用 key 属性

'About me' 缺失列问题:

观察#2:

  • 在(第一次渲染时)和 [图像] 加载期间 html/DOM 结构正常;

React 更新(重新渲染)中断 child div [of AboutContainer] - 没有使用 key(然后 VDOM 中的多个 div 没有唯一标识符) , 结果 - 更新破坏了内容结构。

但这不是唯一的原因 - 真正的原因是使用基于 <Img/> 的组件 - 对于新的 gatsby 图像插件[版本]来说已经过时了。

解决方案 #2:

您应该使用 <GatsbyImage/> 个组件。

始终确保您引用的文档是最新的!

graphql 和使用 gatsby-plugin-image 中的 GatsbyImage / StaticImage 组件而不是 [=15 中的 Img 组件,语法发生了重大变化=].

要修复 Portfolio.js - 图像渲染影响了按钮链接 - 必须重构代码以适应当前引用 gatsby-plugin-image 中的 GatsbyImage 组件的语法。 graphqluseStaticQuery 语法也必须重构。如需更多信息,请访问 Gatsby's documentation (thanks to @xadm 为我指明正确的方向)。

About.js 也通过更新语法修复了...除了这个 Img 标记更改为 StaticImage 并且 graphqluseStaticQuery 被完全删除.