vue + typescript,定義全域性變數或者方法
眾所周知,在 vue中,如果想定義一個全域性變數的方法很簡單,直接在 vue的原型上掛載屬性或者方法即可。
但是,加上了typescript之後, Vue.prototype.$xxx = xxx 這種掛載方式就不行了。無論在哪裡都訪問不了掛載的內容。Vue原型上也沒有。那怎麼辦呢?
第一種方式(推薦):外掛
官方文件在 TypeScript 支援 這一項中的 增強型別以配合外掛使用 表示了可以用外掛的方式來定義全域性變數,然後用 xxx.d.ts 這種檔案來宣告型別。
那就開始開發外掛:官方開發外掛說明
外掛最重要的就是 install 方法。舉個最簡單的例子:
testInstall.ts
const protoInstall = { install: (Vue:any,options:any) => { Vue.prototype.$install = function(){ console.log('install') }; Vue.prototype.$testData = 'testData'; } } export default protoInstall;
(1)不要說我Vue:any,options:any 這種寫法,因為我不知道這兩個的型別到底是什麼ヾ(。 ̄□ ̄)ツ゜゜゜
main.ts
import hhInstall from './assets/js/testInstall'; Vue.use(hhInstall);
使用的時候:
結果:
(1)可以看到,雖然報未找到該屬性,或者該方法的錯誤,但是不影響結果。
(2)那怎麼解決這個問題呢?這時就需要新增 宣告檔案了。
解決:
(1)在src 下新建一個 xxx.d.ts 檔案 。我是在src下再新建一個type資料夾,然後再把剛才的宣告檔案放到 這資料夾中,方便管理。
(2)xxx 名稱可以隨便寫,反正我寫的 vue-prototype.d.ts 更清晰一點。
vue-prototype.d.ts
declare module "vue/types/vue" { interface Vue { $testData:string; $install:Function; } }
儲存後,如果未生效,再重新啟動一下專案就行。然後就有型別提示了。以後如果還需要新增全域性屬性或者方法,在外掛裡面掛載之後,宣告檔案裡面再新增型別說明。
不過我有不明白的地方:
(1) 官方文件說,確保在宣告補充的型別之前匯入 'vue'。我的宣告檔案 vue.prototype.d.ts 並未匯入vue,但是沒問題。為什麼呢?難道是我用vue-cli3 腳手架搭建的專案,shims-vue.d.ts 這個檔案已經匯入就不用了嗎?
(2) 這種 xxx.d.ts 檔案,vue內部是怎麼識別的?又是怎麼處理的呢?
第二種方法:mixin混入
由於是想全域性定義屬性和方法,那麼mixin也能實現。比如:
main.ts
const vueMixins = { data(){ return { $testData:'mixin testData', } }, methods:{ $install:function(){ console.log('mixin install') } } } Vue.mixin(vueMixins)
使用:
結果:
可以看到,定義的屬性 $testData 是undefined。而去看vue例項,發現掛載到 $data 裡面的。但是為什麼訪問不了,而只有 通過 this.$data.$testData 來訪問?這個我也不太清楚
相同的,出現屬性不存在的問題,還是要新增 宣告檔案來說明一下。
用這種方式,如果定義一個建構函式在data裡面,後面的方法都沒提示。不太方便。
最後貼上我自己的全域性事件匯流排程式碼,就是為了不想用的時候還要引入,才知道了 typescript 不能直接定義全域性變數的問題
ヾ(゚∀゚ゞ)
如果不想全域性引入,那就不搞成外掛的方式就行了,直接export default class xxx。
程式碼比較垃圾,輕噴。
assets/js/eventBus.ts
/** * @desc 全域性事件匯流排 */ /** * 事件資訊介面 */ interface EventItem { name:string; fun:Function; } /** * 判斷是否存在已經繫結的事件介面 */ interface JudgeStatus{ status:boolean; index:number } export class EventBusHandler{ private EventArr:EventItem[] = []; private static _instance: EventBusHandler; public static get instance(): EventBusHandler { if (!EventBusHandler._instance) { EventBusHandler._instance = new EventBusHandler(); } return EventBusHandler._instance; } /** * 判斷是否已經存在註冊的事件 * @param eventName 事件名 */ private judgeHadEventAlready(eventName: string):JudgeStatus{ let status = false; let pos = -1; this.EventArr.forEach((val,index) => { if(val.name === eventName){ status = true; pos = index; } }) return { status:status, index: pos } } /** * 事件監聽 * @param eventName 事件名 * @param func 回撥函式 */ public on(eventName: string, func: Function) { const statusTarget = this.judgeHadEventAlready(eventName); // 如果未監聽過則新增進去 if (!statusTarget.status) { this.EventArr.push({ name:eventName, fun:func }) } } /** * 事件觸發 * @param eventName 事件名 * @param arg 傳入的引數。如果想傳多個引數,可把 arg:any 換成 ...arg:any[] */ public emit(eventName: string, arg:any) { const statusTarget = this.judgeHadEventAlready(eventName); if(statusTarget.status){ const func = this.EventArr[statusTarget.index].fun; this.EventArr[statusTarget.index].fun = func; func(arg); }else{ console.warn('暫未監聽:'+eventName+ '事件'); } } /** * 事件移除 * @param eventName * @param func */ public remove(eventName: string) { const statusTarget = this.judgeHadEventAlready(eventName); if(statusTarget.status){ this.EventArr.splice(statusTarget.index,1); }else{ console.warn('未監聽:'+eventName+ '事件'); } } } const EventBusFunc = { install: (Vue:any,options:object) => { Vue.prototype.$eventBus = EventBusHandler.instance; } } export default EventBusFunc;View Code
宣告檔案:
import {EventBusHandler} from '@/assets/js/eventBus'; declare module "vue/types/vue" { interface Vue { $eventBus:EventBusHandler; } }
main.ts 引入:
import EventBus from './assets/js/eventBus'; Vue.use(EventBus);
使用:
this.$eventBus.on('func',() => {}) this.$eventBus.emit('func','1111') this.$eventBus.remove('func')
果然還是實踐出真知,網上的大部分答案都不靠譜 (⊙﹏