在这篇文章中,我们将首先看看应用初始化代码在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对象。
为了证明这一点,让我们看看下面的例子--正如你所看到的,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实例,这些实例在默认情况下不共享任何功能,即使它们是在一个文件中创建的。
然而,如果你想在2个实例之间共享一些功能--你可以这样做!在下面的例子中,vue3AppOne 和vue3AppTwo 共享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应用程序的初始化代码的区别,以及新方法的好处。
