Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Another way to fix this: Using Vue.js runtime-only #6

Open
jordan-thoms opened this issue Aug 6, 2018 · 3 comments
Open

Another way to fix this: Using Vue.js runtime-only #6

jordan-thoms opened this issue Aug 6, 2018 · 3 comments

Comments

@jordan-thoms
Copy link

jordan-thoms commented Aug 6, 2018

As I understand it, If you use the runtime-only version of vue ( see https://vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only ), then the compiler is not present in your production environment and the '{{' blocks that might be there from server-side templating will not cause issues.

It is a little tricky to use Vue.js without the compiler when doing server-side templating, I ended up writing a init script like this:

import Vue from 'vue'; // Make sure you are importing the runtime-only version, that is the default but you might have an override changing it

import MyComponent from '../components/my_component.vue'
import AnotherComponent from '../components/another_component.vue'

document.addEventListener('DOMContentLoaded', () => {
    // Rather than compiling the whole page with Vue, (which isn't possible anyway
    // as we aren't including the compiler), we find the relevant component elements
    // individually and instantiate them as standalone Vue apps.
    const components = { MyComponent, AnotherComponent }

    const instances = [];

    for (const component of Object.entries(components)) {
      const kebabCaseComponentName = component[0].replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();

      const componentElements = Array.from(document.querySelectorAll(kebabCaseComponentName));
  
      for (const componentElement of componentElements) {
        const propsData = {};

        // This applies the attributes from the component dom node to the Vue.js instance
        // So we can still pass data from Rails to the components.
        for (const attribute of componentElement.attributes) {
          propsData[attribute.name] = attribute.value;
        }

        const ComponentClass = Vue.extend(component[1]);
        const instance = new ComponentClass({
            el: componentElement,
            propsData,
            components
        });

        instances.push(instance);
      }
    }
})

And you can still use components in your html like: <my-component a-property="somevalue"></my-component>

( I based this code on https://github.com/drewjbartlett/vue-multivue ).

What do you all think of this approach? It works well for us as we had all our templates in .vue files anyway, so losing the runtime compilation was not a big issue.

@ernestoalejo
Copy link

@jordan-thoms Have you found a way of passing the content inside <my-component> as the default slot? (or named slots?)

@jordan-thoms
Copy link
Author

We don't use slots at the moment, but it looks like you should be able to set the slot contents after the const instance = line with instance.$slots.default = [ 'Some content'] (I'd guess you can set $slots.some_name for a named slot). You could read that value from the element with componentElement.innerHTML. There wouldn't be any interpolation of the slot content though as that's done by the compiler.

From : https://css-tricks.com/creating-vue-js-component-instances-programmatically/#article-header-id-3

@ernestoalejo
Copy link

I had a solution very similar to yours but using data atributes generated with backend helpers: the generated code looked like this: <div data-vue="mycomponent" data-$prop="3" data-propstr="mystr"></div>, the original backend template code looks natural like the rest of the HTML).

I found very quickly that recursive elements were the real problem. A navbar with an animated logo inside (two components), a modal with a responsive image inside (two components), and so on. At the end half my page was already dynamic (extracting the HTML, compiling the vnodes, assigning the slots) so I went forth and enabled it for the whole page because our content is from trusted users and moved all inline scripts outside the element that will have Vue to avoid warnings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants