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: `<h1>Trump is the worst ${position}</h1>`;
});

或者声明一个全局指令:

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.useVue.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 的全局 API3.x 的应用实例 API
Vue.configapp.config
Vue.config.productionTip移除
Vue.config.ignoredElementsapp.config.isCustomElement
Vue.componentapp.component
Vue.directiveapp.directive
Vue.mixinapp.mixin
Vue.useapp.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: `<h1>Trump is the worst ${position}</h1>`;
});

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...(目前还没有中文版)

文章首发于我的卫星攻肿号,请点击:这里


劉凯里
841 声望1.6k 粉丝

我可能是一个假前端