How to set the last updated time of VuePress blog optimization

preface

stay A blog with VuePress + Github Pages In, we built a blog using VuePress, but browse the final site: TypeScript4 Chinese document , we will find that the last update time does not appear at the bottom of each article as in the official VuePress document:

In this article, let's explore how to achieve the final update time.

Official

Review VuePress's Official documents , we can know that VuePress comes with a plug-in that displays the last update time. Under the default theme, you do not need to install this plug-in, because it is already included in VuePress's core.

You can use config JS file:

// .vuepress/config.js
module.exports = {
  themeConfig: {
    lastUpdated: 'Last update', // string | boolean
  }
}

Note that themeconfig Lastupdated is off by default. After a string is given, it will be displayed as a prefix (the default value is: Last Updated).

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

Here, an instruction for use is also given:

Since lastUpdated is git based, you can only enable it in a git based project. In addition, since the timestamp used is from git commit, it will be displayed only after the first submission of a given page and updated only when the page subsequently commits changes.

Let's try the configuration according to the document. The result shows that the so-called last update time does not appear, because there is no content display, so the space here is also very large:

View plug-in source code

Then why? Review our code compilation and submission process. In fact, we write the source code locally, execute the build command, git init the built result, and then force it to be submitted to the remote warehouse. This is from our deploy The SH script file shows:

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

There should be no problem. We submitted a commit as a git repository. Why can't it be displayed?

Moreover, how is the last updated time generated? How did he automatically generate the time according to git's submission records?

To solve this problem and satisfy our curiosity, we might as well take a look at this npm package @ vuepress / plugin last updated Source code , the source code is surprisingly 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 write it to the $page global attribute.

$page

What's $page? Let's check it out Official document - global calculation properties Section:

In VuePress, some core functions are built in Calculation properties For default or custom themes.

For each page, there will be a $page calculation attribute. The official also gives 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"
    },
    ...
  ]
}

From this, we can imagine that VuePress actually generates a $page calculation attribute for each page, and then displays the value of the calculation attribute in multiple places. That's the breakthrough. What if we directly write a lastUpdated attribute to $page like the source code?

Using Vue in Markdown

Each page is generated according to the markdown file. How can we write a lastUpdated attribute to $page in a markdown file? Fortunately, in VuePress, markdown can directly use Vue syntax. Here, we use the example code of VuePress theme reco as an explanation:

// Write directly in markdown copy

<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 = 'This block is rendered by an inline script, and the style also adopts the inline style.'
  }
}
</script>

The display effect on the page is as follows:

Then we directly write the following Vue code in Markdown:

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

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

Vue components

However, it's troublesome to insert a section of Vue code into each article. I can barely accept the manual update time, but I can't accept that so many articles have to be manually changed every time. Even if it's a little troublesome to replace them in batch, is there no more optimized way?

Then we can extract this Vue code into a Vue component, encapsulate the specific time in the component, and then import this component into each md file. VuePress can also support the introduction of components. View next Official documents:

All in *. Found in vuepress/components vue files will be automatically registered as global asynchronous components, such as:

.
└─ .vuepress
   └─ components
      ├─ demo-1.vue
      ├─ OtherComponent.vue
      └─ Foo
         └─ Bar.vue

You can directly use these components in any Markdown file (the component name is obtained through the file name):

<demo-1/>
<OtherComponent/>
<Foo-Bar/>

Then we'll be there Create a new components folder under the vuepress folder, and then create a lastupdated Vue file, code:

<template>
</template>

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

Then we write in the md file that will use this time:

<LastUpdated />

Naturally, the time was successfully written:

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

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 referenced places will be updated. If you want to realize a local update, you have to write the time yourself, which is actually a little troublesome.

Let's review the introduction in the official documents:

Since lastUpdated is git based, you can only enable it in a git based project. In addition, since the timestamp used is from git commit, it will be displayed only after the first submission of a given page and updated only when the page subsequently commits changes.

And in the official documents This sentence:

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

Which document does "this document" in this sentence mean? Recall our submission method. We only submit the compiled file every time. The compiled file is an HTML file. Although it is submitted in the form of GIT warehouse, it should be static after compilation. It is impossible to obtain the final submission time through git warehouse when running the HTML file, Therefore, this file should not refer to the compiled file, excluding the compiled file. In fact, we can think that the file here should actually refer to the markdown file you wrote. When you execute the compile command, get the time from git log and write it into the compiled code.

This can also explain why we do not display the time after configuring according to the official documents, because our source code is really not a git warehouse. We just generate a code warehouse from the compiled code and submit it. Naturally, we can't get the time.

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

  1. Warehouse git init
  2. The file must be commit ted at least once

However, when we git add, we may report an error, which will prompt you that you have added another git warehouse in your current warehouse:

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

This is because the dist directory we compiled is also a git repository, but the solution is very simple. We use one directly The gitignore file just ignores the dist directory. This is Contents written in gitignore file:

node_modules
dist

After we commit and then run the code, we will see that the last update time will appear in each article:

Last updated

If you want to change the Last Updated: character in front of the time, you can JS write:

module.exports = {
    themeConfig: {
        lastUpdated: 'Last update'
    }
}

The effect of the display will become:

change formatting

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

const moment = require('moment');

module.exports = {
  plugins: [
    [
      '@vuepress/last-updated',
      {
        transformer: (timestamp, lang) => {
          // Don't forget to install moment
          const moment = require('moment')
          moment.locale(lang)
          return moment(timestamp).fromNow()
        }
      }
    ]
  ]
}

The effects displayed are:

So far, although we have made a circle, we have simply solved this problem.

Series articles

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

  1. One takes you to build a blog with VuePress + GitHub Pages
  2. An article teaches you to synchronize GitHub and Gitee
  3. Can't use GitHub Actions yet? Look at this one
  4. How does Gitee automatically deploy Pages? Still use GitHub Actions!
  5. A Linux command with sufficient front end
  6. A simple and sufficient explanation of Nginx Location configuration
  7. An article that teaches you how to deploy your blog to your own server

Wechat: "mqyqingfeng", add me to Yu Yu's only reader group.

If there is any mistake or lack of preciseness, please be sure to correct it. Thank you very much. If you like it or have some inspiration, welcome star, which is also an encouragement to the author.

Keywords: Javascript Front-end Vue.js vuepress

Added by saish on Fri, 07 Jan 2022 11:52:30 +0200