在这篇文章中,我们将首先看看Vue 2应用程序中的应用程序初始化代码是如何工作的。然后我们会看到它有哪些缺点,以及如何通过Vue框架第3版中使用的新初始化语法消除这些缺点。
我们先说说目前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 3初始化代码的好处
在Vue 2应用程序初始化代码中,我们使用了从库中导入的Vue对象来创建此应用程序实例以及所有其他新的应用程序实例。
使用这种方法,不可能将一些功能仅隔离到一个Vue实例中,因为Vue应用程序仍然使用从库中导入的同一个Vue对象。
为了演示这一点,让我们看看下面的例子——正如你所看到的,vue2AppOne
和 vue2AppTwo
都可以访问一个名为 myDirective
的指令:
Vue.directive('myDirective', {
/* ... */
})
Vue.component({
/* ... */
})
const vue2AppOne = new Vue(/**/).mount('#app1')
const vue2AppTwo = new Vue(/**/).mount('#app1')
在一个网站或应用程序中创建多个Vue应用程序可能并不常见。
但随着项目规模的扩大,由不同的团队开发,以及前台微服务的流行,你可能会在某个时候发现自己也在这样做。
然后,几乎不可能使用v2语法获得另一个具有不同功能的Vue实例。
Vue 3中的新语法允许我们将每个应用程序的配置作为一个单独的自定义对象,因为应用程序是使用专用函数(createApp
)来创建独立实例的。
新的体系结构使我们可以拥有两个或更多孤立的Vue实例,默认情况下它们不共享任何特性,即使它们是在一个文件中创建的。
然而,如果你想在两个实例之间共享一些功能,你可以!在下面的例子中,vue3AppOne
和 vue3AppTwo
共享 LocalePlugin
,但不共享 searchchinputcomponent
。
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
语法,这些实例不共享组件和指令。请看一下它在本地的玩法。
在配套的仓库中,我们在一个页面模板的两个不同容器中初始化了两个Vue 3应用程序,见public/index.html。
一个应用程序将作为一个相对简单的header标记,而另一个将能够使用router并与store合作。
使用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
运行应用程序,您应该能够看到两个部分在一个页面上工作。在控制台输出中,您将看到Main应用程序可以使用 Vuex
访问 vue-router
和商店,而Header应用程序则不能。
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实例共享。
要解决这个问题,我们必须使用 createlocalvalue
,它(您已经猜到了)创建一个新的孤立的本地Vue实例。
import { createLocalVue, mount } from '@vue/test-utils'
import MyPlugin from '@/plugins/MyPlugin'
const localVueForTest = createLocalVue()
localVueForTest.use(MyPlugin)
mount(Component, {
localVueForTest
})
这在Vue 3中不再是一个问题,因为所有的应用扩展:插件、mixins和全局组件都不会改变全局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]
}
})