繁體中文 | English
# Plugin ???
The most important change in VuePress 1.x is
Pluggable. VP has a lot of core functions implemented by plugins, and offers many options (opens new window) for developer to build their plugins. Moreover,
theme/index.js are also plugins.
It flashed on my mind that if I figure out the execution sequence of all the plugin APIs and what is really executed, maybe it's a special approach to understand the ins and outs of how VP works.
# Warm up
First, let's check out all the plugin options:
// hooks ready generated // options chainWebpack extendMarkdown chainMarkdown enhanceAppFiles extendPageData clientDynamicModules clientRootMixin additionalPages globalUIComponents define alias extendCli beforeDevServer afterDevServer
I've created a diagram to show the whole life-cycle. There's a switch button at header. You can simply click it and the diagram might show up. Note that it is scrollable. Hope it will be a useful reference.
# How do plugins work
All the plugin options will be in initialized at the beginning. They all have their own class. You can take a look at
@vuepress/core/lib/node/plugin-api/abstract. Later, whenever VP get the plugin config, it'll push them into each corresponding option. And at the timing you saw in my diagram, they will be executed.
We all know VP can be started by
vuepress dev or
vuepress build. No matter triggered by which one, it has to enter this process. (Btw, I call it
Prepare because the function which handle this process in VuePress 0.x is named
Suppose you have already known VP uses Markdown-it (opens new window) as the Markdown renderer.
The first two APIs in the life-cycle are:
Edit the internal Markdown config with markdown-it-chain —— A chaining API like webpack-chain but for markdown-it.
A function to edit default config or apply extra plugins to the markdown-it instance used to render source files.
First, VP has to create markdown config. VP leverages markdown-it-chain (opens new window) which is developed directly on the basis of webpack-chain. In the config, VP will add a bunch of markdown-it plugins (most of them is created by VP).
Then, VP is going to run the plugin API
chainMarkdown which is going to config markdown-it as the same way above. After markdown-it-chain created a markdown-it instance with all the above configuration, VP starts to run
Those impressive features about writing Markdown might be accomplished here, such as
Custom Containers which offers shortcuts to create beautiful UI components,
Internal Links converted to
<router-link> for SPA navigation, etc (opens new window).
The next APIs I'm going to talk about are:
A function used to extend or edit the $page object. This function will be invoking once for each page at compile time.
Add a page pointing to a Markdown file.
Finding all page source files located in sourceDir, VP is going to resolve them. It generates the page title and headers, resolves frontmatters, and so on. This information is saved for rendering later.
VP will run
extendPageData after each page is processed. After all of that, It's time to run
additionalPages. The pages added by
additionalPages will be resolve as the same way, so
extendPageData will also be invoked.
There're five options in this section:
The ready hook is executed after the application is initialized and before some specific functional APIs are executed.
Generate some client modules at compile time.
A path to the mixin file which allows you to control the lifecycle of root component.
This option accepts absolute file path(s) pointing to the enhancement file(s), or a function that returns the path(s), which allows you to do some App Level Enhancements.
Inject some global UI fixed somewhere on the page.
VP will run
ready after pages are resolved. You might be interested to know styling (opens new window) is completed by an internal plugin which leverages
clientDynamicModules the option used most often by internal plugins. That's why we can access Global Computed (opens new window), use SPA navigation without manual configuration of Vue router paths and how page components or layout components be imported. Moreover, the following option
clientRootMixin is also handled by
clientDynamicModules. It'll generate some temp files so that VuePress client can use them. You can simply open
@vuepress/core/.temp and check it out.
globalUIComponents are processed by a similar way. VuePress client will do some App Level Enhancements and register those UI components by the temp files generated by them.
The next following things VP will do depends on which command you actually run.
# Custom commands
Register a extra command to enhance the CLI of VuePress. The function will be called with a CAC (opens new window)'s instance as the first argument.
VP leverages CAC (opens new window) to build its Command-line Interface. Before CAC parses the arguments, VP will check node version, register core commands (opens new window) and handle unknown commands which is included your custom command.
Note that you have to tell VP where's your source files, e.g.
vuepress hello docs. VP has to run all I've just mentioned earlier, otherwise it can't access
# Create webpack config
VP split out webpack config into three files: base, client and server. Kind of similar to what Vue SSR Guide suggest (opens new window). It'll invoke the function to create base config no matter it's creating client or server config.
We only need to care client config in this section: Let's take a look at the relevant options:
VuePress opened up a concise define option, note that the values has been automatically processed by JSON.stringify.
The way to set aliases more like configuration than via
Edit the internal webpack config with webpack-chain.
VP is going to do a lot of things in webpack config. I'd like to mention there's a markdown loader in VP which
.md file into Vue single-file component. Since all
.md files are SFC, it can be handled by Vue loader.
Of course, VP will also create global constants and set alias at compile time. Global constants are defined for such as debugging, etc. Its relevant API
define is actually used by many official plugins. When processing client, VP benefits from aliases, such as accessing those temp files generated by
In the end of creating client config, VP run
chainWebpack which offers the last chance to make the last config.
# Dev server
VP leverages webpack-dev-server (opens new window) to handle development.
Equivalent to before in webpack-dev-server. You can use it to define custom handlers before all middleware is executed.
Equivalent to after in webpack-dev-server. You can use it to execute custom middleware after all other middleware.
Whenever a source file is added or removed, config or frontmatters are changed, VuePress will rerun Prepare to reach real hot-reload. Source files and config are watched by chokidar (opens new window). In the other hand, watching frontmatters are handled by the markdown loader I mentioned before.
# Create webpack config
Unlike Dev, in this section, we do care both client and server config:
Those options executed by creating client config are also executed by creating server config, so we're not going to discuss them again, you can take a look at the previous section.
But there's some different config between
Dev. The most important thing is using Vue SSR client and server plugin. It'll generate manifest files which provide information for rendering.
# Generate pages
The last option:
Called when a (production) build finishes, with an array of generated page HTML paths.
What to be generate has been prepared earlier. But before generating, VP will add a 404 page if it doesn't exist. It means VP will also run
When generating each file, VP will invoke createBundleRenderer (opens new window) which helps VP render HTML with those manifest files. Further infos, please head to Vue SSR (opens new window). Those manifest files will be removed very soon after use. That's why you've never seen them in your output folder.
When everything is done, VP run
# Wrap up
So, this approach really led me from beginning to end. Although it may not cover every concept, it saved me from thinking where should I start? Hope it can help you get clear about VP or feel more comfortable to dive into the source code.