Vue 的 2.x 版本有很多的全局 API 和 配置,他们会在全局范围内改变 Vue 的行为。
比如常见的全局 API 有:Vue.component / Vue.mixin / Vue.extend / Vue.nextTick;常见的全局配置有:Vue.config.silent / Vue.config.devtools / Vue.config.productionTip
比如(官方例子),如果你想创建一个全局的组件,你会用到 Vue.component
:
Vue.component('trump-sucks', {
data: () => ({ position: 'America president', }),
template: `Trump is the worst ${position}
`;
});
或者声明一个全局指令:
Vue.directive('focus', {
inserted: el => {
console.log('聚焦!');
el.focus();
},
});
这样确实比较方便,但是会造成一些问题。首先要明确的一点是,Vue 2.x 在设计上并没有 app(应用)的概念。开发者在使用 Vue 2.x 时所谓的 app 不过是一个用 new Vue()
创建的 Vue 实例罢了(呵,不过如此)。由同一个 Vue 构造函数创建的 Vue 实例都会共享来自构造函数的全局配置。这将会导致:
在测试过程中,由于全局配置的存在,测试用例很容易就会被“污染”。开发者需要小心翼翼地讲全局配置找一个地方存下来,在每次测试结束后将其还原,比如 Vue.config.errorHandler
;一些 API 比如 Vue.use
和 Vue.mixin
甚至没有避免其影响的办法。这使得在测试中一旦涉及了插件,整个过程都会变得非常棘手。事实上,为了解决这个问题,vue-test-utils 不得不引入一个特殊的 API:createLocalVue
:
import { createLocalVue, mount } from '@vue/test-utls';
const localVue = createLocalVue();
localVue.use(MyPlugin);
mount(Component, { localVue });
还有一个避免不了的问题是,一旦页面上有多个 Vue 实例时,它们的全局配置就很自然地共享了,但在很多时候开发者并不想要这样的结果
Vue.mixin({
mounted: () => {
console.log('wubba lubba dub dub');
},
});
const rick = new Vue({ el: '#rick' });
const morty = new Vue({ el: '#morty' });
因此,为了规避这些问题,Vue 3 引入了应用实例的概念。
全局 API: createApp
调用 createApp
会返回一个 应用实例,没错,应用实例这个概念是 Vue 3 中新引入的。
import { createApp } from 'vue';
const app = createApp();
应用实例会暴露一个当前全局 API 的子集。在这个重构工作中,Vue 团队秉承的经验法则是:任何会在全局范围内影响 Vue 行为的 API 都会被迁移至应用实例中去。
2.x 的全局 API | 3.x 的应用实例 API |
---|---|
Vue.config | app.config |
Vue.config.productionTip | 移除 |
Vue.config.ignoredElements | app.config.isCustomElement |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use |
其他不会在全局影响 Vue 行为的 api 都已改造为具名导出的构建方式(named exports),就像之前尤雨溪尤大在直播里说的那样:为了支持 TreeShaking。
挂载一个应用实例
在使用 createApp(VueInstance)
得到一个应用实例后,这个应用实例就可以用来把整个 Vue 跟实例挂载到页面上了:
import { createApp } from 'vue';
import MyApp from './MyApp.vue';
const app = createApp(MyApp);
app.mount('#app');
在完成了这些改造之后,开篇我们提到的那些例子将会重写成这样:
app.component('trump-sucks', {
data: () => ({ position: 'America president', }),
template: `Trump is the worst ${position}
`;
});
app.directive('focus', {
inserted: el => {
console.log('聚焦!');
el.focus();
},
});
// 至此,所有在 app 所包含的组件树内创建的 Vue 实例才会共享 trump-sucks 这个组件和 focus 这个指令,而 Vue 构造函数并没有被污染。
多个应用实例的配置共享
上文提到的“不是所有开发者都想要的全局配置共享”,在 Vue 3 中可以通过工厂函数的方式实现:
import { createApp } from 'vue';
import CaiXuKun from './CXK.vue';
import WuYiFan from './WYF.vue';
const createIdolApp = (IdolInstance) => {
const idolApp = createApp(IdolInstance);
idolApp.directive('sing-and-dance', {
inserted: () => {
console.log('I am cool!');
},
});
}
createIdolApp(CaiXuKun).mount('#caixukun');
createIdolApp(WuYiFan).mount('#wuyifan');
这样就能实现多个应用实例的配置共享了:蔡徐坤和吴亦凡都有了一个叫做“唱跳”的 Vue 自定义指令。
想要了解更多信息,请访问官方: https://v3.vuejs.org/guide/mi...(目前还没有中文版)文章首发于我的卫星攻肿号,请点击:这里