Question:
Why does my Vue.js compile render function not work?

Problem

I'm trying to render the template of the Vue component from a string. I tested it on simple HTML but I would like to render a Komplex string including another component.


I use VueJS version 3.3.4 and for compilation import { compile } from 'vue-template-compiler'; I can't use Vue from vue because in my version it doesn't exist.

I tried a few versions and I got to the point, where I could compile string to object with a render function that I'm not able to use.


I made a component with the render method which is called the compile method on string and returned an object with the render function. The problem is that the render function can't be called. First, it is because it is a string of function and not a function. The second problem is that when I convert it to function by new Function() and call it, it crashes to not defined functions like _c(), and _v() which are in the generated function.


My component:

<FsDynamicHtml :html="content"></FsDynamicHtml>


content="<h1> Test </h1>"


<script lang="ts">

    import { defineComponent } from 'vue'

    import { compile } from 'vue-template-compiler';


export default defineComponent({

    name: "FsDynamicHtml",

    components: { compile },

    props: {

        html: { type: String},

    },

    computed: {

       

        

    },

    methods: {

        template: function (){

            if (this.html) {

                const compiled = compile(this.html); //returns object with render (string) "with(this){return _c('h1',[_v(" Test ")])}"

                return compiled;

            }

            return null;

        },

    },

    render(createElement) {

        if (this.template) { 

            const render = this.template().render

            const fnc = new Function(render);  

            return fnc();  //Uncaught (in promise) ReferenceError: _c is not defined

            //I tried bind createElement and this to function like (new Function(render).bind(this))

            //return render; //write text: with(this){return _c('h1',[_v(" Test ")])}

            //return this.template(); //[Vue warn]: Invalid VNode type: undefined (undefined)  at <FsDynamicHtml html = "<h1>Test</h1>"> 

            //return new Function(this.template().render)(); //Uncaught (in promise) ReferenceError: _c is not defined

            

        }

        return null;

    }

})

</script>


<style scoped>


</style>


I commented in code some tried ways with results.


I don't really know what should i return from the render function, I expected the result of the function render of compilated HTML.


Thank you for your help.


Edit: Working version for Me (thx Ali Bahrami):


<script lang="ts">

    import { defineComponent, compile } from 'vue';


    export default defineComponent({

        name: "FsDynamicHtml",

        props: {

            html: { type: String },

        },

        computed: {

            compiledRenderFn() {

                if (this.html) {

                    var  render = compile("<span style='color: red'>zzz</span>"+this.html);

                    return render;

                }

                return null;

            },

        },

        render() {

            if (this.compiledRenderFn) {

                return this.compiledRenderFn(this);

            }

            return null;

        },

    });

</script>


Compile returns function (below) which requires _ctx parameter and for that must be this.compiledRenderFn(this); called with this parameter or any parameter.


(function anonymous(Vue

) {

const _Vue = Vue

const { createElementVNode: _createElementVNode } = _Vue


const _hoisted_1 = /*#__PURE__*/_createElementVNode("span", { style: {"color":"red"} }, "zzz", -1 /* HOISTED */)

const _hoisted_2 = /*#__PURE__*/_createElementVNode("h1", null, " Test ", -1 /* HOISTED */)


return function render(_ctx, _cache) {

  with (_ctx) {

    const { createElementVNode: _createElementVNode, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue


    return (_openBlock(), _createElementBlock(_Fragment, null, [

      _hoisted_1,

      _hoisted_2

    ], 64 /* STABLE_FRAGMENT */))

  }

}

})


Solution

It seems like you're trying to compile a Vue template at runtime using the vue-template-compiler. However, you're using Vue 3, and the vue-template-compiler is meant for Vue 2. In Vue 3, the equivalent package is called @vue/compiler-sfc.


So, install it:


npm install @vue/compiler-sfc


Then update your script to import the compile function from the vue package and use it to compile your template to a render function:


<script lang="ts">

import { defineComponent, compile } from 'vue';


export default defineComponent({

    name: "FsDynamicHtml",

    props: {

        html: { type: String },

    },

    computed: {

        compiledRenderFn() {

            if (this.html) {

                const { render } = compile(this.html);

                return render;

            }

            return null;

        },

    },

    render() {

        if (this.compiledRenderFn) {

            return this.compiledRenderFn();

        }

        return null;

    },

});

</script>


Hope this helps!


Suggested blog

>How to get the date and time and display it in a defineProps in Vuejs?

>Why logged user object is all strings in Vuejs and Laravel 10?

>What is meant by progressive framework?

>How can I replace this.$parent in composition API in Vuejs?

>How do I fix an invalid route component in the new VueJs Project?

>How to get all the rows selected in Vuejs?

>How to set up a dynamic grid based on flex or grid in Vuejs?

>Create a project using Vue.js: Beginners Guide

>Authentication with Vue 3 and Firebase

>Plugins and Presets for Vuejs project

>Create a Vue.js application with CLI Services

>Create a project using Vue.js: Beginners Guide

>Create Vue.js application API


Nisha Patel

Nisha Patel

Submit
0 Answers