Vue 3:全局 API 被取消了?
Vue 的 2.x 版本有很多的全局 API 和 配置,他們會(huì)在全局范圍內(nèi)改變 Vue 的行為。
- “
- 比如常見的全局 API 有:Vue.component / Vue.mixin / Vue.extend / Vue.nextTick;
- 常見的全局配置有:Vue.config.silent / Vue.config.devtools / Vue.config.productionTip
比如(官方例子),如果你想創(chuàng)建一個(gè)全局的組件,你會(huì)用到 Vue.component:
- Vue.component('trump-sucks', {
- data: () => ({ position: 'America president', }),
- template: `<h1>Trump is the worst ${position}</h1>`;
- });
或者聲明一個(gè)全局指令:
- Vue.directive('focus', {
- inserted: el => {
- console.log('聚焦!');
- el.focus();
- },
- });
這樣確實(shí)比較方便,但是會(huì)造成一些問題。首先要明確的一點(diǎn)是,Vue 2.x 在設(shè)計(jì)上并沒有 app(應(yīng)用)的概念。開發(fā)者在使用 Vue 2.x 是所謂的 app 不過是一個(gè)用 new Vue()創(chuàng)建的 Vue 實(shí)例罷了(呵,不過如此)。由同一個(gè) Vue 構(gòu)造函數(shù)創(chuàng)建的 Vue 實(shí)例都會(huì)共享來自構(gòu)造函數(shù)的全局配置。這將會(huì)導(dǎo)致:
在測(cè)試過程中,由于全局配置的存在,測(cè)試用例很容易就會(huì)被“污染”。開發(fā)者需要小心翼翼地將全局配置找一個(gè)地方存下來,在每次測(cè)試結(jié)束后將其還原,比如 Vue.config.errorHandler;一些 API 比如 Vue.use 和 Vue.mixin 甚至沒有避免其影響的辦法。這使得在測(cè)試中一旦涉及了插件,整個(gè)過程都會(huì)變得非常棘手。事實(shí)上,為了解決這個(gè)問題,vue-test-utils 不得不引入一個(gè)特殊的 API:createLocalVue:
- import { createLocalVue, mount } from '@vue/test-utls';
- const localVue = createLocalVue();
- localVue.use(MyPlugin);
- mount(Component, { localVue });
還有一個(gè)避免不了的問題是,一旦頁(yè)面上有多個(gè) Vue 實(shí)例時(shí),它們的全局配置就很自然地共享了,但在很多時(shí)候開發(fā)者并不想要這樣的結(jié)果
- Vue.mixin({
- mounted: () => {
- console.log('wubba lubba dub dub');
- },
- });
- const rick = new Vue({ el: '#rick' });
- const morty = new Vue({ el: '#morty' });
因此,為了規(guī)避這些問題,Vue 3 引入了應(yīng)用實(shí)例的概念。
全局 API: createApp
調(diào)用 createApp 會(huì)返回一個(gè) 應(yīng)用實(shí)例,沒錯(cuò),應(yīng)用實(shí)例這個(gè)概念是 Vue 3 中新引入的。
- import { createApp } from 'vue';
- const app = createApp();
應(yīng)用實(shí)例會(huì)暴露一個(gè)當(dāng)前全局 API 的子集。在這個(gè)重構(gòu)工作中,Vue 團(tuán)隊(duì)秉承的經(jīng)驗(yàn)法則是:任何會(huì)在全局范圍內(nèi)影響 Vue 行為的 API 都會(huì)被遷移至應(yīng)用實(shí)例中去。
2.x 的全局 API 3.x 的應(yīng)用實(shí)例 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
其他不會(huì)在全局影響 Vue 行為的 api 都已改造為具名導(dǎo)出的構(gòu)建方式(named exports),就像之前尤雨溪尤大在直播里說的那樣:為了支持 TreeShaking。
掛載一個(gè)應(yīng)用實(shí)例
在使用 createApp(VueInstance) 得到一個(gè)應(yīng)用實(shí)例后,這個(gè)應(yīng)用實(shí)例就可以用來把整個(gè) Vue 跟實(shí)例掛載到頁(yè)面上了:
- import { createApp } from 'vue';
- import MyApp from './MyApp.vue';
- const app = createApp(MyApp);
- app.mount('#app');
在完成了這些改造之后,開篇我們提到的那些例子將會(huì)重寫成這樣:
- 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 所包含的組件樹內(nèi)創(chuàng)建的 Vue 實(shí)例才會(huì)共享 trump-sucks 這個(gè)組件和 focus 這個(gè)指令,而 Vue 構(gòu)造函數(shù)并沒有被污染。
多個(gè)應(yīng)用實(shí)例的配置共享
上文提到的“不是所有開發(fā)者都想要的全局配置共享”,在 Vue 3 中可以通過工廠函數(shù)的方式實(shí)現(xiàn):
- 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');
這樣就能實(shí)現(xiàn)多個(gè)應(yīng)用實(shí)例的配置共享了:蔡徐坤和吳亦凡都有了一個(gè)叫做“唱跳”的 Vue 自定義指令。