最近在关注gridsome
,顺带按照视频教程来实现了一个案例并成功部署,在此记录一下学习的过程,以免后面遇到什么坑再回来翻一翻。strapi工程源码 gridsome工程源码
Gridsome是什么?
- 一个免费、开源、基于Vue.js技术栈的静态网站生成器
- 官方网站:gridsome.org/
- GitHub: github.com/gridsome/gr…
静态网站生成器?
- 是使用一系列配置、模板及数据,生成静态HTML文件及相关资源的工具
- 也叫预渲染
- 审查个好难过的网站不需要类似PHP这样的服务器
- 只需要放到支持静态资源的Web Server或cdn上即可运行
静态网站的好处
- 省钱 不需要专业的服务器,只需能托管静态文件的空间即可
- 快速 不经过后端服务器的处理,只传输内容
- 安全 没有后端程序的执行,自然更安全
常见的静态网站生成器
- Jekyll(Ruby)
- Hexo(Node)
- Hugo(Golang)
- Gatsby(Node/React)
- Gridsome(Node/Vue)
- 另外,Next.js合Nuxt.js也能生成静态网站,但是他们更多被认为是SSR框架。 这类静态网站生成器也叫JAMStack,本质上是一种胖前端,调用各种API来实现更多的功能,其实也是一种前后端的模式,只不过离得比较开,甚至前后端来自多个不同的厂商。
静态应用的使用场景
- 不适合有大量路由页面的应用
- 如果站点内有成百上千条路由页面,则预渲染将非常缓慢。当然,如果每次更新只需要做一次,但是可能要花一些时间。大多数人不会最终获得数千条静态路由页面,而只是以防万一。
- 不适合有大量动态内容的应用
- 如果渲染路线中包含特定于用户查看其内容或其他动态资源的内容,则应确保具有可以展示的占位符组件,知道动态内容加载到客户端为止,否则可能会比较怪异。
创建Gridsome项目
首先电脑里要有python环境,其次配置sharp和libvips的地址,因为sharp中包含一些c++文件,并且其依赖包中的libvips包比较大,国内很难安装成功,所以最好是配置一下这两个包的国内地址。
npm config set sharp_binary_host "https://npm.taobao.org/mirrors/sharp"
npm config set sharp_libvips_binary_host "https://npm.taobao.org/mirrors/sharp-libvips"
之后还需要对sharp
中的c++
文件进行编译,需要下载node-gyp
包,此时是需要有python环境的。
npm install -g node-gyp
之后就可以创建Gridsome的项目了
gridsome create my-gridsome-site
此时会自动安装依赖,我们可以打断自己重新安装node_modules。之后运行npm run develop
启动项目的开发模式来展示页面效果,其中pages下的两个.vue文件对应的就是相应的路由。
运行npm run build
则会打包整个项目,生成静态的预渲染出来的html文件。
Gridsome基础-Pages
新建页面
有两种方式可以创建页面。
1、使用文件系统在src
目录下创建对应的.vue
格式的文件,gridsome
会自动生成相应的路由配置,并自动转换成小写。
2、还可以使用相应的API来创建组件。在gridsome.server.js
文件中,调用createPage
方法生成路由。
module.exports = function (api) {
api.loadSource(({ addCollection }) => {
// Use the Data Store API here: https://gridsome.org/docs/data-store-api/
})
api.createPages(({ createPage }) => {
// Use the Pages API here: https://gridsome.org/docs/pages-api/
createPage({
path: '/my-page',
component: './src/templates/MyPage.vue'
})
})
}
动态路由
动态路由的创建也有两种方式。
1、基于文件的动态路由:
src/pages/user/[id].vue => /user/:id
src/pages/user/[id]/settings.vue => /user/_id/settings
2、调用API的方式创建。gridsome.server.js
中调用:
api.createPages(({ createPage }) => {
createPage({
path: '/user/:id(\\d+)',
component: './src/templates/User.vue'
})
})
生成路由重写规则
因为/user/:id
的路由最终会生成路径为/user/_id.html
的文件,而普通的静态web服务器是不会进行处理的,所以要由我们自己去重写动态路由的规则。
集合
假如我们有一个接口,返回一个数组,数组中的每一项为一个对象,将整个数组中的数据展示在页面中,这个是很简单的。
[
{
userid: '1',
id: 1,
title: 'title',
body: 'body'
}
]
但是这种动态获取的数据展示在页面中,是由客户端动态渲染的,并不是预渲染生成的静态页面效果,此时就需要用到集合Collections
。
同样在gridsome.server.js
中调用loadSource
方法添加集合,此时的数据调用的是jsonplaceholder的测试接口。
const axios = require('axios')
module.exports = function (api) {
api.loadSource(async ({ addCollection }) => {
const collection = addCollection('Post')
const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts')
for (const item of data) {
collection.addNode({
id: item.id,
title: item.title,
content: item.body
})
}
})
}
}
每个collections
都会向GraphQL schema
中新增两个字段:post
和allPost
。post
是根据id来获取单个节点,allPost
是获取整个所有的节点。在启动开发服务之后,会有一个GraphQL
的资源管理器,在里面可以查询集合里面的数据。当然,在页面中还是需要使用<page-query></page-query>
标签来获取GraphQL中的数据。
<page-query>
query {
post: allPost{
edges {
node {
id
title
}
}
}
}
</page-query>
此时,从GraphQL
中获取的数据post
会被写入到计算属性的$page
中,可以直接在页面中使用:
<template>
<Layout>
<div>
<h1>Post2 Page</h1>
<ul>
<li v-for="edge in $page.post.edges"
:key="edge.id">
<g-link :to="edge.node.path">{{edge.node.title}}</g-link>
</li>
</ul>
</div>
</Layout>
</template>
<page-query>
query {
post: allPost {
edges {
node {
id
title
path
}
}
}
}
</page-query>
<script>
export default {
name: 'Post2Page'
}
</script>
还可以给定一个参数,来根据参数查询数据。
<page-query>
query ($id: ID!) {
post (id: $id) {
id
title
content
}
}
</page-query>
在GraphQL中变量要以id: ID!表示为参数是ID类型,且不为空。
获取数据之后可以使用$page
来使用。
<div>{{ $page.post.content}}</div>
Gridsome案例
首先我们还是使用gridsome create blog-with-gridsome
的命令创建并启动项目,之后使用github
中的开源模板,最好是将其fork
到自己的账户当中,防止模板文件被作者删除或者做一些其他的修改。
模板的处理
模板下载下来之后要把其中引用的资源加到gridsome
项目中,其中有bootstrap
和@fortawesome/fontawesome-free
两个包,还有两个字体文件要在css
中引入并在main.js
中导入进来。新建src/assets/css/index.css
文件,在该文件中引入字体资源:
@import url("https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700itali");
@import url("'https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800")
在模板中,其实每个页面的头部导航和尾部都是相同的,可以将其放在默认的布局组件中,中间的内容部分可以分别放在不同的组件中。所以最终的Layout
布局组件大概内容为:
<template>
<div class="layout">
<nav>
...
</nav>
<!-- 子组件出口 -->
<slot />
<footer>
...
</footer>
</div>
</template>
相关的模板完成之后,接下来会使用gridsome
的方式来构建纯静态的博客网站。
本地md文件管理文章内容
对博客来说,最重要的功能当然是展示文章
strapi
strapi
本身就是一个先进的内容管理框架,可以轻松的生成我们所需要的API。
下面创建本地的strapi
应用:
npx create-strapi-app my-project --quickstart
之后会自动打开strapi
的注册页面,直接注册就好,注册完成之后就会进入系统的后台。
之后在内容类型生成器中点击创建一个新的Content Type来创建一个集合,有了集合,就可以管理我们的数据了。数据添加完之后不要忘记设置用户的权限,对public权限类型设置count find findone
的权限就可以了,在勾选权限时,右侧会出现对应的接口名称。
注意此处需要科学上网,否则保存不上的
设置完成之后就可以测试对应接口的数据。
http://localhost:1337/posts/count
如果想在strapi
中使用graphql
的方式开获取数据,则需要执行下列操作:
yarn strapi install graphql
安装完之后运行npm run develop
启动
注意此时启动的是my-project项目,并不是gridsome创建的项目!
启动成功之后,打开http://localhost:1337/graphql
就可以使用graphql
来查询strapi
中的数据了。
关于数据预取
要将strapi
集成到gridsome
中,需要使用@gridsome/source-strapi
的一个插件。
// 注意此时是在gridsome项目中的。
npm install @gridsome/source-strapi
之后在gridsome
的配置文件gridsome.config.js
的plugins
中添加如下代码:
{
use: '@gridsome/source-strapi',
options: {
apiURL: 'http://localhost:1337',
queryLimit: 1000,
contentTypes: ['post'],
loginData: {
identifier: '',
password: ''
}
}
}
最后重新启动应用npm run develop
,目的是使刚修改的配置生效,从strapi
中获取到数据
注意这里同时启动两个项目,因为gridsome项目查询的数据是从strapi中获取的,
而strapi中的数据是通过my-project项目从strapi的后台系统拿到的。
如果此时在strapi的后台添加一条数据,在gridsome中是查不到的,因为预渲染是要提前拿到数据再渲染到页面上的,因为加数据是预渲染完成之后的操作,所以预渲染的环节是拿不到新增的数据的,若要解决这个问题,重启下gridsome服务就好了。
关于分页
Gridsome
提供了@paginate
的api来实现分页的功能。可以自定义页数和每页条数。
query ($page: Int) {
posts: allStrapiPost (perPage: 1, page: $page) @paginate {
edges {
node {
id
title
created_at
createds {
id
firstname
lastname
}
tags {
id
title
}
}
}
}
}
Gridsome
还提供了分页组件Pager
,需要像正常组件一样先注册再使用,还需要在page-query
中添加查询语句:
query ($page: Int) {
posts: allStrapiPost (perPage: 1, page: $page) @paginate {
pageInfo {
totalPages
currentPage
}
...
}
}
页面中则要给pager
组件添加一个属性才可以正常使用,这个组件是没有添加样式的,可以根据我们的需要来自己配置,至于页面的话此处不在多说。
<pager :info="$page.posts.pageInfo"></pager>
关于部署
gridsome
项目即博客应用本身可以部署在任何支持静态文件的web
服务里,而strapi
应用需要有node
环境。因为gridsome
项目在启动时需要请求strapi
的服务来获取预渲染的数据,所以要先部署strapi
的应用。
strapi
默认是使用的sqlite
来做数据存储,也可以使用诸如mysql mongodb
等其他的数据库,在官网都有相应的配置项,这里使用mysql
实现。
要想使用mysql
,首先要在服务器里面有mysql
的环境,其次修改strapi
项目的配置文件config\database.js
。配置文件修改如下:
module.exports = ({ env }) => ({
defaultConnection: 'default',
connections: {
default: {
connector: 'bookshelf',
settings: {
client: 'mysql',
host: env('DATABASE_HOST', 'localhost'),
port: env.int('DATABASE_PORT', 3306),
database: env('DATABASE_NAME', 'blog'),
username: env('DATABASE_USERNAME', 'root'),
password: env('DATABASE_PASSWORD', ''),
},
options: {},
},
},
});
这里host
使用的是本机地址,是因为我这里的mysql
和strapi
是在同一台服务器上,如果在不同的服务器的话,这里要写成其他服务器的地址,其次要有一个名字为blog
的数据库,否则部署时会报错找不到对应数据库的。
部署的过程也很简单,直接在云服务器上把源码clone
下来,然后运行npm install npm run build
等命令,直接打包就好,但是这种方式有个缺点,就是当前连接断掉之后,服务也就挂了,所以这里使用pm2
来执行npm
命令。
而gridsome
使用的是vercel
来构建部署的。vercel
可以支持deploy hooks
,可以在strap
添加数据之后自动触发构建。但是这种也有缺点,数据量小是合理的,但数据如果多的话会触发多次构建,这个只能说是各有优略。