新的Vue 3应用程序初始化代码的方法

248 阅读1分钟

在这篇文章中,我们将首先看看应用初始化代码在Vue 2应用中是如何工作的。然后我们会看到它有哪些缺点,以及如何通过Vue框架第三版中使用的新初始化语法来消除这些缺点。

让我们从目前在Vue 2中进行初始化的方式开始。通常,在src/main.js 文件中,我们通过调用一个新的Vue作为构造函数来创建一个应用实例来引导应用。

import Vue from "vue";
import App from "./App.vue";
import router from './router'
import SomeComponent from '@/components/SomeComponent.vue'
import SomePlugin from '@/plugins/SomePlugin'

const vue2AppCopy = new Vue({
  router,
  render: h => h(App)
});

vue2AppCopy.component('SomeComponent',SomeComponent);
vue2AppCopy.use(SomePlugin);

vue2AppCopy.$mount('#app')

这个应用实例将在我们SPA的整个生命周期中为所有的逻辑服务。这一切都很好,大约3年来,我们一直使用这种语法来引导我们的Vue应用程序。

然而,在Vue 3中,初始化代码的语法已经改变了。让我们先看一下新的语法,然后再看看使用它的好处。

新的Vue 3 createApp方法

在Vue 3中,我们有一个专门的createApp 函数来做这件事。createApp函数接收一个根组件(App.vue )作为参数,并返回一个Vue应用实例。因此,最简单的应用初始化将看起来像这样。

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

由createApp返回的Vue应用实例也被称为应用上下文对象。这个对象可以用来在启动过程中进一步为应用程序添加更多的功能。下面是一个更高级的初始化代码例子。

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import SomeComponent from '@/components/SomeComponent.vue'
import SomePlugin from '@/plugins/SomePlugin'

const myV3App = createApp(App)
myV3App.component('SomeComponent', SomeComponent)
myV3App
.use(SomePlugin)
.use(router)
// add more functionality to myV3App

// now we're ready to mount
myV3App.mount('#app')

与V2相比,在添加额外的逻辑(如插件和组件)方面没有什么变化,对吗?

你可以在Vue 3的文档中找到支持方法的完整概述。

这很好,但有一个小但重要的变化--我们使用一个专用函数,而不是new Vue 实例。

//v2
const vue2App = new Vue({}) // Working with the main Vue instace
//v3
const myV3App = createApp(App).mount('#app') // Create a copy of the Vue instance

那么,为什么使用那个新的专用createApp 函数比使用new Vue 构造函数更好?

在Vue 2应用程序的初始化代码中,我们使用从库中导入的Vue对象来创建这个和所有其他新的应用程序实例。

通过这种方法,我们不可能将一些功能隔离到只有一个Vue实例,因为Vue应用仍然在使用从库中导入的同一个Vue对象。

为了证明这一点,让我们看看下面的例子--正如你所看到的,vue2AppOnevue2AppTwo 都会访问一个叫做myDirective 的指令。

Vue.directive('myDirective', {
    /* ... */
})

Vue.component({
  /* ... */
})

const vue2AppOne = new Vue(/**/).mount('#app1')
const vue2AppTwo = new Vue(/**/).mount('#app1')

在一个网站或应用程序中创建多个Vue应用程序可能不是那么常见。

Learn Vue.js 3 With Vue School

但随着项目规模的扩大,由不同的团队开发,以及前端微服务的普及,你可能会在某个时候发现自己也会这样做。

然后再想用v2的语法得到另一个具有不同功能的Vue实例就几乎不可能了。

Vue 3的新语法允许我们将每个应用的配置作为一个独立的自定义对象,因为应用的配置是使用一个专门的函数(createApp )来创建独立的实例。

新的架构让我们有可能拥有两个或多个独立的Vue实例,这些实例在默认情况下不共享任何功能,即使它们是在一个文件中创建的。

然而,如果你想在2个实例之间共享一些功能--你可以这样做!在下面的例子中,vue3AppOnevue3AppTwo 共享LocalePlugin ,但不共享SearchInputComponent

const config = {/* some global config */}

const vue3AppOne = Vue.createApp(config)
vue3AppOne.component('SearchInput', SearchInputComponent)
vue3AppOne.use(LocalePlugin)

const vue3AppTwo = Vue.createApp(config)
vue3AppTwo.use(LocalePlugin)

为了演示这种行为,我们创建了一个代码库,里面有2个简单的Vue 3实例,由于新的createApp 语法,这些实例不共享组件和指令。请看一下它,在本地玩玩。

在配套的资源库中,我们在一个页面模板上的2个不同的容器中初始化了2个Vue 3应用程序,见public/index.html

        <div id="header-app"></div>
        <div id="main-app"></div>

一个应用程序将作为一个相对简单的标题标记,而另一个将能够使用路由器并与商店一起工作。

利用Vue 3的语法,我们可以在src/main.js文件的初始化代码中轻松地将它们分开。

import { createApp } from 'vue'
import App from './App.vue'
import Header from './Header.vue'
import router from './router'
import store from './store'

createApp(App)
  .use(store)
  .use(router)
  .mount('#main-app')

createApp(Header)
  .mount('#header-app')

如果你使用vue serve ,你应该能够看到两部分都在一个页面上工作。在控制台输出中,你会看到主程序可以访问vue-router ,并使用VueX ,而头程序没有。

  created () {
    console.log('Hello from Main app')
    console.log('Main app router', this.$route)
    console.log('Main app store:', this.$store)
  }

一个更直接的测试设置

如果你使用vue-test-utils (版本<2.0.0)为你的Vue 2组件编写测试,你可能已经遇到了需要使用createLocalVue 方法来避免污染全局Vue实例的情况。

在我们的测试场景中,我们有同样的潜在问题,因为我们在Vue 2应用程序中也有。当我们添加组件、插件等时,我们会污染全局的Vue实例,而它们都是与每个可用的Vue实例共享。

为了解决这个问题,我们必须使用createLocalVue ,它(你猜对了)创建一个新的本地Vue实例,它是隔离的。

import { createLocalVue, mount } from '@vue/test-utils'
import MyPlugin from '@/plugins/MyPlugin'

const localVueForTest = createLocalVue()
localVueForTest.use(MyPlugin)

mount(Component, {
  localVueForTest
})

这在Vue 3中不再是个问题,因为所有的应用扩展:插件、混合组件和全局组件都不会改变全局的Vue实例。
所以当在Vue 3中使用vue-test-utils (版本>=2.0.0)时,测试文件中的应用启动代码会是这样的。

import { createStore } from 'vuex'
import { mount } from '@vue/test-utils'
import App from '@/App'
import MyPlugin from '@/plugins/MyPlugin'

const wrapper = mount(App, {
  global: {
    plugins: [MyPlugin]
  }
})

总结

在这篇文章中,我们看了Vue 3和Vue 2应用程序的初始化代码的区别,以及新方法的好处。

Learn Vue.js 3 With Vue School