How to correctly use Vue 3 composition-api across multiple Vue instances in multiple files

Tags: , , , ,



tl;dr What is the correct way to import Vue3 in a base js file, then use Vue’s composition-api in other standalone js files that will be loaded after that base file?


I am using Vue to enhance user experience in specific pages. For example, in register page to perform ajax requests and show server errors without page reloads. In short, mini SPAs within MPA…

In Vue2, importing Vue in the base file, then instantiating a new Vue instance and using Vue’s options-api in subsequent files worked without problems. And the size of subsequent files were kept minimal since they contained only the logic needed for that file.

However, due to the fact that Vue3’s composition-api requires importing ref, reactive, watch, onMount...etc, this results in the subsequent files importing Vue all over again. My attempt to overcome this is as follows:

// register.blade.php (contains)
<div id="root">
  <input type="text" name="first_name" v-model="FirstName">
  <input type="text" name="last_name" v-model="LastName">
// (before edit) <script src="main.js"></script>
// (before edit) <script src="register.js"></script>
  <script src="{{ mix('/main.js') }}"></script>
  <script src="{{ mix('/register.js') }}"></script>
</div>
// main.js (contains)
// (before edit) import Vue from 'node_modules/vue/dist/vue.js';
// (before edit) window.Vue = Vue;
window.Vue = require('vue');
// register.js (contains)
const app = Vue.createApp({
  setup() {
    const FirstName = Vue.ref('');
    const LastName = Vue.ref('');
    const FullName = Vue.computed(() => FirstName.value + ' ' + LastName.value);

    return {
      FirstName, LastName, FullName
    }
  }
});
app.mount('#root');

This works fine for this trivial example, but I am wondering if the approach of prefixing with Vue. is even correct? Is it ok to access all the functions exposed by Vue for the setup() method using that approach ?

EDIT: I am using webpack wrapped by laravel mix, I stripped that from the initial code for simplicity, but I think it proved relevant. Edits in the initially provided code are commented to avoid confusion.

// webpack.mix.js (contains)
mix.webpackConfig({
  resolve: {
    alias: {
      'vue$': path.resolve(__dirname, 'node_modules/vue/dist/vue.esm-bundler.js'),
    }
  }
});

mix.js('resources/main.js', 'public/main.js')
  .js('resources/register.js', 'public/register.js')
  .version();

Answer

There should be no extra overhead when importing the separate parts from Vue 3 (ref, watch, computed…) in every one of your .js files. In fact, if you’re using a bundler this will help the tree shaking process make the resulting files smaller (great explanation by Evan You).

There’s nothing wrong with importing all of Vue and using it the way you currently are, similar to how you would have in Vue 2. If the syntax bothers you, you could destructure what you use, i.e.

// register.js (contains)
const { ref, computed } = Vue;

const app = Vue.createApp({
  setup() {
    const FirstName = ref('');
    const LastName = ref('');
    const FullName = computed(() => FirstName.value + ' ' + LastName.value);

    return {
      FirstName, LastName, FullName
    }
  }
});

app.mount('#root');

EDIT:

I’m not familiar with Laravel Mix, but perhaps you could try something like this:

// webpack.mix.js (contains)
const jsfiles = [
    'resources/main.js',
    'public/main.js',
    'resources/register.js',
    'public/register.js',
];

mix.js(...jsFiles).extract(['vue']).webpackConfig({
    resolve: {
        alias: {
            'vue$': path.resolve(__dirname, 'node_modules/vue/dist/vue.esm-bundler.js'),
        }
    }
}).version();

// now in your `register.js` and `main.js` use `import`, not `require`
//
// import Vue from 'vue';


Source: stackoverflow