zoukankan      html  css  js  c++  java
  • 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')

    果然还是实践出真知,网上的大部分答案都不靠谱 (⊙﹏⊙)

  • 相关阅读:
    Java基础知识回顾
    设计模式简单回顾
    数据结构基础知识
    《More Effective C#》读书笔记
    《Effective C#》读书笔记
    《编程匠艺》读书笔记
    《Scrum实战》读书会作业01
    开始一段新的敏捷学习之旅 —— IT帮读书会第4期《Scrum实战》
    【译】别学框架,学架构
    AngularJS学习笔记(1)
  • 原文地址:https://www.cnblogs.com/zjjDaily/p/12931624.html
Copyright © 2011-2022 走看看