3
头图

foreword

In "An article that takes you to build a blog with VuePress + Github Pages" , we used VuePress to build a blog, but browse the final built site: TypeScript4 Chinese document , we will find that at the bottom of each article, There is no last update time like the official VuePress documentation:

In this article, we will explore how to implement the last update time.

Officially brought

Check VuePress of official document , we can know, VuePress Last updated plug-in that comes with time, the default theme, no need to install this plug-in, because the core VuePress This plug-in is already included.

You can use it directly config.js

// .vuepress/config.js
module.exports = {
  themeConfig: {
    lastUpdated: '上次更新', // string | boolean
  }
}

Note that themeConfig.lastUpdated is off by default, and when a string is given, it will be displayed as a prefix (default: Last Updated).

In this sample code, we set lastUpdated to "last update", and the final effect should be consistent with the screenshot of the VuePress official website above.

Here, it also gives a usage note:

Since lastUpdated is git-based, you can only enable it in a git-based project. Also, since the timestamp used is from a git commit, it will only show up after the first commit for a given page, and will only be updated when subsequent commits change to that page.

Then we try to configure it according to the document, and it turns out that there is no so-called last update time, because there is no content to display, it seems that the blank here is also very large:

View plugin source code

So why is this? Under review our code compilation and submission process, in fact, we are locally written source code, build and then execute the command, the result of building git init , then forced to submit to a remote repository. This can be seen from our deploy.sh

npm run docs:build

cd docs/.vuepress/dist

git init
git add -A
git commit -m 'deploy'

git push -f git@github.com:mqyqingfeng/learn-typescript.git master:gh-pages

Then there should be no problem. We submitted it as a git repository and also submitted a commit. Why can't it be displayed?

And speaking of it, how is the last update time of last updated generated? How did he automatically generate the time based on the git commit record?

To solve this problem, and to satisfy our curiosity, we might see this npm package @vuepress/plugin-last-updated of source , and found that its source code unexpected simple:

const path = require('path')
const spawn = require('cross-spawn')

/**
 * @type {import('@vuepress/types').Plugin}
 */
module.exports = (options = {}, context) => ({
  extendPageData ($page) {
    const { transformer, dateOptions } = options
    const timestamp = getGitLastUpdatedTimeStamp($page._filePath)
    const $lang = $page._computed.$lang
    if (timestamp) {
      const lastUpdated = typeof transformer === 'function'
        ? transformer(timestamp, $lang)
        : defaultTransformer(timestamp, $lang, dateOptions)
      $page.lastUpdated = lastUpdated
      $page.lastUpdatedTimestamp = timestamp
    }
  }
})

function defaultTransformer (timestamp, lang, dateOptions) {
  return new Date(timestamp).toLocaleString(lang, dateOptions)
}

function getGitLastUpdatedTimeStamp (filePath) {
  let lastUpdated
  try {
    lastUpdated = parseInt(spawn.sync(
      'git',
      ['log', '-1', '--format=%at', path.basename(filePath)],
      { cwd: path.dirname(filePath) }
    ).stdout.toString('utf-8')) * 1000
  } catch (e) { /* do not handle for now */ }
  return lastUpdated
}

Through the source code, we can find that the time can be generated by using the git log command to obtain the time, and then write it into the $page global attribute.

$page

So what is $page? Let's check the official document - Global Computed Properties chapter:

In VuePress, some core computed properties built-in for use by default or custom themes.

For each page, there will be a $page computed property, and the official also gave an example of $page

{
  "title": "Global Computed",
  "frontmatter": {
    "sidebar": "auto"
  },
  "regularPath": "/zh/miscellaneous/global-computed.html",
  "key": "v-bc9a3e3f9692d",
  "path": "/zh/miscellaneous/global-computed.html",
  "headers": [
    {
      "level": 2,
      "title": "$site",
      "slug": "site"
    },
    {
      "level": 2,
      "title": "$page",
      "slug": "page"
    },
    ...
  ]
}

We can think from this that VuePress actually generates a $page computed property for each page, and then displays the value of the computed property in multiple places, then the breakthrough is coming. If we write a lastUpdated $page properties?

Using Vue in Markdown

Each of our pages is generated based on a markdown file. How do we write a lastUpdated $page in a markdown file? Fortunately, in VuePress, markdown can directly use Vue syntax. Here we use the sample code of the vuepress-theme-reco As an explanation:

// 在 markdown 文案中直接写

<p class="demo" :class="$style.example"></p>

<style module>
.example {
  color: #41b883;
}
</style>

<script>
export default {
  props: ['slot-key'],
  mounted () {
    document.querySelector(`.${this.$style.example}`)
      .textContent = '这个块是被内联的脚本渲染的,样式也采用了内联样式。'
  }
}
</script>

The display effect on the page is as follows:

Then we directly write the following Vue code directly in Markdown:

<script>
export default {
    mounted () {
      this.$page.lastUpdated = "2022/1/6 下午6:09:09";
    }
  }
</script>

We run the code locally, and then check the page, we will find that the last update time is actually written successfully:

Vue components

But it is very troublesome to insert a piece of Vue code in each article. I can reluctantly accept to manually update the time, but I can't accept that every time so many articles have to be manually changed, even if it is replaced in batches, it is a bit troublesome. an optimized way?

Then we can extract this Vue code into a Vue component, encapsulate the specific time in the component, and then import each md file into this component, and VuePress can also support the introduction of components. Check out the official document :

All *.vue files found in .vuepress/components will be automatically registered as global async components like:
.
└─ .vuepress
   └─ components
      ├─ demo-1.vue
      ├─ OtherComponent.vue
      └─ Foo
         └─ Bar.vue
You can use these components directly in any Markdown file (the component name is taken from the filename):
<demo-1/>
<OtherComponent/>
<Foo-Bar/>

Then we create a new .vuepress components folder, and then create a LastUpdated.vue file with the code:

<template>
</template>

<script>
export default {
  props: ['slot-key'],
  mounted () {
      this.$page.lastUpdated = "2022/1/6 下午2:00:00";
  }
};
</script>

Then we write in the md file that specifically uses this time:

<LastUpdated />

Naturally, the time is also successfully written:

In this way, every time we modify the time in the LastUpdated component, the time in all places where the component is introduced will change.

Markdown

This problem seems to be solved by saving the country through a curve, that is, it needs to be updated manually every time, and all the referenced places will be updated. If you want to achieve a local update in a certain place, you have to write the time yourself, which is actually a bit troublesome. of.

Let's review the introduction in the official documentation:

Since lastUpdated is git-based, you can only enable it in a git-based project. Also, since the timestamp used is from a git commit, it will only show up after the first commit for a given page, and will only be updated when subsequent commits change to that page.

There is also the sentence in the official document:

lastUpdated is the UNIX timestamp of the last git commit for this file

So which file does "this file" in this sentence refer to? Recalling our submission method, we only submit the compiled file each time. The compiled file is an HTML file. Although it is submitted in the form of a git repository, it should be static after it is compiled. It is impossible to obtain the last submission time through the git repository when running the HTML file, so this file should not refer to the compiled file, then exclude the compiled file. In fact, we can think that the file here refers to should actually be the markdown file you wrote. When you execute the compile command, get the time from the git log and write it into the compiled code.

This can also explain why the time is not displayed after we configure it according to the official documentation, because our source code is indeed not a git repository, we just generate a code repository to submit the compiled code, and naturally we cannot get the time.

So we enter the root directory of the source code, and then execute git init . Note that if you want to generate the last update time, you need to:

  1. repository git init
  2. The file must be committed at least once

But when we git add , an error may be reported, and you will be prompted that another git repository has been added to your current repository:

You've added another git repository inside your current repository.

This is because the dist directory we compiled and generated is also a git repository, but the solution is actually very simple. We can just ignore the dist .gitignore file .gitignore file writes:

node_modules
dist

When we run the code after commit, we will see the last update time for each article:

Last updated

If you want to change the Last Updated: front of the time, you can write config.js

module.exports = {
    themeConfig: {
        lastUpdated: '上次更新'
    }
}

The displayed effect will become:

Change the format

If you want to change the format of the time display, refer to @vuepress/plugin-last-updated, you can write:

const moment = require('moment');

module.exports = {
  plugins: [
    [
      '@vuepress/last-updated',
      {
        transformer: (timestamp, lang) => {
          // 不要忘了安装 moment
          const moment = require('moment')
          moment.locale(lang)
          return moment(timestamp).fromNow()
        }
      }
    ]
  ]
}

The displayed effect is:

So far, although it has been around in a circle, the problem is still simply solved.

series of articles

The blog building series is the only series of practical tutorials I have written so far, explaining how to use VuePress to build a blog and deploy it to GitHub, Gitee, personal servers and other platforms.

  1. An article takes you to build a blog with VuePress + GitHub Pages
  2. An article that teaches you how to synchronize code with GitHub and Gitee
  3. Not using GitHub Actions yet? Check out this
  4. How does Gitee automatically deploy Pages? Or use GitHub Actions!
  5. A front-end Linux command
  6. A simple and sufficient Nginx Location configuration explanation
  7. An article that teaches you how to deploy your blog to your own server

WeChat: "mqyqingfeng", add me to the only readership of Xianyu.

If there are any mistakes or inaccuracies, please be sure to correct me, thank you very much. If you like or have inspiration, welcome to star, which is also an encouragement to the author.


冴羽
9.3k 声望6.3k 粉丝