I've been building a static blog with Nuxt.js, and I'm using frontmatter-markdown-loader
with [Mode.VUE_RENDER_FUNCTIONS]
to render Markdown pages and posts which contain Vue components. This was working great on v2.3.0, but after upgrading to v3.1.0, I cannot properly render Markdown files which are loaded dynamically using Nuxt's asyncData
function.
Here's my dynamic render component:
<!-- components/DynamicMarkdown.vue -->
<script>
export default {
props: {
renderFn: {
type: Function,
required: true,
},
staticRenderFns: {
type: Array,
required: true,
},
},
data() {
return {
templateRender: null,
};
},
created() {
this.templateRender = this.renderFn;
this.$options.staticRenderFns = this.staticRenderFns;
},
render(createElement) {
return this.templateRender ? this.templateRender() : createElement('div', 'Rendering...');
},
};
</script>
And here is the page component for individual blog posts:
<!-- pages/blog/_slug.vue -->
<template>
<DynamicMarkdown
:render-fn="renderFn"
:static-render-fns="staticRenderFns"
/>
</template>
<script>
import DynamicMarkdown from '~/components/DynamicMarkdown.vue';
export default {
components: {
DynamicMarkdown,
},
async asyncData({ params }) {
const article = await import(`~/content/articles/${params.slug}.md`);
return {
renderFn: article.vue.render,
staticRenderFns: article.vue.staticRenderFns,
};
},
};
</script>
This works if I link to a blog post from somewhere else in the app (ie, the post is rendered client-side). However, if I reload the page, or if I visit the permalink directly, the page crashes and I see several errors. In the browser console, I get TypeError: this.templateRender is not a function
.
And in the terminal I see two warnings: WARN Cannot stringify a function render
and WARN Cannot stringify a function
.
With FML version 2.3.0, this approach worked fine, both with client-side and server-side rendering.
Another releveant bit of information is that if I first load a Markdown file at the top of my <script>
section using regular ES6 module syntax, everything works fine.
The following code allows the page to be loaded either client-side or server-side:
<!-- pages/blog/_slug.vue -->
<template>
<DynamicMarkdown
:render-fn="renderFn"
:static-render-fns="staticRenderFns"
/>
</template>
<script>
import DynamicMarkdown from '~/components/DynamicMarkdown.vue';
import article from '~/content/articles/2019-10-14-my-post.md';
export default {
components: {
DynamicMarkdown,
},
data() {
return {
fm: null,
renderFn: null,
staticRenderFns: null,
};
},
created() {
this.fm = article.attributes;
this.renderFn = article.vue.render;
this.staticRenderFns = article.vue.staticRenderFns;
},
};
</script>
Obviously, the previous code example is not practical since blog posts must be loaded dynamically by extracting the file name from params. Hence the need for asyncData
imports.
In summary, if I import a Markdown file using ES6 module syntax, everything works. But if I import it inside asyncData
it breaks.
If it helps to see a complete app that demonstrates this issue, please have a look at nuxt-markdown-blog-starter by @marinaaisa. I referenced her code a lot when building my own blog (thank you, @marinaaisa!), and after she recently upgraded FML to v3.0.0, her app manifests the exact problem I have described above.
I am aware that FML v3.0.0 introduced breaking changes, and as best I can tell the root issue is that vue.render
and vue.staticRenderFns
now return functions instead of strings. I've looked at your source code to try and find a workaround, but I'm afraid my understanding of Vue render functions is too rudimentary.
Thank you for all your work on frontmatter-markdown-loader
. I really love this project since it enables Vue components to be embedded in Markdown files. This is a huge win for blogging, and I really hope a solution can be found to allow for asyncData
file imports. I would appreciate any help or advice you can offer on this issue!