还在用Hexo吗?来试试Gatsby搭建一个网站吧!

还在用Hexo吗?来试试Gatsby搭建一个网站吧!


Gatsby 是一个基于 React 的、免费开源的、用于搭建静态站点的框架。

官网:https://www.gatsbyjs.com/docs/

前言


最近发现了一个好看的个人主页模板,是用Gatsby搭建的,之前没听说过这个框架,也没有React经验,所以踩了很多坑,为了加一个不蒜子功能整了两天才整明白……但是gatsby的官方文档我觉得写得是很好的,最终结果也是令我比较满意的,所以跟着我的脚步避开这些坑开始愉快的Gatsby体验吧!

最终结果可以去我的个人网站看下:http://www.lekshome.top/

开源地址:https://github.com/LeKZzzz/lekshome

image-20240816110611796

image-20240818024751101

image-20240818024804553

image-20240818024837403

image-20240818024843943

1. 配置


我的环境如下:

  • Ubuntu 20.04
  • Node.js v20.9.0
  • Gatsby-CLI 5.13.3
  1. 环境配置

    1. Node.js(Gatsby v5需要Node.js v18 或更高版本,但低于 v21)

      1
      2
      3
      sudo apt-get install curl
      curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
      nvm install 20.9.0
    2. Git

      这部分网上很多,就不演示了

    3. Gatsby CLI

      1
      npm install -g gatsby-cli

      可以运行gatsby --version检查版本是否正确

  2. 网站模板

    这部分我用的是 https://github.com/bchiang7/v4 的网站模板,接下来也会以此为演示。

    1. clone模板

      可以先创建一个独立文件夹然后再clone

      1
      2
      3
      4
      mkdir website
      cd website
      git clone https://github.com/bchiang7/v4.git
      cd v4
    2. 下载必要模块

      请先删除yarn.lock文件

      1
      2
      3
      rm yarn.lock
      npm install gatsby-plugin-robots-txt@1.6.14
      npm install
  3. 配置socket.io的端口环境变量

    端口可以自行设定,但是请确保这个端口在防火墙是放开的

    https://www.reddit.com/r/gatsbyjs/comments/krifvb/gatsby_randomize_socketio_port_breaking_vagrant/

    1
    2
    3
    4
    5
    6
    vim ~/.bashrc

    # 文件末尾添加
    export INTERNAL_STATUS_PORT=1234

    source ~/.bashrc
  4. 验证是否可以正常启动

    1
    gatsby develop --host=0.0.0.0

    image-20240814161551844

    出现这些输出同时可以在浏览器中可以访问则说明环境配置正确

2. DIY网站


基本配置


  1. gatsby-config.js

    这里可以修改你的网站title、网站描述、siteUrl等

  2. src/config.js

    这里可以修改你的邮箱、社交媒体账号、添加nav导航的元素等

  3. static/resume.pdf

    可以将这个文件替换为你的简历

  4. src/images/me.jpg

    可以将这个文件替换为你的个人照片

  5. src/components/sections/about.js

    在这个文件中可以修改个人介绍

  6. src/components/sections/contact.js

    在这个文件中可以修改邮件联系文本

  7. src/components/sections/hero.js

    在这个文件中可以修改开屏介绍文本

logo配置


默认logo是英文字母B,如果我们需要改成我们自己的首字母则需要花费一点功夫。

  1. 首先需要去figma绘制一个首字母的文本,我的设置如下:

    image-20240814165531322

  2. 然后导出为svg

    image-20240814165613981

  3. 修改src/components/icons/loader.js

    用刚刚导出的svg的path标签的d属性修改loader.js文件中path标签的d属性,然后修改g标签中的transform属性,transform属性需要根据实际结果手动调整为合适数值

  4. 修改src/components/icons/logo.js

    将修改过后的src/components/icons/loader.js文件中的<g>标签及其子标签复制替换src/components/icons/logo.js中的<path>标签及其子标签

  5. 修改src/images/logo.png

    这个是标签栏上的logo,可以参考他原有的图片,将代码生成的logo截图再裁剪得到

    由于手头没有修图软件,我是用PPT裁剪的huh:)

footer配置(不蒜子)


image-20240814175557285

  1. footer文本

    这部分可以根据个人喜好进行修改,我的修改如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    import React, { useState, useEffect } from 'react';
    import PropTypes from 'prop-types';
    import styled from 'styled-components';
    import { Icon } from '@components/icons';
    import { socialMedia } from '@config';

    const StyledFooter = styled.footer`
    ${({ theme }) => theme.mixins.flexCenter};
    flex-direction: column;
    height: auto;
    min-height: 70px;
    padding: 15px;
    text-align: center;
    `;

    const StyledSocialLinks = styled.div`
    display: none;

    @media (max-width: 768px) {
    display: block;
    width: 100%;
    max-width: 270px;
    margin: 0 auto 10px;
    color: var(--light-slate);
    }

    ul {
    ${({ theme }) => theme.mixins.flexBetween};
    padding: 0;
    margin: 0;
    list-style: none;

    a {
    padding: 10px;
    svg {
    width: 20px;
    height: 20px;
    }
    }
    }
    `;

    const StyledCredit = styled.div`
    color: var(--light-slate);
    font-family: var(--font-mono);
    font-size: var(--fz-xxs);
    line-height: 1;

    a {
    padding: 10px;
    }

    .github-stats {
    margin-top: 10px;

    & > span {
    display: inline-flex;
    align-items: center;
    margin: 0 7px;
    }
    svg {
    display: inline-block;
    margin-right: 5px;
    width: 14px;
    height: 14px;
    }
    }
    `;

    const StyledSecond = styled.div`
    color: var(--light-slate);
    font-family: var(--font-mono);
    font-size: var(--fz-xxs);
    line-height: 1;

    a {
    padding: 10px;
    }
    a:hover{
    cursor:default;
    }

    .github-stats {
    margin-top: 10px;

    & > span {
    display: inline-flex;
    align-items: center;
    margin: 0 7px;
    }
    svg {
    display: inline-block;
    margin-right: 5px;
    width: 14px;
    height: 14px;
    }
    }
    `;

    const Footer = () => {
    const [githubInfo, setGitHubInfo] = useState({
    stars: null,
    forks: null,
    });

    const [githubInfo2, setGitHubInfo2] = useState({
    stars: 0,
    forks: 0,
    });

    useEffect(() => {
    const fetchGitHubData = async () => {
    try {
    await Promise.all([
    fetch('https://api.github.com/repos/bchiang7/v4')
    .then(response => response.json())
    .then(data => {
    setGitHubInfo({
    stars: data.stargazers_count,
    forks: data.forks_count,
    });
    }),
    fetch('https://api.github.com/repos/LeKZzzz/lekshome')
    .then(response => response.json())
    .then(data => {
    setGitHubInfo2({
    stars: data.stargazers_count,
    forks: data.forks_count,
    });
    })
    ]);
    } catch (e) {
    console.error('获取 GitHub 仓库信息时出错:', e);
    }
    };
    fetchGitHubData();
    // buusanzi
    const script = document.createElement('script');
    script.async = true;
    script.src = "https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js";
    document.head.appendChild(script);
    }, []);


    return (
    <StyledFooter>
    <StyledSocialLinks>
    <ul>
    {socialMedia &&
    socialMedia.map(({ name, url }, i) => (
    <li key={i}>
    <a href={url} aria-label={name}>
    <Icon name={name} />
    </a>
    </li>
    ))}
    </ul>
    </StyledSocialLinks>

    <StyledCredit tabindex="-1">
    <a href="https://github.com/bchiang7/v4">
    <div>Built by Brittany Chiang |</div>

    {githubInfo.stars && githubInfo.forks && (
    <div className="github-stats">
    <span>
    <Icon name="Star" />
    <span>{githubInfo.stars.toLocaleString()}</span>
    </span>
    <span>
    <Icon name="Fork" />
    <span>{githubInfo.forks.toLocaleString()}</span>
    </span>
    </div>
    )}
    </a>

    <a href="https://github.com/LeKZzzz/lekshome">
    <div>| Customized by LeK</div>

    {githubInfo.stars && githubInfo.forks && (
    <div className="github-stats">
    <span>
    <Icon name="Star" />
    <span>{githubInfo2.stars.toLocaleString()}</span>
    </span>
    <span>
    <Icon name="Fork" />
    <span>{githubInfo2.forks.toLocaleString()}</span>
    </span>
    </div>
    )}
    </a>
    </StyledCredit>
    <StyledSecond tabindex="-1">
    <a>
    <div>
    Last update on 2024/08/15 |
    </div>
    </a>
    <a>
    <div>
    | Total visits <span id="busuanzi_value_site_pv"></span> times
    </div>
    </a>

    </StyledSecond>
    <StyledCredit tabindex="-1">
    <div align="center"><a href="https://beian.miit.gov.cn/" target="_blank">粤ICP备2022018241号</a></div>
    </StyledCredit>
    </StyledFooter>
    );
    };

    Footer.propTypes = {
    githubInfo: PropTypes.object,
    };

    export default Footer;

  2. 不蒜子

    我应该是国内第一个在这个模板里集成不蒜子的,这个功能折磨了我三天,不蒜子的一般使用方法是使用一个<script>标签,但是由于这个模板完全右js构建,所以普通的方法是无法正确访问不蒜子的;而且由于网页元素是js动态写入的,所以不能通过导入的方式导入不蒜子;我发现只能从useEffect入手,然后仔细看了代码发现模板原有的star和fork获取是没有展示出来的,于是又多了个要改的地方……为此我还专门提了个issue,提完的下午发现是NODE_ENV没有正确配置而提前return了,导致后面的代码都没有执行……export NODE_ENV之后再运行,结果gatsby报错无法正常启动……最后还是选择把这个环境判断给注释掉,后面footer就能正常使用了。但是后面还遇到了异步问题,就不多赘述了。唉,花了三天时间来找问题和debug。

工作经历配置


  1. 修改content/jobs中的文件夹

    每个工作经历要新建一个文件夹

  2. 进入文件夹,添加index.md文件

  3. 配置index.md文件,如下例

    1
    2
    3
    4
    5
    6
    7
    8
    ---
    date: '2024-07-10'
    title: 'Software Development Intern'
    company: 'CSAIR'
    location: 'Guangzhou, China'
    range: 'July - August 2024'
    url: 'https://www.csairgroup.cn/cn/'
    ---
    • date:决定jobs展示时的先后顺序
    • title:职位
    • company:公司
    • location:工作地点
    • range:工作开始时间和结束时间
    • url:公司官网链接

个人作品配置


  1. 修改content/featured文件夹

    这个文件夹是关于个人作品最上方展示Featured Project的,添加方式为在featured文件夹中新建文件夹再新建一个index.md

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    ---
    date: '1'
    title: 'Wholz'
    cover: './wholz.png'
    github: 'https://github.com/LeKZzzz/wholz'
    external: 'https://github.com/LeKZzzz/wholz'
    tech:
    - Java
    - Python
    - HTML
    - CSS
    - Vue
    - Unity3D
    - MySQL
    ---

    Using VR virtual reality as a carrier, an immersive learning environment is created. Teaching videos are combined with 3D models to realize the visualization of mind map nodes. Users can interact with the LLM to realize real-time question and answer, making the learning content more vivid and intuitive.

    • date:决定展示顺序,越小优先级越高
    • title:project的名字
    • cover:project图片
    • github:指向github地址
    • external:跳转链接
    • cta:使用cta则使用“learn more”按钮来跳转链接
    • tech:标签
  2. 修改content/projects文件夹

    以原有文章为例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    ---
    date: '2019-11-12'
    title: 'Moms Demand Action Mobile App'
    github: ''
    external: 'https://www.upstatement.com/work/moms-demand-action/'
    ios: 'https://apps.apple.com/us/app/demand-action/id1475502876'
    android: 'https://play.google.com/store/apps/details?id=com.momsdemandaction.app'
    tech:
    - NativeScript Vue
    - iOS
    - Android
    company: 'Upstatement'
    showInProjects: false
    ---

    简介
    • date:时间
    • title:标题
    • github:指向github的链接
    • external:跳转链接
    • ios:appstore链接
    • android:google play链接
    • tech:标签
    • company:公司
    • showInProjects:是否在网站首页展示,否则只有/archive里才能看见

    改成中文后不知道为什么一行只有一个project,所以我的网站删去了这部分内容,只保留了archive的跳转

icon配置


  1. src/components/icons中添加新icon的js文件
  2. 仿照已有icon的js文件,将新icon的svg写入新文件
  3. src/components/icons/icon.js中添加新的Icon组件和映射
  4. 在src/components/icons/index.js中添加新的组件
  5. 在src/config.js中添加新的social图标和链接

写在最后


环境问题真是消耗了我很长时间,因为我本是没有接触过react的,所以很多地方都得现查,写这篇博客也是希望读者能少踩我踩过的坑。

模板作者Brittany Chiang着重提到“ All I ask of you all is to not claim this effort as your own.”,所以请不要使用这个模板而声称是自己原创,这种行为只会消耗开源作者的热情,所以请保持在footer链接到Brittany Chiang的开源仓库。

推荐阅读