静态站点生成(SSG)——Gridsome

Study Notes

Gridsome

静态站点生成(SSG)

介绍

Gridsome 是什么

  • Gridsome 是由 Vue.js 驱动的 Jamstack 框架,用于构建默认情况下快速生成的静态生成的网站和应用。

  • Gridsome 是 Vue 提供支持的静态站点生成器,用于为任何无头 CMS,本地文件或 API 构建可用于 CDN 的网站

  • 使用 Vue.js,webpack 和 Node.js 等现代工具构建网站。通过 npm 进行热重载并访问任何软件包,并使用自动前缀在您喜欢的预处理器(如 Sass 或 Less)中编写 CSS。

  • 基于 Vue.js 的 Jamstack 框架

  • Gridsome 使开发人员可以轻松构建默认情况下快速生成的静态生成的网站和应用程序

  • Gridsome 允许在内容里面引用任何 CMS 或数据源。从 WordPress,Contentful 或任何其他无头 CMS 或 API 中提取数据,并在组件和页面中使用 GraphQL 访问它。

为什么选择 Gridsome

  • 用于前端的 Vue.js——最简单,最容易理解的前端框架。

  • 数据来源——使用任何 Headless CMS,API 或 Markdown 文件存储数据。

  • 通过热重载进行本地开发——实时查看代码更改。

  • 基于文件的页面路由——在src/pages中的任何 Name.vue 文件都是静态路由。

  • 动态路由——在src/pages中的任何 [param].vue 文件都是动态路由。

  • 静态文件生成——可部署到 CDN 或静态 Web 主机。

  • GraphQL 数据层——具有集中式数据层的简单数据管理。

  • 自动代码拆分——将超高性能构建到每个页面中。

  • 插件生态系统——生态圈完善。

什么是 Jamstack

Gridsome 是一个 Jamstack 框架。 Jamstack 使您可以通过预渲染文件并直接从 CDN 直接提供文件来构建快速安全的站点和应用程序,而无需管理或运行 Web 服务器。

Learn more about the Jamstack.

它是如何工作的

Gridsome 生成静态 HTML,一旦加载到浏览器中,该 HTML 就会渗入 Vue SPA。这意味着您可以使用 Gridsome 构建静态网站和动态应用程序。

Gridsome 为每个页面构建一个.html 文件和一个.json 文件。加载第一页后,它仅使用.json 文件来预取和加载下一页的数据。它还为需要它的每个页面构建一个.js 包(代码拆分)。

它使用 vue-router 进行 SPA 路由,并使用 vue-meta 来管理<head>

Gridsome 默认添加最小 57kB 的 gzip JS 捆绑包大小(vue.js,vue-router,vue-meta 和一些用于图像延迟加载的文件)。

详细了解其工作原理

预备知识

您应该具有有关 HTML,CSS,Vue.js 以及如何使用终端 的基本知识。了解 GraphQL 的工作原理是有好处的,但不是必需的。 Gridsome 是学习它的好方法。

Gridsome 需要 Node.js (v8.3 +),并建议使用 Yarn

备选方案

使用场景

  • 不适合管理系统
  • 简单页面展示
  • 想要有更好的 SEO
  • 想要有更好的渲染性能

起步

快速了解 Gridsome 项目

接单

小编承接外包,有意者可加
QQ:1944300940
在这里插入图片描述

微信号:wxid_g8o2y9ninzpp12
在这里插入图片描述

安装 Gridsome CLI 工具

使用 YARN

yarn global add @gridsome/cli

使用 NPM

npm install --global @gridsome/cli

查看是否安装成功

gridsome -v

创建一个 Gridsome 项目

创建一个新项目

gridsome create my-gridsome-site
创建失败时

gridsome 项目安装依赖注意事项:

  • 配置 node-gyp 编译环境

    • https://github.com/nodejs/node-gyp
  • 配置环境变量:

    • https://sharp.pixelplumbing.com/install#chinese-mirror
      • 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"
  • 配置 hosts:199.232.68.133 raw.githubusercontent.com

    • https://www.ipaddress.com/

打开文件夹

cd my-gridsome-site

启动开发模式

yarn develop

npm run develop

gridsome develop

目录结构

.
├── src
│   ├── components # 公共组件
│   ├── layouts # 布局组件
│   ├── pages # 页面路由组件
│   ├── templates # 模板文件
│   ├── favicon.png # 网站图标
│   └── main.js # 应用入口
├── static # 静态资源存储目录,该目录中的资源不做构建处理
├── README.md
├── gridsome.config.js # 应用配置文件
├── gridsome.server.js # 针对服务端的配置文件
├── package-lock.json
└── package.json

创建路由

src/pages 目录中创建一个 .vue 文件

构建

yarn build

构建结果默认输出到 dist 目录中。

Gridsome 会把每个路由文件构建为独立的 HTML 页面。

部署

可以把构建结果 dist 放到任何 Web 服务器中进行部署。

例如我们这里使用 Node.js 命令行工具 serve 来测试构建结果。

或者可以部署到其它第三方托管平台:https://gridsome.org/docs/deployment/。

或是自己的服务器,都可以!

全局安装 serve

yarn add global serve

启动

serve dist

核心概念

学习 Gridsome 的核心概念

Pages

通过在 src/pages 文件夹中添加 Vue 组件来创建页面。他们使用基于文件的路由系统。例如,src/pages/About.vue 将是 mywebsite.com/about/。页面用于简单页面和列出集合的页面(例如/blog/)。

了解有关页面的更多信息

Collections

如果您要在网站上放置博客文章,标签,产品等,则收藏很有用。可以使用 Source 插件Data Store API 从任何 Headless CMS,内容 API 或 Markdown 文件中获取集合。

在这里插入图片描述

集合存储在临时的本地 GraphQL 数据层中,可以在任何地方查询,过滤,分页或有关系。

Templates

模板负责显示集合的节点(单个页面)。模板通常位于 src/templates 中。如果未在模板配置中指定组件,则 Gridsome 尝试查找与集合名称相同的文件。

这是一个例子:

<!-- src/templates/Post.vue -->
<template>
  <Layout>
    <h1 v-html="$page.post.title" />
  </Layout>
</template>

<page-query>
  query ($id: ID!) { post(id: $id) { title } }
</page-query>

更多关于 Templates 的内容

Layouts

布局是在页面和模板内部用于包装内容的 Vue 组件。布局通常包含页眉和页脚。

页面中通常按以下方式使用布局:

<template>
  <Layout>
    <h1>About us</h1>
  </Layout>
</template>

<script>
  import Layout from '~/layouts/Default.vue';

  export default {
    components: {
      Layout,
    },
  };
</script>

:::tip

也可以在全球范围内使用布局,因此您无需每页导入它们。

请注意,Gridsome CLI 创建的默认模板将使用全局布局组件。

:::

更多关于 Layouts 的内容:https://gridsome.org/docs/layouts/。

Images

Gridsome 具有内置的 <g-image> 组件,可输出优化的逐行图像。如果更改宽度和高度,则在开发时还可以实时调整大小和裁剪。 <g-images> 创建一个超小型模糊的嵌入式 base64 图像,然后在视图中使用 IntersectionObserver 延迟加载图像。

更多关于 Images 的内容:https://gridsome.org/docs/images/。

Linking

Gridsome 具有内置的 <g-link> 组件,该组件在查看链接时使用 IntersectionObserver 来预取链接的页面。这使得在 Gridsome 站点中浏览非常快,因为单击的页面已经下载。

更多关于 <g-link> 的内容:https://gridsome.org/docs/linking/。

部署

https://gridsome.org/docs/deployment/

Gridsome 基础

目录结构

.
├── package.json # 包说明文件
├── gridsome.config.js # Gridsome 配置文件
├── gridsome.server.js # 自定义 Gridsome 编译
├── static/ # 静态资源存储目录,该目录中的资源不做构建处理
└── src/
    ├── main.js # 应用入口
    ├── index.html # 公共页面
    ├── App.vue # 根组件
    ├── layouts/ # 布局组件
    │   └── Default.vue
    ├── pages/ # 路由页面
    │   ├── Index.vue
    │   └── Blog.vue
    └── templates/ # 模板
        └── BlogPost.vue

项目配置

Gridsome 需要 gridsome.config.js 才能工作。插件和项目设置位于此处。基本配置文件如下所示:

module.exports = {
  siteName: 'Gridsome',
  siteUrl: 'https://www.gridsome.org',
  plugins: [],
};
属性类型默认值说明
siteNamestring<dirname>该名称通常在标题标签中使用。
siteDescriptionstring''页面描述,<meta name="description" content="xxx">
pathPrefixstring''Gridsome 假定您的项目是从域的根目录提供的。如果您的项目将托管在名为 my-app 的子目录中,则将此选项更改为“ / my-app”。
titleTemplatestring%s - <siteName>设置标题标签的模板。 %s 占位符将替换为您在页面中设置的 metaInfo 的标题。
pluginsArray[]通过将插件添加到 plugins 数组来激活插件。
templatesobject{}定义 collections 的路由和模板。
metadataobject{}将全局元数据添加到 GraphQL 模式。
iconstring | object'./src/favicon.png'Gridsome 默认情况下会将位于 src / favicon.png 的任何图像用作 favicon 和 touchicon,但您可以定义其他路径或大小等。图标应为正方形且至少 16 个像素。网站图标将调整为 16、32、96 像素。默认情况下,触摸图标的大小将调整为 76、152、120、167、180 像素。
configureWebpackobject | Function如果该选项是一个对象,它将与内部配置合并。
chainWebpackFunction该函数将接收由 webpack-chain 驱动的 ChainableConfig 实例。
runtimeCompilerbooleanfalse在运行时包括 Vue 模板编译器。
configureServerFunction配置开发服务器。
permalinks.trailingSlashbooleantrue默认情况下,在页面和模板后添加斜杠。启用此选项后,具有动态路由的页面将不包含尾部斜杠,并且服务器上必须具有额外的重写规则才能正常工作。另外,<g-link>的静态路径不会自动包含尾部斜杠,而应包含在路径中:
permalinks.slugify使用自定义的 Slugify 方法。默认是 @sindresorhus/slugify
css.splitbooleanfalse将 CSS 分成多个块。默认情况下禁用拆分。拆分 CSS 可能会导致奇怪的行为。
css.loaderOptionsObject{}将选项传递给与 CSS 相关的 loader
hoststringlocalhost
portnumber8080
outputDirstring‘dist’运行 gridsome 构建时将在其中生成生产构建文件的目录。

插件示例:

module.exports = {
  plugins: [
    {
      use: '@gridsome/source-filesystem',
      options: {
        path: 'blog/**/*.md',
        route: '/blog/:year/:month/:day/:slug',
        typeName: 'Post',
      },
    },
  ],
};

注意事项:

  • 开发过程中修改配置需要重启服务

Pages 页面

页面负责在 URL 上显示您的数据。每个页面将静态生成,并具有自己的带有标记的 index.html 文件。

在 Gridsome 中创建页面有两种选择:

  • 单文件组件
  • 使用 Pages API 以编程方式创建页面

pages 中的单文件组件

src/pages 目录中的单文件组件将自动具有其自己的 URL。文件路径用于生成 URL,以下是一些基本示例:

  • src/pages/Index.vue becomes /(The frontpage)
  • src/pages/AboutUs.vue becomes /about-us/
  • src/pages/about/Vision.vue becomes /about/vision/
  • src/pages/blog/Index.vue becomes /blog/

大小自动转小写,驼峰命名会自动使用短横杠分割

src/pages 中的页面通常用于诸如 /about/ 之类的固定 URL,或用于在 /blog/ 等处列出博客文章。

使用 Pages API 创建页面

可以使用 gridsome.server.js 中的 createPages 钩子以编程方式创建页面。如果您要从外部 API 手动创建页面而不使用 GraphQL 数据层,则此功能很有用。

module.exports = function (api) {
  api.createPages(({ createPage }) => {
    createPage({
      path: '/my-page',
      component: './src/templates/MyPage.vue',
    });
  });
};

动态路由

动态路由对于仅需要客户端路由的页面很有用。例如,根据 URL 中的细分从生产环境中的外部 API 获取信息的页面。

通过文件创建动态路由

动态页面用于客户端路由。可以通过将名称包装在方括号中来将路由参数放置在文件和目录名称中。例如:

  • src/pages/user/[id].vue becomes /user/:id.
  • src/pages/user/[id]/settings.vue becomes /user/:id/settings.

注意事项:

  • 在构建时,这将生成 user/_id.htmluser/_id/settings.html,并且您必须具有重写规则以使其正常运行。

  • 具有动态路由的页面的优先级低于固定路由。例如,如果您有一个 /user/create 路由和 /user/:id 路由,则 /user/create 路由将具有优先级。

这是一个基本的页面组件,它使用路由中的 id 参数来获取客户端的用户信息:

<template>
  <div v-if="user">
    <h1>{{ user.name }}</h1>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        user: null,
      };
    },
    async mounted() {
      const { id } = this.$route.params;
      const response = await fetch(`https://api.example.com/user/${id}`);

      this.user = await response.json();
    },
  };
</script>

始终使用 mounted 来获取客户端数据。由于在生成静态 HTML 时执行数据,因此在 created 中获取数据会引起问题。

通过编程方式创建动态路由

以编程方式创建带有动态路由的页面,以获取更高级的路径。动态参数使用 : 来指定。

每个参数都可以具有一个自定义的正则表达式,以仅匹配数字或某些值。

module.exports = function (api) {
  api.createPages(({ createPage }) => {
    createPage({
      path: '/user/:id(\\d+)',
      component: './src/templates/User.vue',
    });
  });
};
生成重写规则

Gridsome 无法为动态路由的每种可能的变体生成 HTML 文件,这意味着直接访问 URL 时最有可能显示 404 页。而是,Gridsome 生成一个 HTML 文件,该文件可用于重写规则。例如,类似/ user /:id 的路由将生成位于/user/_id.html 的 HTML 文件。您可以具有重写规则,以将所有与/ user /:id 匹配的路径映射到该文件。

由于每种服务器类型都有自己的语法,因此必须手动生成重写规则。 afterBuild 挂钩中的 redirects 数组包含应生成的所有必要的重写规则。

const fs = require('fs');

module.exports = {
  afterBuild({ redirects }) {
    for (const rule of redirects) {
      // rule.from   - The dynamic path
      // rule.to     - The HTML file path
      // rule.status - 200 if rewrite rule
    }
  },
};

页面 meta 信息

Gridsome 使用 vue-meta 处理有关页面的元信息。

<template>
  <div>
    <h1>Hello, world!</h1>
  </div>
</template>

<script>
  export default {
    metaInfo: {
      title: 'Hello, world!',
      meta: [{ name: 'author', content: 'John Doe' }],
    },
  };
</script>

自定义 404 页面

创建一个 src/pages/404.vue 组件以具有一个自定义 404 页面。

Collections 集合

集合是一组节点,每个节点都包含带有自定义数据的字段。如果您要在网站上放置博客文章,标签,产品等,则集合很有用。

添加集合

集合可以通过 source plugins 添加,也可以使用 Data Store API 自己添加。

在开发和构建期间,这些集合存储在本地内存数据存储中。节点可以来自本地文件(Markdown,JSON,YAML 等)或任何外部 API。

在这里插入图片描述

使用 source plugins 添加集合

将集合添加到 Gridsome 的最简单方法是使用源插件。本示例从 WordPress 网站创建集合。源插件的 typeName 选项通常用于为插件添加的集合名称添加前缀。

// gridsome.config.js
module.exports = {
  plugins: [
    {
      use: '@gridsome/source-wordpress',
      options: {
        baseUrl: 'YOUR_WEBSITE_URL',
        typeName: 'WordPress',
      },
    },
  ],
};

你可以在这里浏览插件列表。

使用 Data Store API 添加集合

您可以从任何外部 API 手动添加集合。

本示例创建一个名为 Post 的集合,该集合从 API 获取内容并将结果作为节点添加到该集合中。

// gridsome.server.js
const axios = require('axios');

module.exports = function (api) {
  api.loadSource(async (actions) => {
    const collection = actions.addCollection('Post');

    const { data } = await axios.get('https://api.example.com/posts');

    for (const item of data) {
      collection.addNode({
        id: item.id,
        title: item.title,
        content: item.content,
      });
    }
  });
};

了解有关 Data Store API 的更多信息。

GraphQL 中的集合

每个集合将向 GraphQL schema 添加两个根字段,这些根字段用于检索页面中的节点。

字段名称是根据集合名称自动生成的。如果您将集合命名为 Post,那么在架构中将具有以下可用字段:

  • post 通过 ID 获取单个节点。
  • allPost 获取节点列表(可以排序和过滤等)。

自动生成 schema

探索可用的类型和字段

您可以通过在 GraphQL 资源管理器中打开架构选项卡来浏览可用字段。

阅读有关如何在 GraphQL 中查询节点的更多信息:https://gridsome.org/docs/querying-data/。

Templates(模板)

模板用于为集合中的节点创建单个页面。节点需要相应的页面才能显示在其自己的 URL 上。

设置模板

以下示例将为 Post 的集合设置路由和模板。如果未指定组件,则将 src/templates/{Collection}.vue组件作为模板。

// gridsome.config.js
module.exports = {
  templates: {
    Post: '/blog/:year/:month/:title',
  },
};

指定自定义组件路径:

// gridsome.config.js
module.exports = {
  templates: {
    Post: [
      {
        path: '/blog/:year/:month/:title',
        component: './src/other/location/Post.vue',
      },
    ],
  },
};

为一个集合设置多个模板:

// gridsome.config.js
module.exports = {
  templates: {
    Product: [
      {
        path: '/product/:slug',
        component: './src/templates/Product.vue',
      },
      {
        name: 'reviews',
        path: '/product/:slug/reviews',
        component: './src/templates/ProductReviews.vue',
      },
    ],
  },
};

模板路径在 GraphQL 模式中具有 path 字段。使用 to 参数获取集合的其他模板的路径。

query ($id: ID!) {
  product(id: $id) {
    path               # 默认模板的路径
    path(to:"reviews") # reviews模板的路径
  }
}

可用的模板选项包括:

  • path - 定义动态路由,并使用任何节点字段作为参数。
  • component - 指定要用作每个页面模板的组件。
  • name - 为模板指定名称,以获取 GraphQL 中的路径。

:::tip

默认情况下,路径参数为 slugged,但是可以通过添加_raw 后缀来使用原始值,例如:title_raw。通过使用双下划线(__)分隔属性或索引来访问深层对象或数组中的值。date 字段有一组速记助手。 :year:month:day

  • :id解析为node.id
  • :value解析为node.value(细分值)
  • :value_raw解析为node.value(原始值)
  • :object__value解析为node.object.value
  • :array__3__id解析为node.array[3].id

:::

path选项可以是一个函数,该函数将节点作为第一个参数接收并返回路径。

// gridsome.config.js
module.exports = {
  templates: {
    Post: [
      {
        path: (node) => {
          return `/product/${node.slug}/reviews`;
        },
      },
    ],
  },
};

每个节点将在 GraphQL 模式中获得一个path字段,其中包含生成的 URL。

将数据添加到模板

通过模板配置生成的页面将在页面查询块中将节点 id 用作查询变量 。使用 $id 变量获取当前页面的节点:

<template>
  <div>
    <h1 v-html="$page.post.title" />
    <div v-html="$page.post.content" />
  </div>
</template>

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

:::tip

其他节点字段也可用作查询变量。通过使用双下划线(__)分隔属性或索引来访问深层对象或数组中的值。

  • $id解析为node.id
  • $value解析为node.value(细分值)
  • $object__value解析为node.object.value
  • $array__3__id解析为node.array[3].id

:::

节点字段作为 meta info

metaInfo选项必须是一个函数才能访问查询结果:

<script>
export default {
  metaInfo() {
    return {
      title: this.$page.post.title,
    };
  },
};
</script>

Layouts(布局)

布局组件用于包装页面。布局应将整个网站中使用的组件包含在内,例如页眉,页脚或侧边栏。

布局只是位于src/layouts中的.vue组件,需要声明为全局组件或在页面中导入。

每个布局都需要一个<slot>组件。这是将页面和模板的内容插入的位置。布局可以具有多个插槽。

<!-- Layout -->
<template>
  <div>
    <header />
    <slot><!-- 页面内容将插入此处 --></slot>
    <footer />
  </div>
</template>

导入布局

创建布局后,需要将其导入到页面和模板中。这是在<script>标记内完成的。

<!-- Page -->
<template>
  <Layout>
    Add page content here
  </Layout>
</template>

<script>
import Layout from '~/layouts/Default.vue';

export default {
  components: {
    Layout,
  },
};
</script>

全局布局

如果您不想将布局导入每个页面或模板,则可以将布局设为全局。要使布局成为全局布局,请转至src/main.js并将布局文件导入此文件。

例如:

// src/main.js

import Layout from '~/layouts/Default.vue';

export default function (Vue, { head, router, isServer }) {
  Vue.component('Layout', Layout);
}

现在,您可以在 Gridsome 项目中的任何位置使用<Layout>,而无需将其导入每个页面:

<!-- Page -->
<template>
  <Layout>
    在此处添加页面内容
  </Layout>
</template>

将 Props 传递到布局

由于布局就像组件一样工作,因此可以将 Props 传递给布局。例如,页面如下所示:

<!-- Page -->
<template>
  <Layout :sidebar="true">
    在此处添加页面内容
  </Layout>
</template>

这会将 Prop 传递到sidebar = true的布局。在布局组件中,可能看起来像这样:

<!-- Layout -->
<template>
  <div>
    <div class="main-content">
      <slot></slot>
    </div>
    <div v-if="sidebar">
      让我们显示侧边栏
    </div>
  </div>
</template>

<script>
export default {
  props: ['sidebar'],
};
</script>

多个内容槽

要将多个插槽添加到布局中,您需要为其命名。在此示例中,我们添加了一个侧边栏插槽,仅在页面具有侧边栏内容时才会显示。

<!-- Layout -->
<template>
  <div>
    <!-- Default slot -->
    <slot />
    <div class="sidebar" v-if="$slots.sidebar">
      <!-- Sidebar slot -->
      <slot name="sidebar"></slot>
    </div>
  </div>
</template>

页面现在可以将内容添加到此插槽中,如下所示

<!-- Page -->
<template>
  <Layout>
    这是默认内容

    <template slot="sidebar">
      这将从页面添加到侧边栏插槽
    </template>
  </Layout>
</template>

主布局

您可以通过将App.vue文件添加到src根目录来创建主布局。这样一来,您就可以在所有页面上保留页眉,页脚并添加页面过渡。

一个简单的 App.vue 文件如下所示:

<template>
  <div id="app">
    <router-view />
  </div>
</template>

Components(组件)

Gridsome 使用 Vue 单个文件组件 。这意味着您将 HTML,JavaScript 和 CSS 添加到同一文件中。这使您的项目更易于维护和测试,并且组件也可重用。这也可在构建过程中进行代码拆分。

这是一个文件示例,我们在src/components/中将其称为Card.vue:

<template>
  <div class="card">
    {{ message }}
    <button @click="onClick">
      Change
    </button>
  </div>
</template>

<script>
export default {
  name: 'Card',
  data() {
    return {
      message: 'Try change me!',
    };
  },
  methods: {
    onClick() {
      this.message = 'Here you go :)';
    },
  },
};
</script>

<style>
.card {
  padding: 20px;
  background: #fff;
}
</style>

导入其他页面或组件

创建组件后,可以轻松将其导入页面。在 Gridsome 项目中,建议将所有.vue组件放在src/components文件夹中,然后将它们导入到PagesLayouts中,如下所示:

<template>
  <Card />
</template>

<script>
import Card from '~/components/Card.vue';

export default {
  components: {
    Card,
  },
};
</script>

将 GraphQL 添加到组件

每个组件都可以具有一个带有 GraphQL 查询的<static-query>块,从数据源获取数据。结果将存储在组件内部的$static属性中。

<template>
  <div v-html="$static.post.content" />
</template>

<static-query>
query {
  post (id: "1") {
    content
  }
}
</static-query>

Linking

<g-link>组件在您的所有页面,模板和组件中全局可用。它是来自Vue Routerrouter-link 的包装。

动态路由

动态路由对于仅需要客户端路由的页面很有用。例如,根据 URL 中的细分从生产中的外部 API 获取信息的页面。

基于文件的动态路由

动态页面用于客户端路由。路由参数可以通过将名称包装在方括号中来放置在文件和目录名称中。例如:

  • src/pages/user/[id].vue 变成 /user/:id.
  • src/pages/user/[id]/settings.vue 变成 /user/:id/settings.

在构建时,这将生成user/_id.htmluser/_id/settings.html,并且您必须具有重写规则以使其正常运行。

具有动态路由的页面的优先级低于固定路由。例如,如果您有一个/user/create路由和/user/:id路由,则/user/create路由将具有优先级。

这是一个基本的页面组件,它使用路由中的id参数在客户端获取用户信息:

<template>
  <div v-if="user">
    <h1>{{ user.name }}</h1>
  </div>
</template>

<script>
export default {
  data() {
    return {
      user: null,
    };
  },
  async mounted() {
    const { id } = this.$route.params;
    const response = await fetch(`https://api.example.com/user/${id}`);

    this.user = await response.json();
  },
};
</script>

:::warning
始终使用mounted钩子来获取客户端数据。在created钩子中获取数据会导致问题,因为生成静态 HTML 时会执行数据。
:::

编程式动态路由

以编程方式创建带有动态路由的页面,以获取更高级的路径。动态参数通过在段前面使用:来指定。每个参数都可以有一个自定义的正则表达式,以仅匹配数字或某些值

module.exports = function (api) {
  api.createPages(({ createPage }) => {
    createPage({
      path: '/user/:id(\\d+)',
      component: './src/templates/User.vue',
    });
  });
};

生成重写规则

Gridsome 无法为动态路由的每种可能的变体生成 HTML 文件,这意味着直接访问 URL 时最有可能显示 404 页。而是,Gridsome 生成一个 HTML 文件,该文件可用于重写规则。例如,类似/user/:id的路由将生成位于/user/_id.html的 HTML 文件。您可以具有重写规则,以将所有与/user/:id匹配的路径映射到该文件。

由于每种服务器类型都有自己的语法,因此必须手动生成重写规则。 afterBuild钩子中的redirects数组包含应生成的所有必要的重写规则。

const fs = require('fs');

module.exports = {
  afterBuild({ redirects }) {
    for (const rule of redirects) {
      // rule.from   - The dynamic path
      // rule.to     - The HTML file path
      // rule.status - 200 if rewrite rule
    }
  },
};

图片处理

页面 Head 管理

添加全局头部元数据

将 meta data 添加到 pages 和 templates

从子组件覆盖 meta data

可用属性

PropertyDescriptionLink
styleAdds a style tagDocs
scriptAdds a script tagDocs
metaAdds a meta tagDocs
titleChanges title textDocs
titleTemplateDynamic title textDocs
linkAdds a link tagDocs

环境变量

处理数据

其实这里 GraphQL 并不是作为服务器端部署,而是作为 Gridsome 在本地管理资源的一种方式。

通过 GraphQL 统一管理实际上非常方便,因为作为一个数据库查询语言,它有非常完备的查询语句,与 JSON 相似的描述结构,再结合 Relay 的 Connections 方式处理集合,管理资源不再需要自行引入其它项目,大大减轻了维护难度。

GraphQL 数据层

在这里插入图片描述

GraphQL 数据层是在开发模式下可用的工具。这是临时存储到 Gridsome 项目中的所有数据的地方。可以将其视为可帮助您更快更好地处理数据的本地数据库。

来自 GraphQL 数据层的数据将生成为静态内容。

数据层和导入数据的源之间没有实时连接。这意味着您需要重新生成网站以获取最新的数据更新。

如果需要动态数据,则应使用客户端数据

提示:默认情况下,Pages 也 Site metadata 已添加到数据层。

处理数据

  • 如何导入数据
  • 如何查询数据
  • 如何过滤数据
  • 如何创建分类页面
  • 如何分页数据
  • 如何添加客户端/动态数据

GraphQL 资源管理器

每个 Gridsome 项目都有一个 GraphQL 资源管理器,可以在开发模式下使用它来探索和测试查询。

在这里,您还将获得所有可用 GraphQL 集合的列表。

通常可以通过转到 http:// localhost:8080/___explore 来打开它。

在这里插入图片描述

导入数据

Gridsome 允许您将数据从任何数据源导入 GraphQL 数据层。

导入源插件

向 Gridsome 添加数据的最简单方法是使用源插件。 Gridsome 数据源插件添加在 gridsome.config.js 中。您可以在 Plugins 目录中找到可用的数据源插件。

这是添加到 config 的 file-system 源的示例:

module.exports = {
  plugins: [
    {
      use: '@gridsome/source-filesystem',
      options: {
        path: 'docs/**/*.md',
        typeName: 'DocPage',
      },
    },
  ],
};

typeName将是 GraphQL 集合的名称,并且必须是唯一的。本示例将添加一个 DocPage 集合。

每个数据源都有不同的选项,因此请查看其文档以了解更多信息。

从 API 导入

使用 Data store API 将数据从任何内容 API 导入到 GraphQL 数据层。要使用该 API,您需要在 Gridsome 项目的根文件夹中有一个 gridsome.server.js 文件。

这是一个导入数据的示例 gridsome.server.js 文件:

const axios = require('axios');

module.exports = function (api) {
  api.loadSource(async (actions) => {
    const { posts } = await axios.get('https://api.example.com/posts');

    const collection = actions.addCollection({
      typeName: 'BlogPosts',
    });

    for (const post of posts) {
      collection.addNode({
        id: post.id,
        title: post.title,
      });
    }
  });
};

:::warning
启动开发服务器或生产构建时将获取数据。您需要重新启动服务器才能使 gridsome.server.js 中的更改生效。
:::

更多

查询数据

您可以将数据从 GraphQL 数据层查询到任何页面,模板或组件中。在 Vue 组件中,使用<page-query><static-query>中添加查询。

  • 在页面和模板中使用<page-query>
  • 在组件中使用<static-query>

如何用 GraphQL 查询

在 Gridsome 中使用 GraphQL 很容易,并且您不需要了解 GraphQL。这是一个如何在页面的页面查询中使用 GraphQL 的示例:

<template>
  <div>
    <div v-for="edge in $page.posts.edges" :key="edge.node.id">
      <h2>{{ edge.node.title }}</h2>
    </div>
  </div>
</template>

<page-query>
query {
  posts: allWordPressPost {
    edges {
      node {
        id
        title
      }
    }
  }
}
</page-query>

使用 GraphQL,您仅查询所需的数据。这使得处理数据更加容易和整洁。查询总是以 query 开始,然后是 Posts(可以是任何东西)。然后,您编写类似posts:allWordPressPostallWordPressPost是您要查询的 GraphQL 集合的名称。posts:部分是可选的别名。将帖子用作别名时,您的数据将位于 $page.posts(如果使用<static-query>,则为 $static.posts)。否则,它将在$page.allWordPressPost上可用。

了解有关 GraphQL 查询的更多信息

查询集合

您会注意到,您架构中的某些根字段都以全部作为前缀。使用它们来获取集合中节点的列表。

ArgumentDefaultDescription
sortBy“date”按节点字段排序。
orderDESC按 order 字段排序 (DESC or ASC).
sort按多个节点字段排序。
skip0跳过多少个节点。
limit要获得多少个节点。
page获取哪个页面。
perPage每页显示多少个节点。如果未提供 page 参数,则省略。
filter{}更多

查找按标题排序的节点

query {
  allPost(sortBy: "title", order: DESC) {
    edges {
      node {
        title
      }
    }
  }
}

按多个字段对集合进行排序

query {
  allPost(sort: [{ by: "featured" }, { by: "date" }]) {
    edges {
      node {
        title
      }
    }
  }
}

查询单个节点

并非 all 开头的其他字段是您的单个条目。模板通常使用它们来获取当前页面的数据。您必须提供 idpath 作为参数来查找节点。

ArgumentDefaultDescription
idnullid 获取节点。

查询示例

query {
  post(id: "1") {
    title
  }
}

查询页面组件中的数据

每个页面都可以具有一个带有 GraphQL 查询的<page-query>块,以从数据源获取数据。结果将存储在页面组件内的 $page 属性中。

<template>
  <Layout>
    <h2>Latest blog posts</h2>
    <ul>
      <li v-for="edge in $page.posts.edges" :key="edge.node.id">
        {{ edge.node.title }}
      </li>
    </ul>
  </Layout>
</template>

<page-query>
query {
  posts: allWordPressPost {
    edges {
      node {
        id
        title
      }
    }
  }
}
</page-query>

页面组件中的多个查询

如果您需要进行多个 GraphQL 查询,请按以下步骤进行。结果将存储在页面组件内的$page属性中,您可以通过指定查询名称来进一步区分。

<template>
  <Layout>
    <h2>Latest blog posts</h2>
    <ul>
      <li v-for="edge in $page.posts.edges" :key="edge.node.id">
        {{ edge.node.title }}
      </li>
    </ul>

    <h2>Latest book reviews</h2>
    <ul>
      <li v-for="edge in $page.books.edges" :key="edge.node.id">
        {{ edge.node.title }}
      </li>
    </ul>
  </Layout>
</template>

<page-query>
query {
  posts: allWordPressPost {
    edges {
      node {
        id
        title
      }
    }
  }
  books: allBooks {
    edges {
      node {
        id
        title
      }
    }
  }
}
</page-query>

查询任何组件中的数据

每个 Vue 组件都可以具有一个带有 GraphQL 查询的<static-query>块,以从数据源获取数据。结果将存储在组件内部的$static属性中。 <static-query>名为static,因为它不能接受任何变量。

<template>
  <div v-html="$static.post.content" />
</template>

<static-query>
query {
  post(id: "1") {
    content
  }
}
</static-query>

筛选资料

GraphQL 模式中的每个集合都有一个 filter 参数,可用于过滤结果。您可以按任何自定义字段进行过滤。每种字段类型都支持不同的运算符。

filter 的语法基于 mongodb 查询语法。

String 字段

Operator描述
eq(equal)查找具有(strict)相等字段的节点。
ne(not equal)查找字段不等于提供的值的节点。
in查找字段匹配提供的任何值的节点。
nin(not in)查找字段与提供的任何值都不匹配的节点。
regex筛选属性匹配正则表达式的节点。
len(length)过滤具有指定长度的字符串字段的节点。
query {
  allPost(filter: { id: { in: ["1", "2"] }}) {
    edges {
      node {
        title
      }
    }
  }
}

本示例将查询 id 为 1 或 2 的节点。

Date 字段

Operator描述
gt(greater than)查找字段大于提供的值的节点。
gte(greater or equal)查找字段大于或等于提供的值的节点。
lt(less than)查找字段小于提供的值的节点。
lte(less than or equal)查找字段小于或等于提供的值的节点。
dteq(equal dates)按等于提供的日期值的 date 属性过滤节点。
between查找字段值在提供的值之间的节点。
query {
  allPost(filter: { date: { gte: "2017" }}) {
    edges {
      node {
        title
      }
    }
  }
}

本示例将仅查询 date 大于或等于 2017 的节点。

Boolean 字段

OperatorDescription
eq(equal)查找具有(strict)相等字段的节点。
ne(not equal)查找字段不等于提供的值的节点。
in查找字段匹配提供的任何值的节点。
nin(not in)查找字段与提供的任何值都不匹配的节点。
query {
  allPost(filter: { featured: { eq: true }}) {
    edges {
      node {
        title
        featured
      }
    }
  }
}

本示例将仅查询 featured 为 true 的节点。

Number 字段

Operator描述
eq(equal)查找具有(strict)相等字段的节点。
ne(not equal)查找字段不等于提供的值的节点。
in查找字段匹配提供的任何值的节点。
nin(not in)查找字段与提供的任何值都不匹配的节点。
gt(greater than)查找字段大于提供的值的节点。
gte(greater or equal)查找字段大于或等于提供的值的节点。
lt(less than)查找字段小于提供的值的节点。
lte(less than or equal)查找字段小于或等于提供的值的节点。
between查找字段值在提供的值之间的节点。
query {
  allProduct(filter: { price: { between: [49, 99] }}) {
    edges {
      node {
        title
        price
      }
    }
  }
}

本示例将仅查询 price 的值在 49 到 99 之间的节点。

Array 字段

Operator描述
size过滤具有指定大小的数组属性的节点。
contains查找字段包含提供的值的节点。
containsAny查找包含包含任何提供的值的字段的节点。
containsNone查找字段不包含任何提供的值的节点。
query {
  allPost(filter: { keywords: { contains: ["gridsome"] }}) {
    edges {
      node {
        title
        keywords
      }
    }
  }
}

本示例将仅查询具有 gridsome keyword 的节点。

分类页面

GraphQL 模式中的字段可以引用其他节点。这是组织页面并在页面之间建立链接的好方法。每个节点都有一个 belongsTo 字段,该字段能够列出引用它的所有其他节点。belongsTo 字段的工作方式类似于具有 totalCountpageInfoedge 的集合,但是 edge 字段始终是 并集字段 ,可以是任何节点类型

创建分类页面

在此示例中,我们将创建两个集合,即 PostTag 类型。我们在 gridsome.server.js 文件的 loadSource 钩子中执行此操作。 Post 节点将具有一个 tags 字段,该字段将是 Tag id 数组。

api.loadSource(actions => {
  const posts = actions.addCollection('Post')
  const tags = actions.addCollection('Tag')

  // makes all ids in the `tags` field reference a `Tag`
  posts.addReference('tags', 'Tag')

  tags.addNode({
    id: '1',
    title: 'The author'
  })

  posts.addNode({
    id: '1',
    title: 'A post',
    tags: ['1']
  })
}

现在,我们在 src/templates 中创建一个 Tag.vue 文件,以此为我们的标签页提供模板。每个标签页面都会有一个列表,其中包含对其进行引用的帖子

<template>
  <Layout>
    <h1>{{ $page.tag.title }}</h1>
    <ul>
      <li v-for="edge in $page.tag.belongsTo.edges" :key="edge.node.id">
        <g-link :to="edge.node.path">
          {{ edge.node.title }}
        </g-link>
      </li>
    </ul>
  </Layout>
</template>

<page-query>
query ($id: ID!) {
  tag(id: $id) {
    title
    belongsTo {
      edges {
        node {
          ... on Post {
            id
            title
            path
          }
        }
      }
    }
  }
}
</page-query>

上方的标签页将显示帖子列表及其链接。

分页的分类页面

@paginate 指令放置在 belongsTo 字段之后以激活分页。该查询将有一个 $page 变量可用于传递到 belongsTo page 参数。

query ($id: ID!, $page: Int) {
  tag(id: $id) {
    title
    belongsTo(page: $page) @paginate {
      totalCount
      pageInfo {
        totalPages
        currentPage
      }
      edges {
        node {
          ... on Post {
            id
            title
            path
          }
        }
      }
    }
  }
}

belongsTo 的参数

ArgumentDefaultDescription
sortBy“date”按节点字段排序。
orderDESC按 order 字段排序 (DESC or ASC).
sort按多个节点字段排序
skip0跳过多少个节点
limit要获得多少个节点。
page获取哪个页面。
perPage每页显示多少个节点。如果未提供 page 参数,则省略。
filter{}idtypeName 过滤节点。

分页数据

在 GraphQL 查询中使用 @paginate 指令可为集合添加自动分页。该查询将收到 $page:Int 变量,可用于加载特定页面的源。每页的默认节点为 25

分页采集

@paginate 指令放置在要分页的集合之后。

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

分页分类页面

@paginate 指令放置在您要分页的 belongsTo 字段之后。

query ($page: Int) {
  category {
    title
    belongsTo(perPage: 10, page: $page) @paginate {
      pageInfo {
        totalPages
        currentPage
      }
      edges {
        node {
          ... on Post {
            id
            title
            path
          }
        }
      }
    }
  }
}

分页组件

Gridsome 具有内置的 Pager 组件,可轻松进行分页。从我们的组件中的 gridsome 导入它以使用它。该组件至少需要 pageInfo.totalPagespageInfo.currentPage 字段才能正确呈现。

示例

<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>
属性默认描述
inforequired来自 GraphQL 结果和页面总数的页面信息
showLinkstrue显示导航链接
showNavigationtrue显示上一个和下一个链接
range5显示多少个链接
linkClass将自定义类添加到链接
firstLabel«
prevLabel
nextLabel
lastLabel»
ariaLabel分页导航
ariaLinkLabel转到第几页
ariaFirstLabel转到第一页
ariaCurrentLabel当前页面。第几页
ariaPrevLabel转到上一页。第几页
ariaNextLabel转到下一页。第几页
ariaLastLabel转到最后一页。第几页

全局 meta 数据

Gridsome 使您可以使用 Data store API 添加全局 meta 数据。要使用该 API,您需要在 Gridsome 项目的根文件夹中有一个 gridsome.server.js 文件。meta 数据是静态的,不能从客户端更新或更改。

:::tip
如果您有想要全局访问的数据,而不必包含在任何 GraphQL 集合中,则填充 meta 数据非常有用。
:::

这是一个例子:

module.exports = function (api) {
  api.loadSource(async (store) => {
    store.addMetadata('message', 'This is a global text');
  });
};

meta 数据将在 meta 数据 GraphQL 根字段中可用。可以像其他任何数据一样获取 meta 数据。

这是有关如何在 Vue 组件中使用它的示例:

<template>
  <h1 v-html="$static.metadata.message" />
</template>

<static-query>
query {
  metadata {
    message
  }
}
</static-query>

客户端数据

从内部页面获取

从其他内部 page-query 页面查询结果和 page context。以下示例从 /other-page 获取数据并存储结果。

export default {
  data() {
    return {
      otherPage: null,
    };
  },
  async mounted() {
    try {
      const results = await this.$fetch('/other-page');
      this.otherPage = results.data;
    } catch (error) {
      console.log(error);
    }
  },
};

fetch 方法也可以从 gridsome 导入。

import { fetch } from 'gridsome';

export default {
  async mounted() {
    const results = await fetch('/other-page');
    console.log(results.data);
  },
};

阅读有关 $fetch() 方法的更多信息。

更多

下一篇——封装 Vue.js 组件库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白驹过隙时光荏苒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值