vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
https://vuejs.org/
MIT License
47.38k stars 8.29k forks source link

Warning using compile() in render() on method calls #7910

Open grillonbleu opened 1 year ago

grillonbleu commented 1 year ago

Vue version

3.2.45

Link to minimal reproduction

https://codesandbox.io/s/fervent-platform-8m2hlu?file=/src/components/ContentRender.vue

Steps to reproduce

I have a component whose template is compiled dynamically through the compile() function. The use case is an online vue-based IDE which gives a real time preview of edited fragments.

It works great so far, except for the following warning. I get it twice for every time the compiled template uses a component method:

[Vue warn]: Property undefined was accessed during render but is not defined on instance.

Here is a minimal component which triggers the issue:

<script>
import { compile } from "vue";
export default {
  name: "ContentRender",
  methods: {
    hello: function (x) {
      return `hello ${x}`;
    },
  },
  render: function (vm) {
    const template = `<div :title="hello('world')">
      {{hello('world')}},
      {{hello('world')}},
      {{hello('world')}}
    </div>`;
    const compiledTemplate = compile(template);
    return compiledTemplate(vm);
  },
};
</script>

It can be observed here: https://codesandbox.io/s/fervent-platform-8m2hlu?file=/src/components/ContentRender.vue

What is expected?

Execution with no warning is expected.

What is actually happening?

Following warning appears twice for every component method call:

[Vue warn]: Property undefined was accessed during render but is not defined on instance.

The method calls otherwise resolve successfully.

System Info

No response

Any additional comments?

No response

toomsie commented 1 year ago

There is more than one way to compile in vue.js. I tried a different approach and it worked, but it might be far different than you want. You might be able to refactor it to something useful. I changed the files main.js, ContentRender.vue and App.vue
https://codesandbox.io/s/pedantic-kowalevski-3d2jb7?file=/src/components/ContentRender.vue

grillonbleu commented 1 year ago

I’m resorting to using compile() instead of the usual approach because the template is only known at runtime. It’s a Laravel+Inertia+Vite application where the users edit the template themselves. They’re stored and pulled from database.

In the full app, ContentRender receives its template as a prop, like this:

https://codesandbox.io/s/recursing-bell-gorsu5?file=/src/components/ContentRender.vue

toomsie commented 1 year ago

I should have used my wording correctly. Still used a compiler but in a different way. I used the compileToFunction. To get it working your way using theimport { compile } from "vue" perhaps the best way is to get the most simple working example of vue using a compiler. Perhaps there is a unit test already in this project you can use. Then build code around it until either the bug occurs where you can isolate it or the code is complete without errors. I will have a bash at it in a few hours time.

grillonbleu commented 1 year ago

@toomsie Oh OK, you moved the runtime compilation to App.vue instead with a call to compileToFunction(). It’s not being called though, because render() isn’t. This is because you kept the template. If we remove the template, render() gets properly executed and I get this error: _vue.compileToFunction is not a function

https://codesandbox.io/s/romantic-dust-xf2ybz?file=/src/App.vue

toomsie commented 1 year ago

I will tidy this up be as close to your original example. Truth is I barely know anything about vue.js

grillonbleu commented 1 year ago

Thanks for having a look. Hopefully someone knowledgeable about vue will chime in as well.

toomsie commented 1 year ago

Ok, I have created a hello world example of my own. Later today I will make it close to your example and hopefully I might even be able to tell you why the error happened. https://codesandbox.io/s/intelligent-sea-lwtdwy?file=/src/components/ContentRender.vue

toomsie commented 1 year ago

The code looks like your code. But I am not sure if the template is indirectly compiled or not. via the const template = this.$options.template;

https://codesandbox.io/s/saturday-18-f7welb?file=/src/App.vue

grillonbleu commented 1 year ago

This is fixed in Vue 3.3.0-alpha.4.

toomsie commented 1 year ago

Very nice. I guess we should have tested the code in different versions 😒.

mannymu commented 1 year ago
image image
mannymu commented 1 year ago
image