本博客案例使用静态站点生成器 Gridsome 快速生成静态网站,后台的数据使用 Strapi 进行管理,案例中使用 GraphQL 访问 Strapi 中的数据,最后通过 Vercel 来自动化部署。
案例介绍
功能介绍:实现一个个人博客的基本功能
页面UI:参照startbootstrap-clean-blog、github地址
案例目的:体验使用Gridsome解决实际开发过程中的问题
案例实现
创建项目
1 | $ gridsome create blog-with-gridsome |
本地启动项目
1 | $ npm run develop |
浏览器访问http://localhost:8080/
,如下:
基础配置
根据 startbootstrap-clean-blog/index.html
,处理首页模板
- 安装相关依赖
1 | $ npm i bootstrap @fortawesome/fontawesome-free |
- 创建
src/assets/css/index.css
文件,引入项目中需要的goole字体文件,并导入其他需要的css样式
1 | @import url('https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic'); |
- 在
src/main.js
中,全局引入项目中所需的静态资源
1 | import 'bootstrap/dist/css/bootstrap.min.css' |
公共模板
每个页面的头部和底部都是一样的,只有中间部分不一样,因此将其公共部分提取出来
将 startbootstrap-clean-blog/index.html
的 body
部分的 Navigation
和 Footer
内容,复制到 src/layout/Default.vue
中
1 | <template> |
首页模板
将 startbootstrap-clean-blog/index.html
的 body
部分的 Page Header
和 Main Content
内容,复制到 src/pages/index.vue
中
- src/pages/index.vue ,创建根节点
- 将 startbootstrap-clean-blog/img 目录拷贝到 static/img 目录下,并修改 url 路径,如下所示:
1 | <template> |
其他页面模板,与首页模板相似
处理页面数据
使用本地md文件管理文章详情
适用范围:适用于需求比较简单的情况
注意:
1.Gridsome有三种获取动态数据的方式:通过插件、特定的API、本地文件。
2.使用@gridsome/source-filesystem获取本地文件内容,它的作用是将本地文件内容转换为在组件中能够被GraphQL请求的形式。
3.使用@gridsome/source-filesystem处理.md文件时,必须搭配将文件转换为html的插件@gridsome/transformer-remark,否则会报错
4.重启项目后配置的插件才会生效
- 安装插件和 markdown 的转换器
1 | # 安装@gridsome/source-filesystem |
- 在
gridsome.config.js
中,配置插件,并创建对应路径的 md 文件
1 | module.exports = { |
- 新建
content/blog/article1.md
,执行npm run develop
启动项目,浏览器访问http://localhost:8080/___explore
,看到blogPost
、allBlogPost
,说明插件引入成功
在GraphQL Data资源管理器里面输入查询语句,获取.md的内容
在页面中使用.md格式的数据
- 先通过
<page-query>
获取GraphQL中的md格式的数据 - 使用markdown-it插件将.md格式的字符串转换为html字符串
- 使用v-html指令在页面上显示转换后的html字符串
- 先通过
例如,获取存放在md中的欢迎页数据
- 安装插件
1 | $ npm install markdown-it --save |
- 将插件引入页面,进行转换
1 | <template> |
注意 :如果获取的数据中包含html,必须启用markdown-it插件的【在源码中启用 html 标签】,否则使用v-html指令显示的还是html 字符串
markdown-it插件的所有的选项列表(默认情况下)如下:
1 | var md = require('markdown-it')({ |
Strapi - 处理来自后台管理系统的数据
Strapi简介
- Strapi - 一款比较通用的内容管理系统(CMS:Content Management System),可以帮助我们轻松的管理内容,官网地址:strapi
- Strapi可以管理各种形式的内容,如博客的文章、商品、用户评论、用户信息等
- Strapi特点
- 可以管理用户自定义的任何内容
- 用户管理内容的方式简单易操作
- 对开发者而言,提供了友好的API
- 支持角色权限的管理
- 可以通过插件系统扩展功能
- 支持用户自定义功能
Strapi使用
- 创建一个Strapi项目,建议使用npx
1 | # yarn |
执行 npm run develop
命令启动项目,默认打开浏览器,注册一个管理员账号,登录/内容类型生成器/创建一个新的content type,创建文章列表及文章详情内容,这里就不在叙述,具体使用参考:Strapi使用
将Strapi后台管理系统的数据集成到Gridsome项目中
- 安装@gridsome/source-strapi
1
$ npm install @gridsome/source-strapi --save
- 在 blog-with-gridsome/gridsome.config.js 文件中,配置插件
@gridsome/source-strapi
信息
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
27module.exports = {
plugins: [
{
use: '@gridsome/source-filesystem',
options: {
typeName: 'BlogPost',
// 抓取文件的路径,即抓取哪些文件
path: './content/blog/**/*.md',
}
},
{
use: '@gridsome/source-strapi',
options: {
apiURL: 'http://localhost:1337', // 接口地址
queryLimit: 1000, // Defaults to 100
contentTypes: ['post'], // 查询的数据类型
// singleTypes: ['impressum'], // 单个节点
// Possibility to login with a Strapi user,
// when content types are not publicly available (optional).
// loginData: { // 登录信息
// identifier: '',
// password: ''
// }
}
}
],
}
执行 npm run develop
重新启动项目,访问 http://localhost:8080/___explore
,如下图,说明数据集成成功
注意:
1、在项目中配置好@gridsome/source-strapi
,启动项目之前,必须在strapi后管添加用户、分配相应的权限、并登录成功之后,gridsome集成strapi的数据才能成功,否则会报403。
2、在strapi后管添加数据后,由于是预渲染,gridsome必须重启才能拿到最新的数据。
具体实现
设计数据模型
- 计文章的字段,如图所示:
- 设计标签时,需创建一个 引用 字段,用于表示 标签和文章的关系
- 标签字段列表,如图所示:
页面实现
展示文章列表和分页
1、使用
<page-query>
获取GraphQL 数据层获取数据,进行页面数据动态渲染
2、使用Gridsome自带的分页插件pagination处理分页,插件会自动在页面路由中获取当前的页码pages/index.vue
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<template>
<Layout>
<!-- Main Content -->
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<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>
</a>
<p class="post-meta">
Posted by
<a href="#">Start Bootstrap</a>
on {{ edge.node.create_at }}
</p>
<p>
<span v-for="tag in edge.node.tags" :key="tag.id">
<g-link :to="'/tags/' + tag.id">
{{ tag.title }}
</g-link>
</span>
</p>
<hr />
</div>
<!-- Pager -->
<Pager :info="$page.posts.pageInfo" />
</div>
</div>
</div>
</Layout>
</template>
<page-query>
query ($page: Int) {
posts: allStrapiPosts (perPage: 1, page: $page) @paginate {
pageInfo {
totalPages
currentPage
}
edges {
node {
id
title
created_at
tags {
id
title
}
}
}
}
}
</page-query>
<script>
// 引入分页组件
import { Pager } from 'gridsome'
export default {
components: {
Pager
}
};
</script>展示文章详情
在
gridsome.config.js
中,配置模板1
2
3
4
5
6
7
8
9
10
11
12module.exports = {
siteName: 'Gridsome',
...
templates: {
//StrapiPost是由@gridsome/source-strapi插件options的typeName + contentTypes
StrapiPost: [
{
path: '/post/:id', //模板路径
component: './src/templates/Post.vue' //模板组件
}]
}
}src/pages/Index.vue
使用<g-link>
实现点击文章标题时,跳转到文章详情页
1 | ... |
src/templates/Post.vue
使用<page-query>
获取文章详情并展示,由于文章的内容有可能是markdown
格式的,所以在显示时需要先使用markdown-it
插件将.md
格式的字符串转换为html
字符串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<template>
<Layout>
<!-- Page Header -->
<header class="masthead" :style="{backgroundImage: `url(http://localhost:1337${$page.post.cover.url})`}">
<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>{{$page.post.title}}</h1>
<span class="meta">Posted by
<a href="#">eline</a>
{{$page.post.created_at}}
</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" v-html="mdToHtml($page.post.content)"></div>
</div>
</div>
</article>
</Layout>
</template>
<page-query>
query($id: ID!){
post:strapiPost(id:$id){
id
title
content
created_at
cover{
name
url
}
tags{
id
title
}
}
}
</page-query>
<script>
import MarkdownIt from 'markdown-it'
const md = new MarkdownIt();
export default {
name:'PostPage',
metaInfo: {
title: 'Hello, world!'
},
methods:{
mdToHtml(markdown){
return md.render(markdown);
}
}
}
</script>
设置网站的基本信息
主要设置网站的标题、副标题以及封面
在 strapi 中新增一个 Single Type(单一类型),名称为 General,并添加三个字段,如图所示:
在 gridsome.config.js 的 plugins 选项中,进行配置
1
2
3
4
5
6
7
8
9
10module.exports = {
plugins: [
{
options: {
...
singleTypes: ['General'], // 单个节点
}
}
]
}在
src/pages/index.vue
中,读取 GraphQL 数据层 的数据,并在视图中渲染,代码详见src/pages/index.vue
联系我页面,文章标签页面
这里就不在叙述,代码详见blog-with-gridsome
部署
部署 Strapi
准备工作
- 支持 Node 的服务器,本次部署在ucloud服务器中
- 数据库: 建议 MySQL 或者 MongoDB,本次项目中使用的是MySQL
- 项目代码blog-backend
- 修改
config/database.js
,将原来的sqlite
的配置,修改为mysql
的配置
1 | module.exports = ({ env }) => ({ |
项目中需要安装mysql,此项目安装的版本是2.18.1
ucloud服务其中安装Mysql
- 检测系统是否自带安装 MySQL
1 | $ rpm -qa | grep mysql |
如有,类似mysql-libs-5.1.52-1.el6_0.1.x86_64那可以选择进行卸载:
1 | rpm -e mysql-libs-5.1.52-1.el6_0.1.x86_64 // 普通删除模式 |
- 安装yum源
1 | # 下载 |
检查是否安装成功
1 | yum repolist enabled | grep "mysql.*-community.*" |
如下图所示表示安装成功:
- 安装MySQL
1 | $ sudo yum install -y mysql-community-server |
- 启动MySQL服务
因为只有启动了MySQL服务,才会产生/var/log/mysqld.log文件,初始root密码在这个文件目录下
启动mysql服务(在CentOS7下,启动和关闭服务的命令是systemctl start|stop)
1 | # 启动Mysql |
Mysql启动成功后如下图所示:
- 查看mysql的初始密码
首次使用时候查看初始密码并登录Mysql修改密码
1 | # 查看初始密码 |
- 开启远程访问,创建数据库设置访问权限
- 查看用户表select host,user from user;看到当前host是localhost只允许本地访问,所以需要创建一个可以远程访问数据的的user,这里为blog,具体步骤如下:
1 | # 进入数据库 |
- 创建用户
1 | # 更新user用户表,创建blog用户 |
- 创建项目中使用的数据库名称都为blog
1 | # 创建数据库 |
- root用户新建了一个数据库blog并设置密码,并赋权限给用户blog
1 | mysql> grant all privileges on blog.* to blog identified by '123456Blog+'; |
执行脚本时候报错
1 | [Err] 1044 - Access denied for user 'root'@'%' to database 'blog' |
报错可以看出blog没有权限,查询用户表看blog用户权限
查看用户表
1 | mysql> select Host,User,Grant_priv,Super_priv from mysql.user; |
可以看到现在这两个权限都是N, 权限设置为Y,然后重启mysql,在此grant脚本时候就正常了
1 | mysql> update mysql.user set Grant_priv='Y',Super_priv='Y' where user = 'blog' and host = '%'; |
- 查看数据库端口号
查看端口监听状态,默认是3306
1 | mysql> show global variables like 'port'; |
代码上传到服务器中并启动项目
- 登录服务,将
blog-backend
代码上传到服务器中,并安装相关依赖,构建打包
1 | # 登录服务 |
- 启动项目
Strapi 默认端口号是1337,需要在ucloud服务器中开放1337端口号
具体步骤详见NuxtJS项目本地部署和自动化部署中的UCloud服务器防火墙设置部分
启动项目推荐使用pm2,使用npm run start
直接启动项目,会占用命令行应用,当退出时,则服务也会停止。因此,不建议使用。
pm2详解见NuxtJS项目本地部署和自动化部署中的使用PM2启动Node服务部分
设置服务名称并启动服务
1 | # 服务启动的name设置为strapi |
启动成功如下图所示:
登录 http://106.75.181.60:1337
服务器IP + 端口号(1337),将项目中所需的数据进行配置,如图所示:
角色和权限,必须要进行配置,否则无法调用接口
本地服务联通远程 Strapi
具体实现
- 创建
.env.development
和.env.production
环境文件,配置GRIDSOME_API_URL
,代码如下:
1 | GRIDSOME_API_URL=http://123.57.28.48:1337 |
- 将
gridsome.config.js
中配置的 apiURL ,修改为环境变量GRIDSOME_API_URL
1 | apiURL: process.env.GRIDSOME_API_URL, // 接口地址 |
Vercel – 部署 Gridsome 应用
使用 Vercel 进行静态应用项目的部署。
基本使用
登录 Vercel,可以使用第三方账户,也可以自行注册(在此使用 GitHub 账户登录)
这里我使用的是gitHub登录,集成到github中
登录以后,新建项目,可以导入 Git 仓库中静态应用项目,或者克隆其他模板(在此导入 GitHub 中已存在的仓库)
这里使用Import Git Repository方式导入项目,点击 Continue with GitHub,选择 GitHub 仓库,进行导入
点击仓库名称右侧的 import 按钮,展示基本配置,点击 Deploy ,开始安装依赖,构建发布
构建成功后点击 Visit,即可访问生成的静态站点
配置自动构建
当数据改变时,需要告诉 Vercel,触发自动构建。
在 Vercel 中,找到构建的应用项目,然后点击进入
点击 Settings ,然后点击 Git,找到 Deploy Hooks,创建部署钩子,生成链接地址
然后,进入 Strapi 内容管理平台,添加 Webhooks
填入名称、请求地址(指的是在 Vercel 中生成的地址),并点击保存
在 Strapi 中,添加数据或者是Github中更新代码,都会在Vercel中自动构建,在 Vercel 的应用项目中,点击 Deployments既可以查看正在构建的项目
注意:Vercel 页面可能会有延时,可以刷新 Vercel 页面。
完整代码
静态站点项目前端代码:blog-with-gridsome
静态站点项目后端代码:blog-backend
应用访问地址:https://blog-with-gridsome-six.vercel.app/