vue 扩展现有组件的操作

时间:2022-12-03 18:47:53

1. 使用vue.mixin全局混入

混入 (mixins) 是一种分发 vue 组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。mixins 选项接受一个混合对象的数组。

混入的主要用途

1、在你已经写好了构造器后,需要增加方法或者临时的活动时使用的方法,这时用混入会减少源代码的污染。

2、很多地方都会用到的公用方法,用混入的方法可以减少代码量,实现代码重用。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<h1>mixins</h1>
  <hr>
  <div id="app">
    <p>num:{{ num }}</p>
    <p>
      <button @click="add">增加数量<tton>
    </p>
  </div>
  <script type="text/javascript">
    var addlog = { //额外临时加入时,用于显示日志
      updated: function () {
        console.log("数据发生变化,变化成" + this.num + ".");
      }
    }
    vue.mixin({// 全局注册一个混入,影响注册之后所有创建的每个 vue 实例
      updated: function () {
        console.log("我是全局的混入")
      }
    })
    var app = new vue({
      el: '#app',
      data: {
        num: 1
      },
      methods: {
        add: function () {
          this.num++;
        }
      },
      updated() {
        console.log("我是原生的update")
      },
      mixins: [addlog]//混入
    })

输出的结果

vue 扩展现有组件的操作

mixins的调用顺序

从执行的先后顺序来说,混入对象的钩子将在组件自身钩子之前调用,如果遇到全局混入(vue.mixin),全局混入的执行顺序要前于混入和组件里的方法。

2.使用extends 扩展

extends选项允许声明扩展另一个组件,而无需使用 vue.extend。

通过外部增加对象的形式,对构造器进行扩展。它和混入mixins非常的类似。只不过接收的参数是简单的选项对象或构造函数,所以extends只能单次扩展一个组件。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var bbb = {
      updated() {
        console.log("我是被扩展出来的");
      },
      methods: {
        add: function () { //跟原生的方法冲突,取原生的方法,这点跟混入一样
          console.log('我是被扩展出来的add方法!');
          this.num++;
        }
      }
    };
    var app = new vue({
      el: '#app',
      data: {
        num: 1
      },
      methods: {
        add: function () {
          console.log('我是原生add方法');
          this.num++;
        }
      },
      updated() {
        console.log("我是扩展出来的");
      },
      extends: bbb// 接收对象和函数
    })

vue 扩展现有组件的操作

结果

执行的先后顺序和mixins一样,另外扩展的方法与原生的冲突时,扩展的方法不生效,这点跟混入一样

ps extends和mixins优先级比较

vue 扩展现有组件的操作

相对于mixins,extends触发的优先级更高

3.加slot扩展

.默认插槽和匿名插槽

slot用来获取组件中的原内容,此方式用于父组件向子组件传递“标签数据”。有的时候为插槽提供默认的内容是很有用的,例如,一个 组件可能希望这个按钮的默认内容是“如果没有原内容,则显示该内容”,但是同时允许用户覆写为别的内容。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<body>
 <div id="itany">
  <my-hello>180812</my-hello>
 </div>
<template id="hello">
 <div>
  <h3>welcome to xiamen</h3>
  <slot>如果没有原内容,则显示该内容</slot>// 默认插槽
 </div>
</template>
<script>
 var vm=new vue({
   el:'#itany',
   components:{
   'my-hello':{
   template:'#hello'
  }
   }
 }); 
</script>

具名插槽

有些时候我们需要多个插槽, 元素有一个特殊的特性:name。这个特性可以用来定义额外的插槽:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<div id="itany">
  <my-hello>
   <ul slot="s1">
  <li>aaa</li>
  <li>bbb</li>
  <li>ccc</li>
   </ul>
   <ol slot="s2">
  <li>111</li>
  <li>222</li>
  <li>333</li>
   </ol>
  </my-hello>
</div>
<template id="hello">
  <div>
   <slot name="s2"></slot>
   <h3>welcome to xiamen</h3>
   <slot name="s1"></slot>
  </div>
</template>
<script>
 var vm=new vue({
  el:'#itany',
  components:{
   'my-hello':{
     template:'#hello'
    }
  }
 });

补充知识:vue extends拓展任意组件功能(el-select实例)-两种写法

用到elementui的select组件,要求能够多选并且重复选择。如果直接使用的话,首先el-tag会报错,因为循环中key值重复;其次,他的移除是通过indexof搜索移除的tag的值,且在remove-tag事件中未抛出被移除tag的索引,这样的后果是存在多个相同值的tag时,只会移除第一个相同值的tag

思路

在el-tag的循环中,给close事件增加一个参数index,然后重写deletetag方法,直接通过index删除该tag

?
1
2
3
4
5
6
vue: @close="deletetag($event, item)"
jsx: on-close={e => this.deletetag(e, this.selected[0])}
deletetag(event, tag, tagindex){
 const value = this.value.slice();
 value.splice(tagindex, 1);// 核心代码,其他代码省略
}

写法一、vue template(推荐)

非常简单,改动特别少,可以使用vue的所有用法,只需要复制el-select的template

新建一个vue文件

复制el-select的template模板内容过来

导入el-select,继承

覆盖methods中的deletetag

结果

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
 <div
  class="el-select"
  :class="[selectsize ? 'el-select--' + selectsize : '']"
  @click.stop="togglemenu"
  v-clickoutside="handleclose">
  我是示例代码,此处为自定义模板内容
 </div>
</template>
 
<script>
 import { select} from 'element-ui';
 export default {
  extends: select,//继承
  name: 'my-el-select',
  methods: {
   deletetag(event, tag, tagindex) {
// 重写该方法
  },
 },
 };
</script>

写法二、jsx(比较麻烦)

需要手动将vue template转为jsx写法,无法使用事件修饰符,部分指令等等,改动比较大

1、导入继承

?
1
2
3
4
5
import {select} from 'element-ui';
 
const myelselect = {
 extends: select
}

2、 重写render

vue template最终编译之后也是生成render函数,这里覆盖render函数,

生成自定义内容。此处的意义只是为了记录以便于方便我用render函数时的jsx写法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
render()
{
  const tagcontent = () => {
   if (this.collapsetags && this.selected.length) {
    const tag0 = (
     <el-tag
      closable={!this.selectdisabled}
      size={this.collapsetagsize}
      hit={this.selected[0].hitstate}
      type='info'
      on-close={e => this.deletetag(e, this.selected[0])}
      disable-transitions={true}>
      <span class='el-select__tags-text'>{this.selected[0].currentlabel}</span>
     </el-tag>
    );
    const tag1 = (
     <el-tag
      closable={false}
      size={this.collapsetagsize}
      type='info'
      disable-transitions={true}>
      <span class='el-select__tags-text'>+ {this.selected.length - 1}</span>
     </el-tag>
    );
 
    if (this.selected.length > 1) {
     return (
      <span>
       {tag0}
       {tag1}
      </span>
     );
    }
    return (
     <span>
      {tag0}
     </span>
    );
   }
  };
  const emptytext = () => {
   if (this.emptytext && (!this.allowcreate || this.loading || (this.allowcreate && this.options.length === 0))) {
    return (
     <p class='el-select-dropdown__empty'>{this.emptytext}</p>
    );
   }
  };
  const selectoption = () => {
   return (
    <transition
     name='el-zoom-in-top'
     on-before-enter={this.handlemenuenter}
     on-after-leave={this.dodestroy}>
     <el-select-menu
      ref='popper'
      append-to-body={this.popperappendtobody}
      v-show={this.visible && this.emptytext !== false}>
      <el-scrollbar
       tag='ul'
       wrap-class='el-select-dropdown__wrap'
       view-class='el-select-dropdown__list'
       ref='scrollbar'
       class={{'is-empty': !this.allowcreate && this.query && this.filteredoptionscount === 0}}
       v-show={this.options.length > 0 && !this.loading}>
       {this.shownewoption ? (
        <el-option
         value={this.query}
         created={true}>
        </el-option>
       ) : null}
       {
        this.$slots.default
       }
      </el-scrollbar>
      {emptytext()}
     </el-select-menu>
    </transition>
   );
  };
  return (
   <div
    class={['el-select', this.selectsize ? 'el-select--' + this.selectsize : '']}
    on-click={this.togglemenu} v-clickoutside={this.handleclose}>
    <div
     class='el-select__tags'
     ref='tags'
     style={{'max-width': this.inputwidth - 32 + 'px'}}>
     {tagcontent()}
     <transition-group onafterleave={this.resetinputheight}>
      {this.selected.map((item, index) => {
       return (
        <el-tag
         key={index}
         closable={!this.selectdisabled}
         size={this.collapsetagsize}
         hit={item.hitstate}
         type='info'
         on-close={(e) => this.deletetag(e, item, index)}
         disable-transitions={false}>
         <span class='el-select__tags-text'>{item.currentlabel}</span>
        </el-tag>
       );
      })}
     </transition-group>
    </div>
    <el-input
     ref='reference'
     value={this.selectedlabel}
     type='text'
     placeholder={this.currentplaceholder}
     name={this.name}
     id={this.id}
     auto-complete={this.autocomplete}
     size={this.selectsize}
     disabled={this.selectdisabled}
     readonly={this.readonly}
     validate-event={false}
     class={{'is-focus': this.visible}}
     on-focus={this.handlefocus}
     on-blur={this.handleblur}
     on-keyup_native={this.debouncedoninputchange}
     on-paste_native={this.debouncedoninputchange}
     on-mouseenter_native={(this.inputhovering = true)}
     on-mouseleave_native={(this.inputhovering = false)}
    >
     <i slot='suffix'
       class={['el-select__caret', 'el-input__icon', 'el-icon-' + this.iconclass]}
       on-click={() => this.handleiconclick}/>
    </el-input>
    {selectoption()}
   </div>
  );
 }

3、 重写method里的deletetag方法

4、结果

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import {select} from 'element-ui';
 
const myelselect = {
 extends: select,
 methods: {
  deletetag(event, tag, tagindex) {
   // *****略
  },
 },
 render() {
  return (
   <div>例子</div>
  );
 }
};
export default myelselect;

以上这篇vue 扩展现有组件的操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持服务器之家。

原文链接:https://www.cnblogs.com/whkl-m/p/12888502.html