Default starter for Gridsome
This is the project you get when you run gridsome create new-project
.
1. Install Gridsome CLI tool if you don't have
npm install --global @gridsome/cli
2. Create a Gridsome project
-
gridsome create my-gridsome-site
to install default starter -
cd my-gridsome-site
to open the folder -
gridsome develop
to start a local dev server athttp://localhost:8080
- Happy coding ????
項目搭建過程
-
項目初始化
- Fork此項目到自己的倉庫
- 將 Fork 到的項目添加到當(dāng)前工作區(qū),便于復(fù)制
- 安裝 bootstrap 和@fortawesome/fontawesome-free npm 方式
- 在 main.js 中加載全局樣式
- 加載谷歌字體 assets/css/index.css 中 @import 方式引入
- 引入樣式
- 將 index.html 中的內(nèi)容復(fù)制到 pages/index.vue 中
- 啟動項目依沮,訪問正常碧库,需要復(fù)制圖片到 static/img/目錄下板乙,修改圖片路徑為/
- 將<nav>和<footer>標(biāo)簽放入 layouts/Default.vue 中魄藕,添加默認(rèn)插槽<slot />
- 添加頁面 Post.vue何恶、About.vue漠嵌、Contact.vue敬矩,初始化頁面完成
-
使用本地 md 文件管理文章內(nèi)容
- 在 GridSome 官網(wǎng)找到source-filesystem插件概行,轉(zhuǎn)化文件內(nèi)容為可以在組件中使用 GraphQL 獲取的內(nèi)容。
-
npm install @gridsome/source-filesystem
安裝 - 在 gridsome.config.js 的 plugins 中進(jìn)行配置
plugins: [ { use: '@gridsome/source-filesystem', // 插件 options: { typeName: 'BlogPost', // 類型弧岳,對應(yīng)GraphQL中的查詢 path: './content/blog/**/*.md', // 文件路徑 }, }, ],
- 創(chuàng)建 content/blog/article1.md 和 arttcle2.md 文件
- 重啟項目凳忙,報錯
Error: No transformer for 'text/markdown' is installed.
- 原因是需要 Gridsome 的 Markdown 變換器@gridsome/transformer-remark,安裝并重啟
- 打開 GraphQL 操作頁面禽炬,查詢數(shù)據(jù)得到結(jié)果涧卵,然后既可以渲染到頁面上
query { allBlogPost { edges { node { id path title content } } } }
使用 strapi 接口數(shù)據(jù)
-
快速開始安裝并啟動 strapi 應(yīng)用,安裝成功打開http://localhost:1337/腹尖,創(chuàng)建 Content Type柳恐,比如 post,添加 id热幔、title 和 content 字段
-
分配權(quán)限
需要給 publish 角色以下權(quán)限乐设,就可在 postman 中使用
-
-
使用 postman 測試,符合 REST API 的規(guī)范
修改 Authenticated 權(quán)限绎巨,全選近尚,添加用戶,設(shè)置角色為 Authenticated
-
在 postman 測試登陸
localhost:1337/auth/local { "identifier": "756638369@qq.com", "password": "123456" } { "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjE3MjM2NTM1LCJleHAiOjE2MTk4Mjg1MzV9.8Ha5sZxLwSKERdQH3OOvWU9qNsGw2fabpL4uxuChJvQ", "user": { "id": 1, "username": "wangxiang", "email": "756638369@qq.com", "provider": "local", "confirmed": false, "blocked": false, "role": { "id": 1, "name": "Authenticated", "description": "Default role given to authenticated user.", "type": "authenticated" }, "created_at": "2021-04-01T00:20:37.396Z", "updated_at": "2021-04-01T00:20:37.405Z" } }
得到 jwt 就可以請求了
可以使用 GraphQL 進(jìn)行安裝场勤,安裝 npm run strapi install graphql
安裝完成肿男,打開 http://localhost:1337/graphql即可訪問
如何把 strapi 數(shù)據(jù)通過預(yù)取的方式集成到 gridsome 中介汹,想要實現(xiàn)渲染前就拿到 strapi 中的數(shù)據(jù),借助 gridsome 插件 strapi
-
使用
export default { plugins: [ { use: '@gridsome/source-strapi', options: { apiURL: 'http://localhost:1337', queryLimit: 1000, // Defaults to 100 contentTypes: ['article', 'user'], singleTypes: ['impressum'], // Possibility to login with a Strapi user, // when content types are not publicly available (optional). loginData: { identifier: '', password: '', }, }, }, ], }
重啟項目舶沛,啟動完成后就可以在 grapjQL 中訪問 strapi 中的數(shù)據(jù)了
-
strapi 新增或刪除數(shù)據(jù)后,gridsome 需要重啟項目窗价,因為在啟動過程中拉取數(shù)據(jù)
刪除 post如庭,重新創(chuàng)建,添加字段 title撼港、content坪它、cover、is_publish帝牡、created_name 關(guān)聯(lián) user
創(chuàng)建 tags
重啟 gridsome往毡,發(fā)現(xiàn)報錯,因為沒有權(quán)限靶溜,重置 post 和 tag 的權(quán)限
-
修改頁面开瞭,添加查詢
<div v-for="item in $page.posts.edges" class="post-preview" :key="item.node.id" > <a href="post.html"> <h2 class="post-title"> {{ item.node.title }} </h2> <h3 class="post-subtitle"> {{ item.node.title }} </h3> </a> <p class="post-meta"> Posted by <a href="#" >{{ item.node.created_name.firstname + item.node.created_name.lastname }}</a > {{ item.node.created_at }} </p> <span v-for="tag in item.node.tags" :key="tag.id"> <a href="">{{ tag.title }}</a> </span> <hr /> </div> <page-query> query { posts: allStrapiPost { edges { node { id title created_name { id firstname lastname } created_at updated_at tags { id title } } } } } </page-query>
-
加載分頁組件
引入并注冊 Pager 組件,頁面中添加罩息,查詢時添加 pageInfo
... <!-- Pager --> <Pager :info="$page.posts.pageInfo"/> ... query ($page: Int){ posts: allStrapiPost (perPage: 2, page: $page) @paginate{ pageInfo { totalPages currentPage } edges { node { id title created_name { id firstname lastname } created_at updated_at tags { id title } } } } } </page-query> import { Pager } from 'gridsome' export default { name: 'Home', components: { Pager }, }
-
加載文章詳情
修改 gridsome.config.js嗤详,添加 templates
// 詳情的模板頁面 根據(jù)對應(yīng)內(nèi)容類型創(chuàng)建模板 // 模板名稱StrapiPost一定要寫集合的名字 此時集合由'@gridsome/source-strapi'生成 templates: { StrapiPost: [ { path: '/post/:id', // 詳情對應(yīng)路由 component: './src/templates/Post.vue', }, ] },
創(chuàng)建 templates/Post.vue 文章詳情模板
查詢數(shù)據(jù),替換模板
<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> <h2 class="subheading"> {{ $page.post.title }} </h2> <span class="meta" >Posted by <a href="#">{{ $page.post.created_name.firstname + $page.post.created_name.lastname }}</a> on {{ $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="$page.post.content" ></div> </div> </div> </article> </Layout> </template> <page-query> query ($id: ID!){ post: strapiPost (id: $id) { id title content cover { url } created_name { id firstname lastname } created_at tags { id title } } } </page-query> <script> export default { name: 'Post', metaInfo: { title: 'Post', }, } </script> <style></style>
修改/pages/index.vue瓷炮,a 標(biāo)簽為 g-link 標(biāo)簽
... <g-link :to="`/post/${item.node.id}`"> <h2 class="post-title"> {{ item.node.title }} </h2> <h3 class="post-subtitle"> {{ item.node.title }} </h3> </g-link> ...
-
處理 markdown 格式文檔
使用markdown-it處理 markdown 格式數(shù)據(jù)
<template> ... <div class="col-lg-8 col-md-10 mx-auto" v-html="mdToHtml($page.post.content)" ></div> ... </template> <script> import MarkdownIt from 'markdown-it' const md = new MarkdownIt() export default { name: 'Post', metaInfo: { title: 'Post', }, methods: { mdToHtml(markdown) { return md.render(markdown) }, }, } </script>
-
文章標(biāo)簽
修改 gridsome.config.js葱色,contentTypes 添加 tag
{ use: '@gridsome/source-strapi', options: { apiURL: 'http://localhost:1337', queryLimit: 1000, // Defaults to 100 contentTypes: ['post', 'tag'], // StrapiPost // typeName: 'Strapi', // singleTypes: ['impressum'], // Possibility to login with a Strapi user, // when content types are not publicly available (optional). // loginData: { // identifier: '', // password: '', // }, }, },
添加 tag 模板
StrapiTag: [ { path: '/tag/:id', // 詳情對應(yīng)路由 component: './src/templates/Tag.vue', }, ]
創(chuàng)建/templates/Tag.vue 模板
<template> <Layout> <!-- 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># {{ $page.tag.title }}</h1> </div> </div> </div> </div> </header> <!-- Main Content --> <div class="container"> <div class="row"> <div class="col-lg-8 col-md-10 mx-auto"> <div v-for="item in $page.tag.posts" class="post-preview" :key="item.id" > <g-link :to="`/post/${item.id}`"> <h2 class="post-title"> {{ item.title }} </h2> <h3 class="post-subtitle"> {{ item.title }} </h3> </g-link> <hr /> </div> <!-- Pager --> <Pager :info="$page.posts.pageInfo" /> </div> </div> </div> </Layout> </template> <page-query> query ($id: ID!){ strapiTag (id: $id) { id title posts { id title } } } </page-query> <script> export default { name: 'Tag', } </script> <style></style>
修改/pages/index.vue,a 標(biāo)簽為 g-link 標(biāo)簽
... <span v-for="tag in item.node.tags" :key="tag.id"> <g-link href="" :to="`/tag/${tag.id}`">{{ tag.title }}</g-link> </span> ...
-
基本設(shè)置
設(shè)置博客首頁標(biāo)題娘香、副標(biāo)題和封面
處理網(wǎng)站標(biāo)題和副標(biāo)題苍狰、包括網(wǎng)站首頁封面,都可以統(tǒng)一管理起來烘绽,設(shè)計統(tǒng)一數(shù)據(jù)結(jié)構(gòu)淋昭,在頁面展示即可
Content Type 相當(dāng)于集合,而此時只需要創(chuàng)建單個的數(shù)據(jù)節(jié)點诀姚,選擇 Single Type
在 strapi 中新增一個 Single Type(單一類型)响牛,名稱為 General,并添加三個字段赫段,title呀打、subtitle 和 cover
保存成功后,在 general 中添加相應(yīng)字段并保存糯笙,對其配置 find 權(quán)限
接著集成到網(wǎng)站當(dāng)中去使用贬丛,配置 gridsome.config.js 的 plugins 選項
... { use: '@gridsome/source-strapi', options: { apiURL: 'http://localhost:1337', queryLimit: 1000, // Defaults to 100 contentTypes: ['post', 'tag'], // StrapiPost配置集合 // typeName: 'Strapi', singleTypes: ['general'], // 配置單節(jié)點 // Possibility to login with a Strapi user, // when content types are not publicly available (optional). // loginData: { // identifier: '', // password: '', // }, }, }, ...
在/pages/index.vue`中,讀取 GraphQL 數(shù)據(jù)層的數(shù)據(jù)给涕,并在視圖中渲染
<template> <Layout> <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> ... </Layout> </template> <page-query> query ($page: Int){ ... allStrapiGeneral { edges { node { id title subtitle cover { url } } } } } </page-query> <script> ... // 使用計算屬性 computed: { general() { return this.$page.allStrapiGeneral.edges[0].node }, }, ... </script> <style></style>
-
聯(lián)系我頁面實現(xiàn)
使用純客戶端實現(xiàn)并將數(shù)據(jù)保存
創(chuàng)建contact集合豺憔,根據(jù)頁面需要添加name额获、email、phone和message字段恭应,并賦予contact集合create權(quán)限
使用postman進(jìn)行測試抄邀,localhost:1337/contacts添加數(shù)據(jù)并測試
修改/pages/contact.vue頁面
<template> <!-- 使用v-model綁定表單數(shù)據(jù)、并添加按鈕點擊事件 --> </template> <script> import axios from 'axios' export default { name: 'Contact', metaInfo: { title: 'Contact', }, data() { return { form: { name: '', email: '', phone: '', message: '', }, } }, methods: { // 此處應(yīng)該加入表單校驗 async onSubmit() { try { const { data } = await axios({ method: 'POST', url: 'http://localhost:1337/contacts', data: this.form, }) this.form = { name: '', email: '', phone: '', message: '', } window.alert('發(fā)送成功') } catch (err) { window.alert('發(fā)送失敗') throw new Error(err) } }, }, } </script> <style></style>
部署strapi
需要支持 Node 的服務(wù)器
數(shù)據(jù)庫 – 建議 MySQL 或者 MongoDB寫給前端的MySQL極簡安裝
strapi默認(rèn)使用 sqlite 數(shù)據(jù)庫昼榛,部署到線上時境肾,不推薦使用
-
切換數(shù)據(jù)庫為mysqlconfigurations,將 config/database.js 替換為Mysql的配置
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", "strapi"), username: env("DATABASE_USERNAME", "root"), password: env("DATABASE_PASSWORD", "329926"), }, options: {}, }, }, });
修改 package.json胆屿,添加 mysql 依賴
npm install mysql --save
奥喻,如果不需要測試,則可以刪除 sqlite3將項目上傳到遠(yuǎn)程倉庫后臺項目地址
在服務(wù)器上克隆上傳到項目地址
git clone https://github.com/wang1xiang/blog-backend.git
安裝依賴
npm i
非迹,打包項目npm run build
环鲤,啟動項目npm run start
可以使用pm2
pm2 start npm -- run start --name blog-backend
啟動項目,可以讓node應(yīng)用跑到后臺安裝成功憎兽,打開
http://106.75.190.29/admin
訪問并重新加載數(shù)據(jù)
本地GridSome應(yīng)用連接遠(yuǎn)程服務(wù)器
-
修改gridsome.config.js
module.exports = { siteName: 'Gridsome', plugins: [ ... { use: '@gridsome/source-strapi', options: { apiURL: 'http://106.75.190.29:1337', queryLimit: 1000, // Defaults to 100 contentTypes: ['post', 'tag'], // StrapiPost配置集合 // typeName: 'Strapi', singleTypes: ['general'] }, }, ] }
-
apiURL可以使用環(huán)境變量的形式設(shè)定冷离,配置環(huán)境變量,添加.env.development和.env.production
// .env.development GRIDSOME_API_URL=http://106.75.190.29:1337 // .env.production GRIDSOME_API_URL=http://106.75.190.29:1337 // gridsome.config.js { use: '@gridsome/source-strapi', options: { apiURL: process.env.GRIDSOME_API_URL, ... }, },
可以配置不同的ip地址唇兑,修改apiURL為process.env.GRIDSOME_API_URL酒朵,重啟
-
此時可以正常訪問網(wǎng)站,當(dāng)時發(fā)現(xiàn)圖片加載有問題扎附,需要設(shè)置圖片路徑蔫耽,當(dāng)然不能直接在模板中寫
process.env.GRIDSOME_API_URL
,使用mixin代替// main.js Vue.mixin({ data() { return { GRIDSOME_API_URL: process.env.GRIDSOME_API_URL, } }, })
使用ip的地方替換為GRIDSOME_API_URL即可
打開應(yīng)用
http://localhost:8080/
訪問成功留夜,圖片正常加載匙铡,此時已聯(lián)通GridSome客戶端和服務(wù)器的strapi
使用Vercel – 部署 Gridsome 應(yīng)用
使用Vercel 進(jìn)行靜態(tài)應(yīng)用項目的部署
基本使用
使用gitHub登陸,選擇
Continue With GitHub
登陸成功碍粥,選擇
new Project
選擇Import Git Repository鳖眼,添加自己的git倉庫,選擇項目導(dǎo)入import
-
如果不需要修改build和環(huán)境變量嚼摩,直接選擇Deploy钦讳,等待Vercel部署打包完成
-
等待部署成功,點擊visit枕面,即可訪問生成的靜態(tài)站點
配置自動構(gòu)建
配置strapi愿卒,當(dāng)數(shù)據(jù)改變時,觸發(fā)Vercel自動構(gòu)建
-
選擇項目blog-with-gridsome潮秘,選擇settings --> git --> Deploy Hooks琼开,設(shè)置部署鉤子
- 點擊create Hook,將生成的鉤子地址復(fù)制到strapi中
- 在strapi后臺項目中枕荞,選擇設(shè)置 --> Webhooks --> 創(chuàng)建新Hook柜候,當(dāng)數(shù)據(jù)更新操作時搞动,請求webhooks,重新觸發(fā)構(gòu)建
- 此時回到vercel渣刷,打開項目的Deployments部署記錄鹦肿,修改數(shù)據(jù),當(dāng)前頁面會自動刷新
- 等待部署完成辅柴,有時頁面可能會有延時狮惜,需要手動刷新
使用GitHub Page+ GitHub Actions – 部署 Gridsome 應(yīng)用
-
在gridsome.config.js中修改pathPrefix為github項目路徑
pathPrefix: 'blog-with-gridsome/' #項目地址
-
重新打包,取消
.gitignore
中的dist
碌识,將dist目錄上傳到遠(yuǎn)程分支git subtree push --prefix dist origin gh-pages
-
項目對應(yīng)的settings,GitHub Pages會自動加載gh-pages下的分支虱而,點擊上面的連接訪問網(wǎng)站
-
配置GitHub Actinos自動部署
個人設(shè)置 --> Personal access tokens --> New personal access token
-
github對應(yīng)項目 --> Settings --> New Secrets筏餐,添加剛剛生成的token
.github/workflows/deploy.yml
name: GitHub Actions Build and Deploy Demo
on:
push:
branches:
- master
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@master
- name: Build and Deploy
uses: JamesIves/github-pages-deploy-action@master
env:
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
BRANCH: gh-pages
FOLDER: dist
BUILD_SCRIPT: npm install && npm run build
-
重新修改代碼,并提交牡拇,可以看到actions中的狀態(tài)