Vue.js compiled render function doesn't work

154 Views Asked by At

Im trying render template of vue component from string. I test it on simple html but I would like to render komplex string including another components.

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 few versions and I get to the point, where i can compile string to object with render function that Im not able use.

I made component with render method which call compile method on string and return object with render function. Problem is that render function can't be called. First it is because it is string of function and not a function. Second problem is that when i convert it to function by new Function() and call it, it crash to not defined functions like _c(), _v() which are in 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 dont really know what should i return from render function, i expected result of function render of compilated html.

Thank you for 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 require _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 */))
  }
}
})
1

There are 1 best solutions below

1
Ali Bahrami On BEST ANSWER

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 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>