admin管理员组

文章数量:1336632

Is there a way for a .vue file to be responsible for creating its own Vue instance in a Single File Component pattern?

Here's the Vue File.

// MyComponent.vue
<template><div>Hello {{ name }}!</div></template>

<script>
const Vue = require('vue');

// what would usually be exports default
const ponentConfig = {
    name: "my-ponent",
    props: {
        name: String,
    },
};

function create(el, props) {
    const vm = new Vue({
        el,
        render(h) {
            return h(ponentConfig, { props });
        });
    vm.$mount();
    return vm;
}

module.exports = { create };
</script>

and then the usage in some JS file:

// index.js
const MyComponent = require('./MyComponent.vue');

const el = '.container';
const props = {
    name: 'Jess',
};

MyComponent.create(el, props);
</script>

When I do the above, I get errors about not being able to find the template.

[Vue warn]: Failed to mount ponent: template or render function not defined.

found in

---> <MyComponent>
       <Root>

Like instinctually, I don't understand how the Vue piler would be able to magically deduce (from within the script tags) that I want to reference the template declared above... so.. yeah. Is there an explanation for why I can't do this, or thoughts on how I could get it to work?

Is there a way for a .vue file to be responsible for creating its own Vue instance in a Single File Component pattern?

Here's the Vue File.

// MyComponent.vue
<template><div>Hello {{ name }}!</div></template>

<script>
const Vue = require('vue');

// what would usually be exports default
const ponentConfig = {
    name: "my-ponent",
    props: {
        name: String,
    },
};

function create(el, props) {
    const vm = new Vue({
        el,
        render(h) {
            return h(ponentConfig, { props });
        });
    vm.$mount();
    return vm;
}

module.exports = { create };
</script>

and then the usage in some JS file:

// index.js
const MyComponent = require('./MyComponent.vue');

const el = '.container';
const props = {
    name: 'Jess',
};

MyComponent.create(el, props);
</script>

When I do the above, I get errors about not being able to find the template.

[Vue warn]: Failed to mount ponent: template or render function not defined.

found in

---> <MyComponent>
       <Root>

Like instinctually, I don't understand how the Vue piler would be able to magically deduce (from within the script tags) that I want to reference the template declared above... so.. yeah. Is there an explanation for why I can't do this, or thoughts on how I could get it to work?

Share Improve this question edited Oct 18, 2018 at 21:12 Jess asked Oct 18, 2018 at 21:06 JessJess 3,1462 gold badges17 silver badges42 bronze badges 3
  • 2 Your question seems to be that you want to instantiate Vue on the ponent instead of in index.js. That is definitely counter to the main pattern Vue sets forth. Why do you want to do that? – For the Name Commented Oct 22, 2018 at 17:16
  • We have a larger system where multiple root Vue instances will be in control of individual parts of a page. There is no one "index.js" file in our case, we have many sections of a page that must be managed individually. That, in bination with using Vue in an existing code base (because starting greenfield is a dream, right?) means that we want to give the direct usages of Vue a single home. So we settled on the thought that the ponents that are entry points to the rest of the Vue stack could encapsulate the setting up and tearing down of their own root Vue instance. – Jess Commented Oct 23, 2018 at 15:51
  • We're also a plugin, so we have limitations of when we can instantiate and the type of environment we can assume. As a plugin, using a framework is an interesting problem and we're trying to stick to the textbook/most-supported path of Vue as much as possible... Although encapsulating the mounting and setup of a Component's root vue instance was a desire my group mentioned. So we're looking into it. – Jess Commented Oct 23, 2018 at 15:53
Add a ment  | 

2 Answers 2

Reset to default 4 +150

What you are describing is done in a pre-pilation step through Webpack and Vue Loader. The Vue piler doesn't actually parse Single File Components. What the Vue piler can parse is templates provided in a ponent’s options object. So if you provide a template option in your ponentConfig object your example will work. Otherwise you'll have to go through the pre-pilation step with Webpack and Vue Loader to parse a Single File Component's template. To do that you'll have to conform to the SFC structure defined in the spec. Here's an excerpt ..

Template

  • Each *.vue file can contain at most one <template> block at a time.
  • Contents will be extracted and passed on to vue-template-piler and pre-piled into JavaScript render functions, and finally injected into the exported ponent in the <script> section.

Script

  • Each *.vue file can contain at most one block at a time.
  • The script is executed as an ES Module.
  • The default export should be a Vue.js ponent options object. Exporting an extended constructor created by Vue.extend() is also supported, but a plain object is preferred.
  • Any webpack rules that match against .js files (or the extension specified by the lang attribute) will be applied to contents in the <script> block as well.

To make your specific example work You can re-write main.js file like this ..

const MyComponent = require("./MyComponent.vue");

const el = ".container";
const data = {
  name: "Jess"
};

MyComponent.create(el, data);

And your MyComponent.vue file (This could just as well be a js file as @Ferrybig mentioned below) ..

<script>
const Vue = require('vue');

function create(el, data) {
  const ponentConfig = {
    el,
    name: "my-ponent",
    data,
    template: `<div>Hello {{ name }}!</div>`
  };
  const vm = new Vue(ponentConfig);
  return vm;
}

module.exports = { create };
</script>

See this CodeSandbox

Or if you prefer render functions your MyComponent.vue will look like this ..

<script>
const Vue = require('vue');

function create(el, data) {
  const ponentConfig = {
    el,
    name: "my-ponent",
    data,
    render(h) { return h('div', 'Hello ' + data.name) } 
  };
  const vm = new Vue(ponentConfig);
  return vm;
}

module.exports = { create };
</script>

CodeSandbox


One last thing to keep in mind: In any ponent you can use either a template or a render function, but not both like you do in your example. This is because one of them will override the other. For example, see the JSFiddle Vue boilerplate and notice how when you add a render function the template gets overridden. This would explain that error you were getting. The render function took precedence, but you fed it a ponent's options object that provides no template to be rendered.

You are really close to a working solution, but you are missing just some "glue" parts to bine everything together:

<template>
    <div>Hello {{ name }}!</div>
</template>
<script>
    import HelloWorld from "./ponents/HelloWorld";
    import Vue from "vue";

    const Component = {
        // Add data here that normally goes into the "export default" section
        name: "my-ponent",
        props: {
            name: String,
        },
        data() {
            return {
            };
        },

    };
    Component.create = function(el, props) {
        const vm = new Vue({
            el,
            render(h) {
                return h(Component, { props });
            },
        });
        vm.$mount();
        return vm;
    };
    export default Component;
</script>

This can then be used as follows in other files:

import App from "./App";
App.create("#app", {
    name: 'Hello World!',
});

Example on codesandbox: https://codesandbox.io/s/m61klzlwy

本文标签: javascriptCreating the root Vue instance inside of a Vue fileStack Overflow