如何跨多个文件中的多个 Vue 实例正确使用 Vue 3 composition-api

Posted

技术标签:

【中文标题】如何跨多个文件中的多个 Vue 实例正确使用 Vue 3 composition-api【英文标题】:How to correctly use Vue 3 composition-api across multiple Vue instances in multiple files 【发布时间】:2021-02-06 23:01:40 【问题描述】:

tl;dr 在基础 js 文件中导入 Vue3 的正确方法是什么,然后在将在该基础文件之后加载的其他独立 js 文件中使用 Vue 的 composition-api?


我正在使用 Vue 来增强特定页面的用户体验。例如,在注册页面以执行 ajax 请求并显示服务器错误而无需重新加载页面。简而言之,MPA 中的迷你 SPA...

在 Vue2 中,在基础文件中导入 Vue,然后实例化一个新的 Vue 实例并在后续文件中使用 Vue 的 options-api 没有问题。并且后续文件的大小保持最小,因为它们只包含该文件所需的逻辑。

但是,由于 Vue3 的 composition-api 需要导入 ref, reactive, watch, onMount...etc,导致后面的文件重新导入 Vue。我试图克服这个问题如下:

// 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');

这对于这个简单的例子来说很好,但我想知道前缀 Vue. 的方法是否正确?是否可以使用该方法访问 Vue 为 setup() 方法公开的所有函数?

编辑: 我正在使用 laravel mix 包装的 webpack,为了简单起见,我从初始代码中删除了它,但我认为它被证明是相关的。对最初提供的代码中的编辑进行注释以避免混淆。

// 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();

【问题讨论】:

【参考方案1】:

在每个.js 文件中从 Vue 3(refwatchcomputed...)导入单独的部分时,应该没有额外的开销。事实上,如果您使用的是捆绑器,这将有助于摇树过程使生成的文件更小 (great explanation by Evan You)。

导入所有 Vue 并以当前方式使用它并没有错,类似于在 Vue 2 中的使用方式。如果语法困扰您,您可以解构您使用的内容,即

// 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');

编辑:

我对 Laravel Mix 不熟悉,但也许你可以试试这样:

// 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';

【讨论】:

谢谢,这工作得很好。我更新了代码以更好地展示捆绑的处理方式。我想知道是否有更好的方法来尝试在main.js 中导入以获得正确的摇树,因为我想现在 Vue 是作为一个整体导入的。因此,如果我要跟踪main.js 之后的所有后续文件使用的所有功能,是否有更好的导入方法并且仍然能够使用不带前缀的语法?再次感谢const ref, computed = Vue; 我已经更新了我的答案。我想知道是不是因为使用了require 而不是import 提取确实生成了manifest.jsvendor.js,其中vendor.js 的大小反映了整个Vue 被导入。然后,当在任何其他文件中使用 import Vue from 'vue'; 导入 Vue 时,最终会在该文件中再次导入 Vue。您的第一个答案对于最初的问题已经很完美了,至于关于摇树的后续问题,也许 Laravel Mix 本身需要修改以支持 Vue3 的正确摇树。再次感谢const ref, computed = Vue; 更新:当前版本的 Laravel Mix 5 仅支持 Vue2,Laravel Mix 6 的 beta version 支持 Vue3,tree shaking 工作正常。提取webpack.mix.js 中的供应商文件时要考虑的一个错误是,使用mix.extract(['vue']) 会导致在从vue 导入的所有文件中复制vue,而使用通配符mix.extract() 提取所有使用的node_modules 库时,会导致正确的摇树和vue在从 vue 导入的任何文件中都没有重复。【参考方案2】:

你可以使用Vue.refref 如果你像这样import ref,reactive from 'vue/dist/vue.js'; 那样描述

这应该不是必需的:

window.Vue = Vue;

只要您在每个文件中使用import Vue from 'vue/dist/vue.js';,例如register.js(或解构版本)。即使您有多个导入,也不意味着它将被多次重新包含。打包器只会添加一次 Vue(或部分 Vue 库)。

【讨论】:

通过在register.js 中添加import ref, computed from 'vue'; 并使用laravel mix 编译,文件大小从几千字节跃升至1.04 MiB。我认为这是因为 webpack 不知道 register.js 会在 main.js 之后出现,并且会继承 main.js 中已经导入的内容。如果我在编辑前的问题没有画出完整的画面,请接受我的道歉

以上是关于如何跨多个文件中的多个 Vue 实例正确使用 Vue 3 composition-api的主要内容,如果未能解决你的问题,请参考以下文章

Vue检查多个单选按钮

如何跨同一个 Web 应用程序的多个实例存储会话信息? [复制]

如何使用 Vue JS 组合多个 Vue JS 实例(与多个 html div 关联)?

如何不使用多个 Vue 的实例

Vue 一个或多个实例?

如何跨多个视图控制器引用同一个类的实例?