1. 程式人生 > >vue + typescript,定義全域性變數或者方法

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')

 

果然還是實踐出真知,網上的大部分答案都不靠譜 (⊙﹏