Vue组件继承 您所在的位置:网站首页 vue组件继承扩展 Vue组件继承

Vue组件继承

2023-08-13 18:58| 来源: 网络整理| 查看: 265

公司后台系统使用了 Vue + ElementUI, 使用 TypeScript ElementUI 简单,但是还不够简单,就想写一些比较简单的和公司业务有关联的组件; 最简单的,例如 Autocomplete 组件, 例如,基于某一个类型的 Autocomplete 组件,用法如下: 简单的用法,好办: 自定义一个组件,里面包含有 elautocomplete 组件,然后写属性,写代码,轻松搞定;

    import { Component, Prop, Vue, Watch } from "vue-property-decorator"; import { FetchSuggestions, FetchSuggestionsCallback } from "element-ui/types/autocomplete"; @Component export default class extends Vue { @Prop() entityTypeName: string; @Prop() fetchSuggestions: FetchSuggestions; 。。。。。。。。 实体查询的代码 }

。。。。。 但是,很快问题来了,我的组件需要增加 placeholder 属性,这个也很见简单。。。  

    import { Component, Prop, Vue, Watch } from "vue-property-decorator"; import { FetchSuggestions, FetchSuggestionsCallback } from "element-ui/types/autocomplete"; @Component export default class extends Vue { @Prop() entityTypeName: string; @Prop() placeholder: string; @Prop() fetchSuggestions: FetchSuggestions; }

但是,很快问题来了,我的组件需要添加 class 属性, 于是,这样似乎没完没了了。。。因为基本上 el-autocomplete 里面的所有属性都需要一五一十的添加到我自定义的组件里,然后再添加到自定义的组件所包含的 el-autocomplete 里; 这还只是一个 autocomplete 组件,还有一堆的其他的 element-ui 组件 很明显,component模式不是一个好的想法;因为对于包裹的组件,我们需要暴露包裹的组件的每一个属性和方法以及事件,这样一想,当然使用继承更好; 最简单的,当然就是下面这样,

export default class EhayAutoComplete extends elementUI.Autocomplete { }

可惜,楼上这样的写法是错误的。。正确的写法是像下面这样:

​​​​​​​export default class EhayAutoComplete extends Vue.component('ElAutocomplete') { }

但是,郁闷的是,我们需要覆盖 ElAutocomplete 组件里面的 fetchSuggestions  属性,这样我们才能够使用 这样的方式, 然后我们的组件覆盖 fetchSuggestions 属性,让这个属性所对应的函数 能够根据 entityTypeName 去从服务器后台查询 客户列表,并显示在  Autocomplete 的下拉列表中 好吧,这个也不难,因为 fetchSuggestions 是 ELAutocomplete 定义的 Prop, 我们不能直接给这个 Prop 赋值,(要强制赋值貌似也可以,vue也就给一个警告而已),但是对于强迫症的俺而言,警告当然是不能接受的; 于是,不能直接给 fetchSuggestions  prop 赋值,那我自己定义一个 fetchSuggestions  属性, 像下面这样,

​​​​​​​export default class EhayAutoComplete extends Vue.component('ElAutocomplete') { fetchSuggestions: FetchSuggestions; created() { this.fetchSuggestions = (key: string , cb) => { let ary := [{ value: 'abc'}, { value: 'def' }]; cb(ary); }; } }

测试一下,貌似能够工作,但是,稀奇的事情发生了: 如果 autocomplete 没有内容时,点击 autocomplete 将显示 abc 和 def 两个下拉选项 但如果选中 abc 这一项,再次点击,居然就告诉我说 fetchSuggestions 函数没有定义;看起来,感觉上 el-autocomplete 在没有内容时,使用的是 this.fetchSuggestions 进行填充数组的查询; 但, el-autocomplete 选中了内容时,使用的是  this.$props['fetchSuggestions'] 来进行填充数组的查询; 稀奇的处理方式。。。 这样,我们的问题是,我们需要继承 EL-autocomplete 组件,但是还必须能够覆盖其中的 Props 定义。。。 这个也不难。。。

​​​​​​​export default class EhayAutoComplete extends Vue.component('ElAutocomplete') { /** 实体类型名称 */ @Prop({required: true}) entityTypeName: string; /** 查询实体时可能还需要的父实体Id */ @Prop({ default: ''}) parentEntityId: string; @Prop({ default: () => (key: string, cb: FetchSuggestionsCallback) => { let vthis = this as any; vthis.remoteQuery(key, cb); } }) fetchSuggestions: FetchSuggestions; 。。。。。。。。。。。 定义和 entityTypeName 有关系的从服务器查询实体的方法 remoteQuery。。。。。 }

上面的代码默认会报编译错误,因为 typescript  如果启用 strict 模式,noImplicitThis 为 true, tsconfig.json 里面的 noImplicitThis 编译选项需要改为 false, 才能够使 this 可以为 any 类型; 看上去貌似没有问题; 但熟悉 TypeScript 的同学都知道,这儿有一个隐藏的 this 问题;http://blog.darkthread.net/post-2016-04-23-typescript-this-pitfall-in-debug.aspx  这儿有详细说明, TypeScript 太智能了,它自动把  this 移到了外面,这个时候的 this 将指向  {     default: () =>  (key: string, cb: FetchSuggestionsCallback) => {         let vthis = this as any; // as any; //this as AbstractEntityQueryComponent;         vthis.remoteQuery(key, cb);       }   } 这个对象,而不是我们想要的  EhayAutoComplete  组件的实例; 所以,这个时候会报一个脚本错误,也就是  remoteQuery 未定义错误; 这儿的悲催问题是,在  {     default: () =>  (key: string, cb: FetchSuggestionsCallback) => {         let vthis = this as any;          vthis.remoteQuery(key, cb);       }   }    这个对象内,我们没有办法访问直接或者间接访问 EhayAutoComplete 的当前实例, 悲催的是,调试一下,设个断点, this 明明指向了 EhayAutoComplete 的当前实例, 但是 typescript 编译器产生的代码就变成了 _this 变量; 还好,typescript 还是留了一道小口子, 我们改成  let vthis = eval('this');    就可以访问到调用这个函数的 this 对象,也就是  EhayAutoComplete 的当前实例,  

​​​​​​​export default class EhayAutoComplete extends Vue.component('ElAutocomplete') { /** 实体类型名称 */ @Prop({required: true}) entityTypeName: string; /** 查询实体时可能还需要的父实体Id */ @Prop({ default: ''}) parentEntityId: string; @Prop({ default: () => (key: string, cb: FetchSuggestionsCallback) => { // tslint:disable-next-line:no-eval let vthis = eval('this'); vthis.remoteQuery(key, cb); } }) fetchSuggestions: FetchSuggestions; 。。。。。。。。。。。 定义和 entityTypeName 有关系的从服务器查询实体的方法 remoteQuery。。。。。 }

很好,组件基本完成,但是这样就够了吗??当然不够啦; 实体查询的代码,很明显,不但可以用于 autocomplete 还可以用于其他的地方,例如 select 之类的组件,所以,我们需要把实体查询的代码从 EhayAutoComplete 中抽离出来,这样我们的任务是, 我们的组件需要继承 el-autocomplete 以及 我们自己的 entity-query 两个类 Vue实现这个也不难,因为它有 mixins 方法,可以混合2个类的属性、方法; 于是把实体查询的代码抽离出来, 这里面 WebApi, EhayModel 之类的都是根据服务器后台接口自动产生的 typescript 代码;

import { Component, Prop, Vue, Watch } from "vue-property-decorator"; import { WebApi } from "../../api/auto_actions"; import * as EhayModel from "../../api/auto_interface"; import { ArrayUtil } from "../../utils/BrowserExtension"; import { FetchSuggestions, FetchSuggestionsCallback } from "element-ui/types/autocomplete"; /** * 定义实体查询的组件, * 包含和实体查询有关的方法; **/ @Component export default class AbstractEntityQueryComponent extends Vue { /** 实体类型名称 */ @Prop({required: true}) entityTypeName: string; /** 查询实体时可能还需要的父实体Id */ @Prop({ default: ''}) parentEntityId: string; @Prop({ default: () => (key: string, cb: FetchSuggestionsCallback) => { // tslint:disable-next-line:no-eval let vthis = eval("this") as any; // as any; //this as AbstractEntityQueryComponent; vthis.remoteQuery(key, cb); }, }) fetchSuggestions: FetchSuggestions; /** * 查询函数 * @param key 实体名称关键字 * @param callback 回调函数,对于 autocomplete 组件有用; */ private queryFunc: (key: string, callback?: any) => Promise = (key: string, callback?: any) => new Promise >((resolve) => []) ; /** * 重新计算查询函数; */ recalateQueryFunc() { this.queryFunc = (key: string) => new Promise((resolve, reject) => { WebApi.EntityQuery.getEntityListByKey(this.entityTypeName, key, this.parentEntityId) .then((data) => resolve(data.data)); }); } /** * onMounted 消息函数 * 给 entityTypeName、parentEntityId 2个属性设置观察函数; */ mounted() { this.$watch("entityTypeName", () => this.recalateQueryFunc(), { immediate: true }); this.$watch("parentEntityId", () => this.recalateQueryFunc(), { immediate: true }); } /** 是否在加载数据 */ loading: boolean = false; /** 可用的实体数组 */ entityArray: EhayModel.LabelAndValue[] = []; /** 远程查询 * @param key 实体名称关键字 * @param callback 回调函数,对于 autocomplete 组件有用; */ remoteQuery(key: string, callback?: any) { this.loading = true; setTimeout(() => { this.queryFunc(key).then((data) => { this.loading = false; ArrayUtil.clearAndCopy(this.entityArray, data); if (callback) { callback(data); } }); }, 50); } }

再修改一下组件定义,混合 elementUI.Autocomplete 和  AbstractEntityQueryComponent 两个类定义, 终于,大功告成。。

import AbstractEntityQueryComponent from '../AbstractEntityQueryComponent'; import { Component, Prop, Watch, Mixins } from "vue-property-decorator"; import Vue, { PropOptions } from 'vue'; import elementUI from "element-ui"; /** * 自动完成组件 * 使用方法,继承自 elementUI.Autocomplete, * */ @Component({}) export default class EhayAutoComplete extends Mixins(elementUI.Autocomplete, AbstractEntityQueryComponent) { }

 

 

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有