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.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: `<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 自定義指令。