Compile Markdown as Vue template on Nuxt.js dynamically
Every static site content is by Markdown
For implementing data pipeline for the static website, I used to have Markdown files for content parts from long time ago. Probably starting with Jekyll, then Middleman, Harp and Nuxt.js right now.
- Writing Posts by Markdown with Jekyll
https://jekyllrb.com/docs/posts/ - Middleman Markdown Engine
https://middlemanapp.com/basics/template-engine-options/#markdown - Markdown Preprocessor in Harp
http://harpjs.com/docs/development/markdown
Also about current project, decided to accept HTML on Markdown. And an additional spice, I provide expressive appearances with Vue component on Markdown.
If this original-component
works as Vue component, could do a lot of things, right?
# Title## Sub TitleParagraph.
<original-component caption="Original Caption" />
However, it was a bit tricky on Nuxt.js. This template section on Vue compoent was the base idea to render Markdown on Vue component:
<template>
<div v-html="renderedMarkdown" />
</template>
renderedMarkdown
is given as HTML compiled from Markdown data from JS side. This was completely straight forward, but couldn’t run as I expected unfortunately. The HTML on Markdown does not work as Vue template/component.
original-component
will be just rendered as plain HTML 😢
<h1>Title</h1>
<h2>Sub Title</h2>
<p>Paragraph.</p>
<original-component caption="Original Caption" />
As a matter of course, the content of v-html
is not compiled as Vue’s component dynamically. So requires to compile the HTML in runtime.
After some experiments/investigations, I found out this codes are minimum changes for realizing that. The most of the results are come from this thread actually.
Parent Component
Compiles markdown and passes to the no-ssr-ed component to evaluate as Vue template.
<template>
<div>
<no-ssr>
<dynamic-renderer :body="compiledMarkdown"/>
</no-ssr>
</div>
</template><script lang="ts">
import Vue from "vue";
import VueWithCompiler from "vue/dist/vue.esm";
import Component from "nuxt-class-component";
... @Component({
components: { DynamicRenderer }
})
export default class ParentComponent extends Vue {
@ContentState content; // From Vuex State get body (): string {
return new MarkdownIt({ html: true }).render(this.content);
}
}
</script>
<no-ssr>
is required to compile the HTML dynamically- Compiled markdown should be allowed having HTML
Child Component
Use render function by Vue.compile
<script lang="ts">
import Vue from "vue";
import VueWithCompiler from "vue/dist/vue.esm";
import Component from "nuxt-class-component"; @Component({
props: ["body"],
components: { OriginalComponent }
})
export default class DynamicRenderer extends Vue {
templateRender;
body: string; render (createElement) {
if (this.templateRender) {
return this.templateRender();
} else {
return createElement("div", "Looading");
}
} created () {
const compiled = VueWithCompiler.compile(`<div>${this.body}</div>`);
this.templateRender = compiled.render;
this.$options.staticRenderFns = [];
for (const staticRenderFunction of compiled.staticRenderFns) {
this.$options.staticRenderFns.push(staticRenderFunction);
}
}
}
</script>
- Compile is bundled in
vue/dist/vue
📝 Runtime + Compiler vs. Runtime-only - Register components which are called in Markdown such like OriginalComponent
- Don’t have template tag as SFC
- Compile passed a prop as Vue’s template (which should be single root elemnt 😉)
Notes
Bundle size is increased by importing “Full-build Vue”
Since the runtime-only builds are roughly 30% lighter-weight than their full-build counterparts, you should use it whenever you can.
I don’t think this difference will be significant rather other contents like images 😉 could be acceptable as static generated site.
Dynamic part couldn’t be pre-rendered
Should care about SEO for visitor without JS execution
Markdown should be rendered as template of Vue component before running Vue basically?
Probably, so. If my approach didn’t work, I would take that way. Although, I preferred to handle Markdown as the state on Vuex as possible, for comfortable development speed. didn’t want to have process to compile markdown avoiding HMR with Nuxt.js.