使用 Gridsome + Strapi + GraphQL 搭建博客应用

文章输出来源拉勾教育大前端高薪训练营

1、学习体会

加入拉勾大前端课程已经有快5个月的时间了,相比较与以前,自己变得更加忙碌了,因为拉勾大前端课程,目前基本每天下班都在学习,看视频,做笔记~~~😂😂😂,然后因为目前完成了一个大模块的学习任务,所以今晚就来总结下这一个多月以来所学习的内容。主要是 学习了基于 Vue深入SSR的实现、服务器部署相关、封装 Vue.js组件库,以及Vue 3.0的介绍、Vue3.0 Composition APIVue3.0 原理剖析,课程设计到的知识体系非常多,当然讲师在每一个知识点上也讲的通俗易懂,基本一个模块开始就是从基本概念开始讲起,再到将一个组件、API、或者说一个方法的实现、最后当然是深入实战项目啦!除此之外,每一个模块都对应的有大作业,目前这一个模块就有 Vue SSR- Nuxt.js 项目、部署搭建静态的 Blog 应用、 还有Strapi 管理Blog 数据管理系统,在学习过程中,就遇到了非常多的问题,只要跟着做,跟着跑,不知不觉你就会觉得 “啊原来前面那个xx说的是这个xx 啊”的奇妙感觉。

2、课程评价

1、目前大前端的讲师都是拉勾资深前端技术专家, 经验都非常丰富,对于一些难以理解的知识点, 会以一些 XMind 思维导图来讲解,还有一些案例,Demo等来加深我们对知识点的记忆和理解,同时讲师也会不定期的进行直播答疑,给大家解决学习过程遇到的问题。

2、班主任是更多的是收集同学们在学习过程中的问题和督促大家的学习进度,收集每个模块大家不懂的问题,然后与讲师沟通,安排技术大咖直播。每一次直播都是干货满满,比如这次的技术大咖 教瘦给我们讲的就是 从 0 到 1 部署项目,简单来说就是从购买服务器、域名、 -> 部署项目 -> Nginx 配置、HTTPS 配置等等,直播授课过程中也是非常有趣的,看完直播,跟着学,跟着做, 可以完全学会如何去部署一个项目。😄😄😄

3、导师在这4个月中,也是非常的负责,解决了学员提出的无数个问题, 然后进行辅导,告诉是如何让解决的,避免以后踩坑,除此之外,导师也会在学习群中, 丢一些目前高频的面试题,让大家去讨论、思考,最后都是会由导师给出答案的, 这不仅提高了大家的技术能力,还可以让大家了解目前外面的高频试题,😄 ,还有对于目前正在找工作的同学,导师也会帮助大家进行简历指导、毕竟简历优势凸显是加分项。还有很多很多做的非常好的方面。

收获

这一个半月在拉勾课程学习,并且做的事情,分别有

1、对Vue 服务端渲染的学习, 使用nuxt 写了一个项目,并且部署
http://139.224.75.201:3000/

2、对Gridsome 的学习总结,在服务器部署了 Blog 应用

https://blog-frontend-woad.vercel.app/

3、部署了 strapi 内容管理后台系统

http://org.yangjiaxin.cc:1337/admin

4、使用Gridsome 部署了个人博客应用

https://vblog.vercel.app/

5、学习了Vue3.0 响应式原理,Composition API 新特性,笔记在整理中…

3-4 模块需要自己动手造轮子的项目有很多,当然自己在写的过程中也遇到非常多的问题,这里也非常感谢熊熊导师帮忙解决,还有其他同学的帮忙! 🌹🌹🌹 一起帮我解决了服务端部署的问题,最后成功将项目部署在服务端中,在这几个月的时间,让我学会了很多新技能,学会了如何在服务器中部署项目, GraphQL 查询数据, Strapi的使用、深入Vue.js 3.0 源码解析等等, 以下就是目前一个多月整理出来的学习笔记,后续有更多的内容也会陆续分享出来。

Blog 应用介绍

有了上一篇 Gridsome 的基础,这篇文章我们将来搭建一个 SSG 静态站点,如下图所示

地址: https://startbootstrap.github.io/startbootstrap-clean-blog/

在这里插入图片描述

1、什么是Gridsome

官网 https://www.gridsome.cn/

  • Gridsome 是基于 Vue 构建的 Jamstack 框架,让开发人员可以轻松的构建静态生成的网站和应用程序,
  • 使用Gridsome,默认情况下可以获得几乎完美的页面响应速度
  • Gridsome 使用的技术工具是现在最为广泛应用的,使用Vue.js,GraphQL和Webpack构建网站,接受度会更高,Gridsome 包含了热重新加载和 Node.js 的所有功能

2、Gridsome 初始化项目

安装 Gridsome 脚手架

yarn global add @gridsome/cli
npm install --global @gridsome/cli

创建一个 Gridsome 项目

gridsome create my-gridsome-site

cd my-gridsome-site

gridsome develop
// 访问
http://localhost:8080
// 开始愉快的编码 🎉👏

在这里插入图片描述

3、处理首页模版

参考 startbootstrap-clean-blog , 将代码 克隆到本地,
https://github.com/StartBootstrap/startbootstrap-clean-blog

先处理 index 中的 css 资源文件,在 startbootstrap-clean-blog 项目的 index 页面中,将 css文件提取到 我们的 Gridsome 项目中, 其他 HTML 也是一样的。

npm install bootstrap
npm install @fortawesome/fontawesome-free

// 在 main.js 中引入
import 'bootstrap/dist/css/bootstrap.min.css'
import '@fortawesome/fontawesome-free/css/all.min.css'
import './assets/index.css'

Google 字体文件 可以在 assets 中创建一个 index.css

// asstes/index.css
@import url("https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic");
@import url("https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800");

// 将clean-blog css 复制到assets/index.css 文件中

pages/index.vue 将页面内容替换成 startbootstrap-clean-blog 的页面内容,在后续我们都这么来做。

<!-- Navigation -->
  <nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
    <div class="container">
      <a class="navbar-brand" href="index.html">Start Bootstrap</a>
      <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
        Menu
        <i class="fas fa-bars"></i>
      </button>
      <div class="collapse navbar-collapse" id="navbarResponsive">
        <ul class="navbar-nav ml-auto">
          <li class="nav-item">
            <a class="nav-link" href="index.html">Home</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="about.html">About</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="post.html">Sample Post</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="contact.html">Contact</a>
          </li>
        </ul>
      </div>
    </div>
  </nav>

  <!-- Page Header -->
  <header class="masthead" style="background-image: url('img/home-bg.jpg')">
    <div class="overlay"></div>
    <div class="container">
      <div class="row">
        <div class="col-lg-8 col-md-10 mx-auto">
          <div class="site-heading">
            <h1>Clean Blog</h1>
            <span class="subheading">A Blog Theme by Start Bootstrap</span>
          </div>
        </div>
      </div>
    </div>
  </header>

  <!-- Main Content -->
  <!-- Footer -->
<script>
export default {
  metaInfo: {
    title: "Hello, world!",
  },
  name: 'HomePage'
};
</script>

这里直接打开 会发现图片没有显示 所以需要将 index.vue中的资源路径修改下,例如background-image: url('/img/home-bg.png') ,当然我们还需要将图片拷贝到我们自己项目上
再次打开页面,就可以看到如下效果

4、处理其他模版

这里我们会发现其他页面的头部和首页的一样,那么我们可以将头部抽离出去,变成公共的部分,将Index.vue 的 Navigation 和底部的 Footer 部分 放到 layouts/Default.vue 文件中,主要内容直接用 <slot /> 插槽就行。

<template>
  <div class="layout">
    <!-- Navigation -->
    <nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
      <div class="container">
        <a class="navbar-brand" href="index.html">Start Bootstrap</a>
        <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
          Menu
          <i class="fas fa-bars"></i>
        </button>
        <div class="collapse navbar-collapse" id="navbarResponsive">
          <ul class="navbar-nav ml-auto">
            <li class="nav-item">
              <a class="nav-link" href="index.html">Home</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="about.html">About</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="post.html">Sample Post</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="contact.html">Contact</a>
            </li>
          </ul>
        </div>
      </div>
    </nav>

    <!-- 默认插槽 -->
    <slot />

    <!-- Footer -->
    <footer>
      <div class="container">
        <div class="row">
          <div class="col-lg-8 col-md-10 mx-auto">
            <ul class="list-inline text-center">
              <li class="list-inline-item">
                <a href="#">
                  <span class="fa-stack fa-lg">
                    <i class="fas fa-circle fa-stack-2x"></i>
                    <i class="fab fa-twitter fa-stack-1x fa-inverse"></i>
                  </span>
                </a>
              </li>
              <li class="list-inline-item">
                <a href="#">
                  <span class="fa-stack fa-lg">
                    <i class="fas fa-circle fa-stack-2x"></i>
                    <i class="fab fa-facebook-f fa-stack-1x fa-inverse"></i>
                  </span>
                </a>
              </li>
              <li class="list-inline-item">
                <a href="#">
                  <span class="fa-stack fa-lg">
                    <i class="fas fa-circle fa-stack-2x"></i>
                    <i class="fab fa-github fa-stack-1x fa-inverse"></i>
                  </span>
                </a>
              </li>
            </ul>
            <p class="copyright text-muted">Copyright &copy; Your Website 2020</p>
          </div>
        </div>
      </div>
    </footer>
  </div>
</template>

Post 页面
将Post.html 拷贝到 Gridsome pages/post.vue 中,新建post.vue

  <!-- Page Header -->
  <template>
  	<Layout>
	  <header class="masthead" style="background-image: url('img/post-bg.jpg')">
	    <div class="overlay"></div>
	    <div class="container">
	      <div class="row">
	        <div class="col-lg-8 col-md-10 mx-auto">
	          <div class="post-heading">
	            <h1>Man must explore, and this is exploration at its greatest</h1>
	            <h2 class="subheading">Problems look mighty small from 150 miles up</h2>
	            <span class="meta">Posted by
	              <a href="#">Start Bootstrap</a>
	              on August 24, 2019</span>
	          </div>
	        </div>
	      </div>
	    </div>
	  </header>

	  <!-- Post Content -->
	  <article>
	    <div class="container">
	      <div class="row">
	        <div class="col-lg-8 col-md-10 mx-auto">
	          <p>Never in all their history have men been able truly to conceive of the world as one: a single sphere,</p>
	        </div>
	      </div>
	    </div>
	  </article>
  </Layout>
</template>

在这里插入图片描述
其他页面 的处理方式也是一样的,需要注意的是每个页面都需要使用 <Layout></Layout> 来进行包裹,那么剩下的 About,contact 页面大家可以自己去处理下。

5、使用本地md文件管理文章内容

在准备完了项目所需要的模版文件之后,接下来我们就要做核心的功能了,核心作用就是展示,文章,详情等内容,文章主要就是 Markdown 文件了。

使用 Importing Data
在这里插入图片描述

npm install @gridsome/source-filesystem
yarn add @gridsome/source-filesystem


// gridsome.config.js
module.exports = {
  plugins: [
    {
      use: '@gridsome/source-filesystem',
      options: {
        path: './content/blog/**/*.md',
        typeName: 'BlogPost'
      }
    }
  ]
}

在 根目录下创建两个 Markdown 文件,内容可自己定义,配置完之后,重启才能生效
在这里插入图片描述
打开 http://localhost:8080/___explore 使用 GraphQL 查询数据,语法如下

query {
  allBlogPost {
    edges {
      node {
        id
        title
      }
    }
  }
}
// 或者查询单个
query {
  strapiPost (id: 1) {
    id
    title
    content
    created_by {
      id
      firstname
      lastname
    }
    cover {
      url
    }
    tags {
      id
      title
    }
  }
}

在这里插入图片描述

6、 Strapi 介绍

Strapi 官网

Strapi是一种灵活的开源Headless CMS,它使开发人员可以自由选择自己喜欢的工具和框架,同时还允许编辑者轻松管理和分发其内容。通过使管理面板和API可通过插件系统进行扩展,Strapi使全球最大的公司能够加速内容交付,同时构建优美的数字体验。

特点

  • 自定义内容结构
  • 轻松的内容管理
  • 开发人员友好的API
  • 角色和权限
  • 插件系统

7、Strapi快速入门指南

yarn create strapi-app my-project --quickstart

npx create-strapi-app my-project --quickstart

访问 http://localhost:1337/admin

注册 登录
在这里插入图片描述


创建一个新的Content Type



  • 有了结合, 以及集合中的数据,那么如何在外部来获取到应用的数据呢

  • Content API

  • 因为strapi 内置了角色权限管理的功能,所以我们需要开放对应的权限,需要授权就能访问

  • Authenticated 分配所有权限, 不同的用户可以拥有不同的角色,而不同的角色拥有不同的权限


8、使用Strapi 接口数据

如何在外部获取到 Strapi 的数据呢,Strapi 默认提供了一个插件 API Endpoints

API Endpoints

角色/权限 分配权限

在这里插入图片描述

9 、访问受保护的API

给这个用户开放所有权限
在这里插入图片描述
在这里插入图片描述
通过Axios 请求接口数据

import axios from 'axios';

const token = 'YOUR_TOKEN_HERE';
// Request API.
axios
  .get('http://localhost:1337/posts', {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  })
  .then(response => {
    // Handle success.
    console.log('Data: ', response.data);
  })
  .catch(error => {
    // Handle error.
    console.log('An error occurred:', error.response);
  });

10、通过GraphQL 访问 Strapi

yarn strapi install graphql

// 在市场可视化面板也可以安装

安装完毕 直接打开 http://localhost:1337/graphql 即可访问

// localhost:1337/posts
// 语法
query {
  posts {
    id
    title
    content
  }
}

11、将 Strapi 数据预取到Gridsome 应用中

在渲染应用之前,提前拿到数据,然后渲染静态内容,我们需要借助Gridsom的插件

// @gridsome/source-strapi
yarn add @gridsome/source-strapi

@gridsome/source-strapi

// 在gridsome.config.js中配置
export default {
  plugins: [
    {
      use: '@gridsome/source-strapi',
      options: {
        apiURL: 'http://localhost:1337', // 接口地址
        queryLimit: 1000, // Defaults to 100
        contentTypes: ['post'] // 对应的数据类型,接口名称
        // Possibility to login with a Strapi user,
        // when content types are not publicly available (optional).
        // loginData: {
        //   identifier: '',
        //   password: ''
        // }
      }
    }
  ]
}

安装完毕记得重启

给接添加数据字段



记得保存

11、设计文章标签数据类型

  • 对Post 进行扩展 css 相关 vue 相关 同一个文章属于多个标签
  • 新建一个content type 需要引用
  • 多对多的关系,一个标签有多篇文章



通过 http://localhost:1337/graphql查询数据 ,记得在角色权限上给Public 开放权限

12、 展示文章列表页

// index.vue
<page-query>
query {
  posts: allStrapiPost {
    edges {
      node {
        id
        title
        created_by {
          id
          firstname
          lastname
        }
        tags {
          id
          title
        }
        created_at
      }
    }
  }
}
</page-query>
<div class="post-preview" v-for="edge in $page.posts.edges" :key="edge.node.id">
  <a href="post.html">
    <h2 class="post-title">{{ edge.node.title }}</h2>
    <!-- <h3 class="post-subtitle">Problems look mighty small from 150 miles up</h3> -->
  </a>
  <p class="post-meta">
    Posted by
    <a
      href="#"
    >{{ edge.node.created_by.firstname + edge.node.created_by.lastname }}</a>
    on {{ edge.node.created_at }}
  </p>
  <p>
    <span v-for="tag in edge.node.tags" :key="tag.id">
      <a href>{{ tag.title }}</a>
      &nbsp;&nbsp;
    </span>
  </p>
  <hr />
</div>

完成之后可以看到数据是从Strapi 中获取的

13、文章列表分页

Paginate data

<template>
  <Layout>
    <ul>
      <li v-for="edge in $page.allBlogPost.edges" :key="edge.node.id">
        {{ edge.node.title }}
      </li>
    </ul>
    <Pager :info="$page.allBlogPost.pageInfo"/>
  </Layout>
</template>

<script>
import { Pager } from 'gridsome'

export default {
  components: {
    Pager
  }
}
</script>

<page-query>
query ($page: Int) {
  allBlogPost(perPage: 10, page: $page) @paginate {
    pageInfo {
      totalPages
      currentPage
    }
    edges {
      node {
        id
        title
      }
    }
  }
}
</page-query>

14、Gridsome-展示文章详情

templates: {
    StrapiPost: [
      {
        path: '/post/:id',
        component: './src/templates/Post.vue'
      }
    ]
  }


// post.vue

<page-query>
  query ($id: ID!) {
    post: strapiPost (id: $id) {
      id
      title
      content
      cover {
        url
      }
      tags {
        id
        title
      }
    }
  }
</page-query>

通过g-link 跳转

<div class="post-preview" v-for="edge in $page.posts.edges" :key="edge.node.id">
  <g-link :to="'/post/' + edge.node.id">
    <h2 class="post-title">{{ edge.node.title }}</h2>
    <!-- <h3 class="post-subtitle">Problems look mighty small from 150 miles up</h3> -->
  </g-link>
  <p class="post-meta">
    Posted by
    <g-link
            href="#"
            >{{ edge.node.created_by.firstname + edge.node.created_by.lastname }}			  </g-link>
    on {{ edge.node.created_at }}
  </p>
  <p>
    <span v-for="tag in edge.node.tags" :key="tag.id">
      <a href>{{ tag.title }}</a>
      &nbsp;&nbsp;
    </span>
  </p>
  <hr />
</div>

15、处理Markdown格式的文章内容

通过使用 markdown-it

yarn add markdown-it
import MarkdownIt from 'markdown-it'
const md = new MarkdownIt()
export default {
  name: 'PostPage',
  methods: {
    mdToHtml (markdown) {
      return md.render(markdown)
    }
  }
};
<div class="col-lg-8 col-md-10 mx-auto"
	v-html="mdToHtml($page.post.content)">
 </div>

16、文章标签

在gridsome.config.js 配置文件中加入tag, 然后重新启动

options: {
  apiURL: 'http://localhost:1337',
    queryLimit: 1000, // Defaults to 100
      contentTypes: ['post', 'tag']
  // Possibility to login with a Strapi user,
  // when content types are not publicly available (optional).
  // loginData: {
  //   identifier: '',
  //   password: ''
  // }
}
query {
  allStrapiTag {
    edges {
      node {
        id
        title
      }
    }
  }
}

17、基本设置

创建一个单个的集合 Create new single type

// gridsome.config.js
singleTypes: ['general']


// index.vue
// 获取数据语法
 general: allStrapiGeneral {
    edges {
      node {
        id
        title
        subtitle
        cover {
          url
        }
      }
    }
  }
import { Pager } from 'gridsome'
export default {
  metaInfo: {
    title: "Hello, world!",
  },
  name: 'HomePage',
  components: {
    Pager
  },
  computed: {
    general () {
      return this.$page.general.edges[0].node
    }
  }
};
 <header class="masthead" 
    :style="{
      backgroundImage: `url(http://localhost:1337${general.cover.url})`
    }">
      <div class="overlay"></div>
      <div class="container">
        <div class="row">
          <div class="col-lg-8 col-md-10 mx-auto">
            <div class="site-heading">
              <h1>{{ general.title}}</h1>
              <span class="subheading">{{general.subtitle}}</span>
            </div>
          </div>
        </div>
      </div>
    </header>

18、联系我

新建一个content-type contace 分别创建name,email,phone,message 字段


分配权限 create

yarn install axios

import axios from 'axios'
export default {
  name: "ContactPage",
  data () {
    return {
      from: {
        name: '',
        email: '',
        phone: '',
        message: ''
      }
    }
  },
  methods: {
    async onSubmit () {
      try {
        await axios({
          method: 'POST',
          url: 'http://localhost:1337/contacts',
          data: this.from
        })
        window.alert('提交成功')
      } catch (err) {
        window.alert('提交失败, 请稍后重试')
      }
    }
  }
};

ok! 到这里我们通过Gridsome 搭建了一个Blog 应用,后续还有部署相关,也就是将Blog 应用部署到服务器,那么就看后续啦

完整代码请点击这里

线上预览

感谢

最后感谢您花宝贵的时间阅读本文, 如果本文对你有帮助的话,就给本文点个赞吧,您的肯定是我前进的最大动力~🌹🌹🌹

更多笔记

1、Vue 3.0 Composition API 快速入门

https://blog.csdn.net/sinat_35349493/article/details/108817022

2、Vue3.0 Composition API -TodoList 案例

https://blog.csdn.net/sinat_35349493/article/details/108816475

3、Vue3.0 响应式原理

https://blog.csdn.net/sinat_35349493/article/details/108819469

4、Vue3.0介绍

https://blog.csdn.net/sinat_35349493/article/details/108804900

5、使用 Gridsome + Strapi + GraphQl 搭建博客应用

https://blog.csdn.net/sinat_35349493/article/details/108569386

6、Gridsome 生成静态站点基础

https://blog.csdn.net/sinat_35349493/article/details/108569363

  • 11
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值