使用Gridsome搭建博客過程

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

  1. gridsome create my-gridsome-site to install default starter
  2. cd my-gridsome-site to open the folder
  3. gridsome develop to start a local dev server at http://localhost:8080
  4. Happy coding ????

項目搭建過程

  • 項目初始化

    • Fork此項目到自己的倉庫
    • 將 Fork 到的項目添加到當前工作區,便于復制
    • 安裝 bootstrap 和@fortawesome/fontawesome-free npm 方式
    • 在 main.js 中加載全局樣式
    • 加載谷歌字體 assets/css/index.css 中 @import 方式引入
    • 引入樣式
    • 將 index.html 中的內容復制到 pages/index.vue 中
    • 啟動項目,訪問正常,需要復制圖片到 static/img/目錄下,修改圖片路徑為/
    • 將<nav>和<footer>標簽放入 layouts/Default.vue 中,添加默認插槽<slot />
    • 添加頁面 Post.vue、About.vue、Contact.vue,初始化頁面完成
  • 使用本地 md 文件管理文章內容

    • 在 GridSome 官網找到source-filesystem插件,轉化文件內容為可以在組件中使用 GraphQL 獲取的內容。
    • npm install @gridsome/source-filesystem安裝
    • 在 gridsome.config.js 的 plugins 中進行配置
      plugins: [
        {
          use: '@gridsome/source-filesystem', // 插件
          options: {
            typeName: 'BlogPost', // 類型,對應GraphQL中的查詢
            path: './content/blog/**/*.md', // 文件路徑
          },
        },
      ],
      
    • 創建 content/blog/article1.md 和 arttcle2.md 文件
    • 重啟項目,報錯
      Error: No transformer for 'text/markdown' is installed.
      
    • 原因是需要 Gridsome 的 Markdown 變換器@gridsome/transformer-remark,安裝并重啟
    • 打開 GraphQL 操作頁面,查詢數據得到結果,然后既可以渲染到頁面上
      query {
        allBlogPost {
          edges {
            node {
              id
              path
              title
              content
            }
          }
        }
      }
      
  • 使用 strapi 接口數據

  • 快速開始安裝并啟動 strapi 應用,安裝成功打開http://localhost:1337/,創建 Content Type,比如 post,添加 id、title 和 content 字段

    • 分配權限

      需要給 publish 角色以下權限,就可在 postman 中使用

      image-20210401081645397.png
  • 修改 Authenticated 權限,全選,添加用戶,設置角色為 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 進行安裝,安裝 npm run strapi install graphql

    • 安裝完成,打開 http://localhost:1337/graphql即可訪問

    • 如何把 strapi 數據通過預取的方式集成到 gridsome 中,想要實現渲染前就拿到 strapi 中的數據,借助 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 中的數據了

    • strapi 新增或刪除數據后,gridsome 需要重啟項目,因為在啟動過程中拉取數據

      image-20210401091814427.png
  • 刪除 post,重新創建,添加字段 title、content、cover、is_publish、created_name 關聯 user

  • 創建 tags

  • 重啟 gridsome,發現報錯,因為沒有權限,重置 post 和 tag 的權限

  • 修改頁面,添加查詢

    <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>
        &nbsp;&nbsp;
      </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

     // 詳情的模板頁面 根據對應內容類型創建模板
    // 模板名稱StrapiPost一定要寫集合的名字 此時集合由'@gridsome/source-strapi'生成
    templates: {
        StrapiPost: [
            {
                path: '/post/:id', // 詳情對應路由
                component: './src/templates/Post.vue',
            },
        ]
    },
    

    創建 templates/Post.vue 文章詳情模板

    查詢數據,替換模板

    <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 標簽為 g-link 標簽

    ...
    <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 格式數據

    <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>
    
  • 文章標簽

    修改 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', // 詳情對應路由
        component: './src/templates/Tag.vue',
      },
    ]
    

    創建/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 標簽為 g-link 標簽

    ...
    <span v-for="tag in item.node.tags" :key="tag.id">
        <g-link href="" :to="`/tag/${tag.id}`">{{ tag.title }}</g-link>
        &nbsp;&nbsp;
    </span>
    ...
    
  • 基本設置

    設置博客首頁標題、副標題和封面

    處理網站標題和副標題、包括網站首頁封面,都可以統一管理起來,設計統一數據結構,在頁面展示即可

    Content Type 相當于集合,而此時只需要創建單個的數據節點,選擇 Single Type

    在 strapi 中新增一個 Single Type(單一類型),名稱為 General,并添加三個字段,title、subtitle 和 cover

    保存成功后,在 general 中添加相應字段并保存,對其配置 find 權限

    接著集成到網站當中去使用,配置 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'], // 配置單節點
            // Possibility to login with a Strapi user,
            // when content types are not publicly available (optional).
            // loginData: {
            //   identifier: '',
            //   password: '',
            // },
    },
    },
    ...
    

    在/pages/index.vue`中,讀取 GraphQL 數據層的數據,并在視圖中渲染

    <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>
    
  • 聯系我頁面實現

    使用純客戶端實現并將數據保存

    創建contact集合,根據頁面需要添加name、email、phone和message字段,并賦予contact集合create權限

    使用postman進行測試,localhost:1337/contacts添加數據并測試

    修改/pages/contact.vue頁面

    <template>
      <!-- 使用v-model綁定表單數據、并添加按鈕點擊事件 -->
    </template>
    
    <script>
    import axios from 'axios'
    export default {
      name: 'Contact',
      metaInfo: {
        title: 'Contact',
      },
      data() {
        return {
          form: {
            name: '',
            email: '',
            phone: '',
            message: '',
          },
        }
      },
      methods: {
        // 此處應該加入表單校驗
        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('發送成功')
          } catch (err) {
            window.alert('發送失敗')
            throw new Error(err)
          }
        },
      },
    }
    </script>
    <style></style>
    
    
部署strapi
  • 需要支持 Node 的服務器

  • 數據庫 – 建議 MySQL 或者 MongoDB寫給前端的MySQL極簡安裝

  • strapi默認使用 sqlite 數據庫,部署到線上時,不推薦使用

  • 切換數據庫為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

  • 將項目上傳到遠程倉庫后臺項目地址

  • 在服務器上克隆上傳到項目地址git clone https://github.com/wang1xiang/blog-backend.git

  • 安裝依賴npm i,打包項目npm run build,啟動項目npm run start

  • 可以使用pm2pm2 start npm -- run start --name blog-backend啟動項目,可以讓node應用跑到后臺

  • 安裝成功,打開http://106.75.190.29/admin訪問并重新加載數據

本地GridSome應用連接遠程服務器
  • 修改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可以使用環境變量的形式設定,配置環境變量,添加.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,重啟

  • 此時可以正常訪問網站,當時發現圖片加載有問題,需要設置圖片路徑,當然不能直接在模板中寫process.env.GRIDSOME_API_URL,使用mixin代替

    // main.js
     Vue.mixin({
        data() {
          return {
            GRIDSOME_API_URL: process.env.GRIDSOME_API_URL,
          }
        },
      })
    

    使用ip的地方替換為GRIDSOME_API_URL即可

  • 打開應用http://localhost:8080/訪問成功,圖片正常加載,此時已聯通GridSome客戶端和服務器的strapi

使用Vercel – 部署 Gridsome 應用

使用Vercel 進行靜態應用項目的部署

基本使用

  • 使用gitHub登陸,選擇Continue With GitHub

  • 登陸成功,選擇new Project

  • 選擇Import Git Repository,添加自己的git倉庫,選擇項目導入import

image-20210403163231079.png
  • 如果不需要修改build和環境變量,直接選擇Deploy,等待Vercel部署打包完成

    image-20210403163433202.png
  • 等待部署成功,點擊visit,即可訪問生成的靜態站點

    image-20210403165828150.png

配置自動構建

配置strapi,當數據改變時,觸發Vercel自動構建

  • 選擇項目blog-with-gridsome,選擇settings --> git --> Deploy Hooks,設置部署鉤子

    image-20210403171715153.png
  • 點擊create Hook,將生成的鉤子地址復制到strapi中
image-20210403171740795.png
  • 在strapi后臺項目中,選擇設置 --> Webhooks --> 創建新Hook,當數據更新操作時,請求webhooks,重新觸發構建
image-20210403172046262.png
  • 此時回到vercel,打開項目的Deployments部署記錄,修改數據,當前頁面會自動刷新
image-20210403172805290.png
  • 等待部署完成,有時頁面可能會有延時,需要手動刷新
使用GitHub Page+ GitHub Actions – 部署 Gridsome 應用
  • 在gridsome.config.js中修改pathPrefix為github項目路徑

    pathPrefix: 'blog-with-gridsome/' #項目地址
    
  • 重新打包,取消.gitignore中的dist,將dist目錄上傳到遠程分支

    git subtree push --prefix dist origin gh-pages
    
  • 項目對應的settings,GitHub Pages會自動加載gh-pages下的分支,點擊上面的連接訪問網站

    image-20210403180137115.png
  • 配置GitHub Actinos自動部署

    個人設置 --> Personal access tokens --> New personal access token

    img
  • github對應項目 --> Settings --> New Secrets,添加剛剛生成的token

    img
  • .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中的狀態

    image-20210403182638066.png
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容