1.0.2 • Published 5 years ago

vuepress-plugin-cpt v1.0.2

Weekly downloads
3
License
MIT
Repository
github
Last release
5 years ago

Demos and examples

Basic(Default Options) - Demo Netlify Status | Code

i18n - Demo Netlify Status | Code

Getting started

Install

# via npm
$ npm install -D vuepress-plugin-cpt

# via yarn
$ yarn add -D vuepress-plugin-cpt

Usage

// .vuepress/config.js or .vuepress/theme/index.js

const registerPostTypes = {
  news: {
    label: 'News',
    postsDir: '_posts-news',
    taxonomys: {
      category: true,
      tag: true,
      author: true
    }
  }
};

module.exports = {
  plugins: [
    [
      'vuepress-plugin-cpt', registerPostTypes
    ],
    ...
  ]
}

2. File structure

demo
├─ index.md
└─ _posts-news
   └─ 1.md
   └─ hello-world.md

3. Article Front Matter

---
title: Hello World

// By default it is used for posts sorting.
date: 2018-01-01

// Add taxonomy terms you set up
category: Plugins
author: tmiame
tag:
  - Vue
  - VuePress

// Single
category: Plugins

// Multiple
category:
- Plugins
- Projects
---

4. Create Theme layout file required by default

demo
├─ index.md
└─ .vuepress
   └─ theme
      └─ layouts
         ├─ Posts.vue
         ├─ Post.vue
         ├─ Taxonomy.vue
         └─ Term.vue

5. Write layout template

Posts.vue
<div v-if="$cpt.posts">
  <router-link
    v-for="item of $cpt.posts"
    v-if="item && item.path"
    v-bind:to="item.path">
    <div
      v-if="item.taxonomys"
      v-for="taxonomy of item.taxonomys"
      v-on:click:stop.prevent>
      <span>{{ taxonomy.indexPage.label }}:</span>
      <router-link
        v-for="category of taxonomy.list"
        v-bind:to="category.path"
        v-text="category.name" />
    </div>
    <h2 v-if="item.title" v-text="item.title" />
    <div v-if="item.frontmatter.date" v-text="item.frontmatter.date" />
    <div v-if="item.frontmatter.description" v-text="item.frontmatter.description" />
  </router-link>
</div>

<div class="pagination" v-if="$cpt.postsPagination && $cpt.postsPagination.length">
  <router-link v-if="$cpt.postsPagination.hasPrev" v-bind:to="$cpt.postsPagination.prevLink">
    Prev
  </router-link>
  <router-link
    v-for="(item, index) of $cpt.postsPagination._paginationPages"
    v-bind:to="item.permalink"
    v-bind:disable="$cpt.postsPagination._currentPage.permalink === item.permalink">
    {{ index + 1 }}
  </router-link>
  <router-link v-if="$cpt.postsPagination.hasNext" v-bind:to="$cpt.postsPagination.nextLink">
    Next
  </router-link>
</div>
Post.vue
<template>
  <div class="breadcrumb">
    <router-link v-if="$cpt.indexPage" v-bind:to="$cpt.indexPage.path">
      < {{ $cpt.indexPage.label }}
    </router-link>
  </div>

  <h1>{{ $page.title }}</h1>

  <header v-if="$cpt.post">
    <div v-for="taxonomy of $cpt.post">
      <span>
        {{ taxonomy.indexPage.label }}:
      </span>
      <router-link
        v-for="category of taxonomy.list"
        v-bind:to="category.path"
        v-text="category.name"
      />
    </div>
    <h1 v-text="$page.title" />
  </header>

  <div class="content">
    <slot name="top" />
    <Content v-bind:custom="false" />
    <slot name="bottom" />
  </div>
</template>
Taxonomy.vue
<template>
  <div class="breadcrumb">
    <router-link v-if="$cpt.indexPage" v-bind:to="$cpt.indexPage.path">
      < {{ $cpt.indexPage.label }}
    </router-link>
  </div>

  <h1>{{ $page.title }}</h1>

  <div v-if="$cpt.terms">
    <router-link v-for="item of $cpt.terms.list" v-bind:to="item.path">
      <div v-text="item.name" />
      <div v-text="item.posts.length" />
    </router-link>
  </div>
</template>
Term.vue
<template>
  <div class="breadcrumb">
    <router-link v-if="$cpt.indexPage" v-bind:to="$cpt.indexPage.path">
      < {{ $cpt.indexPage.label }}
    </router-link>
    <router-link v-if="$cpt.terms.indexPage" v-bind:to="$cpt.terms.indexPage.path">
      < {{ $cpt.terms.indexPage.label }}
    </router-link>
  </div>

  <h1>{{ $page.title }}</h1>

  <ul v-if="$cpt.termPosts">
    <li v-for="item of $cpt.termPosts">
      <div v-if="item.taxonomys" v-for="taxonomy of item.taxonomys">
        <span>{{ taxonomy.indexPage.label }}:</span>
        <router-link v-for="category of taxonomy.list" v-bind:to="category.path" v-text="category.name" />
      </div>
      <router-link v-bind:to="item.path" v-text="item.title" />
      <router-link v-bind:to="item.path">
        <div v-if="item.frontmatter.date" v-text="item.frontmatter.date" />
        <div v-if="item.frontmatter.description" v-text="item.frontmatter.description" />
      </router-link>
    </li>
  </ul>
</template>

Documentation

Options

// .vuepress/config.js or .vuepress/theme/index.js

const registerPostTypes = {
  // (string) (required) Post type ID. (If `slug` and `label` is not set, this is used for URL and Title)
  news: {
    // (string) (required) directory path for posts
    postsDir: '_posts-news',

    // (string) (optional) It is used for URL. (If `label` is not set, this is used for Post type name)
    slug: 'my-news',

    // (string) (optional) Post type name except for URL.
    label: 'News',

    // (string) (optional)
    baseUrl: '/',

    posts: {
      // (string) (optional) layout of article list
      layout: 'Posts',

      // (string) (optional) title of article list
      // - Title variables
      // - %cpt = post type name
      title: '%cpt',

      // (function) (optional)
      postsSorter: ((prev, next) => {
        const prevTime = new Date(prev.frontmatter.date).getTime()
        const nextTime = new Date(next.frontmatter.date).getTime()
        return prevTime - nextTime < 0 ? -1 : 1
      }),

      // (object|boolean) (optional) pagination of article list
      // pagination: false, (Disable pagination)
      pagination: {
        // (number) (optional)
        perPagePosts: 10,
        // (string) (optional)
        paginationUrl: '/page/',
        // (string) (optional)
        // - Title variables
        // - %cpt = post type name
        // - %paginationIndex = page index number
        title: '%cpt - Page %paginationIndex'
      }
    },

    post: {
      // (string) (optional) layout of article
      layout: 'Post',
      // (string) (optional) before title of article
      // - Title variables
      // - %cpt = post type name
      titleBefore: '',
      // (string) (optional) after title of article
      // - Title variables
      // - %cpt = post type name
      titleAfter: ' | %cpt',
      // (string) (optional)
      // https://vuepress.vuejs.org/guide/permalinks.html
      permalink: '/:year/:month/:day/:slug'
    },

    // (object|boolean) (optional) - Default is {}
    taxonomys: {
      // (string) (required) Taxonomy ID. (If `slug` and `label` is not set, this is used for URL and Title)
      category: {
        // (string) (optional) It is used for URL. (If `label` is not set, this is used for Taxonomy name)
        slug: 'my-category',
        // (string) (optional) Taxonomy name
        label: 'Category',
        // (object|boolean) (optional)
        // terms: false, (Disable terms page)
        terms: {
          // (string) (optional) Terms page layout
          layout: 'Taxonomy',
          // (string) (optional) Terms page title
          // - Title variables
          // - %cpt = post type name
          // - %taxonomy = taxonomy name
          title: `%taxonomy | %cpt`
        },
        term: {
          // (string) (optional) Term page layout
          layout: 'Term',
          // (string) (optional) Term page title
          // - Title variables
          // - %cpt = post type name
          // - %taxonomy = taxonomy name
          // - %term = term name
          title: `%term - %taxonomy | %cpt`,
          // (function) (optional)
          postsSorter: ((prev, next) => {
            const prevTime = new Date(prev.frontmatter.date).getTime()
            const nextTime = new Date(next.frontmatter.date).getTime()
            return prevTime - nextTime < 0 ? -1 : 1
          }),
          // (object|boolean) (optional) pagination of Term page
          // pagination: false, (Disable pagination)
          pagination: {
            // (number) (optional)
            perPagePosts: 10,
            // (string) (optional)
            paginationUrl: '/page/',
            // (string) (optional)
            // - Title variables
            // - %cpt = post type name
            // - %taxonomy = taxonomy name
            // - %term = term name
            // - %paginationIndex = page index number
            title: '%term - %taxonomy - Page %paginationIndex | %cpt'
          }
        }
      },
      ...
    }
  }
  ...
}

module.exports = {
  plugins: [
    [
      'vuepress-plugin-cpt', registerPostTypes
    ],
  ]
}

Get data

this.$cpts - All pages

this.$cpts: Object
  news:
    indexPage: // Root page info data
    pagination: // Posts pagination data
    posts: // Posts data
    taxonomys: // Taxonomys data
  blog:
    indexPage:
    pagination:
    posts:
    taxonomys:

this.$cpt - Page under the custom post type

Posts page            -  /news/
Posts Pagination page -  /news/page/2/
Post page             -  /news/hello-world/
Taxonomy Page         -  /news/tag/
Term Page             -  /news/tag/VuePress/
Term Pagination Page  -  /news/tag/VuePress/page/2/
this.$cpt: Object|Boolean
  indexPage: // Root page info data
  post:// Post data (only Post page)
  posts: // Posts data
  postsPagination: // Posts pagination data
  taxonomys: // Taxonomys data
  terms: // All terms data (only Taxonomy, Term and Term Pagination page)
  term: // Term data (only Term and Term Pagination page)
  termPosts: // Term posts data (only Term and Term Pagination page)
  termPostsPagination: // Term posts pagination data (only Term and Term Pagination page)

Internationalization(i18n)

1. Set Site Level i18n Config

https://vuepress.vuejs.org/guide/i18n.html
Then, specify the locales option in .vuepress/config.js:

// .vuepress/config.js
module.exports = {
  locales: {
    // The key is the path for the locale to be nested under.
    // As a special case, the default locale can use '/' as its path.
    '/': {
      lang: 'en-US', // this will be set as the lang attribute on <html>
      title: 'VuePress',
      description: 'Vue-powered Static Site Generator'
    },
    '/zh/': {
      lang: 'zh-CN',
      title: 'VuePress',
      description: 'Vue 驱动的静态网站生成器'
    },
    '/jp/': {
      lang: 'ja-JP',
      title: 'VuePress',
      description: 'Vue-powered Static Site Generator'
    }
  }
}

2. Set Plugin i18n Config

// .vuepress/config.js or .vuepress/theme/index.js
const registerPostTypes = {
  locales: {
    '/': {
      news: {
        label: 'News',
        postsDir: '_posts-news',
        posts: {
          layout: 'Posts',
          title: `%cpt`,
          pagination: {
            title: `%cpt - Page %paginationIndex`
          }
        },
        post: {
          layout: 'Post'
        },
        taxonomys: {
          category: {
            label: 'Category',
            terms: {
              layout: 'Taxonomy',
              title: `%taxonomy - %cpt`
            },
            term: {
              layout: 'Term',
              title: `%term - %taxonomy - %cpt`,
              pagination: {
                title: `%termの投稿 - Page %paginationIndex`
              }
            }
          }
        }
      }
    },
    '/jp/': {
      news: {
        label: 'ニュース',
        postsDir: '/jp/_posts-news',
        posts: {
          layout: 'Posts',
          title: `%cpt`,
          pagination: {
            title: `%cpt - %paginationIndexページ目`
          }
        },
        post: {
          layout: 'Post'
        },
        taxonomys: {
          category: {
            label: 'カテゴリー',
            terms: {
              layout: 'Taxonomy',
              title: `%taxonomy一覧 - %cpt`
            },
            term: {
              layout: 'Term',
              title: `%taxonomy - %termの投稿 - %cpt`,
              pagination: {
                title: `%taxonomy - %termの投稿 - %paginationIndexページ目`
              }
            }
          }
        }
      }
    }
  }
}

module.exports = {
  plugins: [
    [ 'vuepress-plugin-cpt', registerPostTypes ],
    ...
  ]
}

Tips

  1. Set URL as root page
  2. Resource prefetch settings for many pages
  3. Build performance for many pages

1. Set URL as root page

const registerPostTypes = {
  news: {
    baseUrl: '/',
    ...
  }
}

> https://examples.com/news/

const registerPostTypes = {
  news: {
    baseUrl: '../',
    ...
  }
}

> https://examples.com/

2. Resource prefetch settings for many pages

https://vuepress.vuejs.org/config/#shouldprefetch

https://ssr.vuejs.org/api/#shouldprefetch

https://ssr.vuejs.org/api/#shouldpreload

module.exports = {
  ...
  shouldPrefetch: (file, type) => {
    if (type !== 'script') {
      return true
    }
  }
}

3. Build performance for many pages

https://github.com/vuejs/vuepress/issues/19

I see where you're coming from, but performance isn't a core focus of VuePress. The design is largely limited by webpack (having 1000 pages means 1000 Vue components being compiled via webpack and minified) and looks like it runs out of memory at some point with 1000 pages.
Tested that we can handle ~300 pages in around 40s, and there are probably quite a few low hanging fruits to improve perf as the current implementation essentially didn't take build perf into consideration at all.


1.0.2

5 years ago

1.0.1

5 years ago

1.0.0

5 years ago