视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
如何抽象一个Vue公共组件
2020-11-27 22:27:39 责编:小采
文档

之前一直想写一篇关于抽象 Vue 组件的随笔,无奈一直没想到好的例子。恰巧最近为公司项目做了一个数字键盘的组件,于是就以这个为例聊聊如何抽象 Vue 的组件。

先上 Demo 与 源码。(demo最好在浏览器里以手机模式浏览)

在讲具体实现前,我想先分享下自己认为的理想的公用组件是什么样的:

1. 黑盒性,即除了你自己以外,其他的开发者在快速阅读使用文档之后可以立刻上手,而不用关心你的内部实现;

2. 性,即做好解耦,不与父组件有过多关联;

3 自定义性,适当地暴露一些输入接口或者方法给外部用于自定义,同时也要设置好这些属性在外部未输入时的默认值。 

下面我们先以黑盒的方式看看 demo 中数字键盘组件是如何调用的(已省略非关键部分代码)。

App.vue

<template>
......
 <keyboard @submit-event='handleSubmit' @change-event='handleChange'></keyboard>
</template>
<script>
import keyboard from 'Keyboard.vue';
export default {
 data() {
 return {
 value: ''
 };
 },
 methods: {
 handleChange(value, currentValue) {
 console.log(value, currentValue);
 this.value = value;
 },
 handleSubmit() {
 console.log('submit: ' + this.value);
 }
 }
}
</script>

如上,最基本的调用就完成了。效果如下:

接着,点击 1-6 与“确定”。如果如下:

可以看到数字键盘已经如我们预期工作了,接下来分析下该数字键盘组件所有的输入项。

@change-event:该事件为自定义事件,父组件通过 v-on 注册监听,子组件内部通过 $emit 进行触发(更多自定义事件相关内容请参考:Vue官方教程)。

每一次点击数字按键以及退格键均会触发该事件,其传递两个参数:value,累积点击的字符组合;currentValue,当前点击的字符。父组件通过 handleChange 方法接收该事件的回调内容。

@submit-event:当点击“确定”键即会触发该事件,其不传递参数,只是告诉父组件“我的确定按钮被点击了,你要做什么操作自己看着办吧,之前点击的数字已经通过 change-event 传给你了”。父组件通过 handleSubmit 方法接收回调。

如果只写这两个方法未免也太没诚意了,我还根据一些场景编写了以下几个自定义属性。

max:最大输入长度,超过的部分将不会触发 change-event 事件,默认无。

<keyboard max='6'></keyboard>

sp-key:自定义的特殊字符,如身份证输入时的“X”,会添加到左下角空白格,默认无。

<keyboard sp-key='X'></keyboard>

random:是否打乱数字顺序,一些有关银行账户或密码的输入经常会见到这种场景,默认 false。

<keyboard random='true'></keyboard>

从上面的几个自定义属性与事件,我们大概知道了父组件是如何向子组件传值以及监听子组件的变化,但父组件该如何直接调用子组件内部的函数呢?我们看下面这个场景。

数字键盘上的键盘图标,点击之后会将数字键盘收起隐藏。组件内部拥有一个方法 keyboardToggle(true|false) 来控制键盘的弹起和收回,那么如果在组件外部也想调用这个方法呢?比如当父组件中的 input 获取到焦点时。

可以通过 Vue 中的 ref 属性来获取到键盘的组件引用,从而调用其内部的方法,如下:

$refs.[refName].keyboardToggle(true|false)

<template>
 <input type='text' @focus='handleShowKeyboard($event)' />
 <keyboard ref='kbref'></keyboard>
</template>
<script>
import keyboard from 'Keyboard';
export default {
 //...
 methods: {
 handleShowKeyboard(e) {
 e && e.preventDefault();
 this.$refs.kbref.keyboardToggle(true);
 }
 }
}
</script>

以上面这种形式便可以在父组件上下文中调用子组件内的方法。

$refs.[refName].handleInit()

数字键盘组件内部的初始化方法,用于重新渲染组件。若 random 属性为 true,则数字键会刷新随机排列。

$refs.[refName].handleClear()

清除之前输入的字符组合,并触发 change-event 且返回空字符串。

上面分享了这个组件所有对外的属性与事件,可以发现我们并未看过该组件内部的一行代码,但已经可以完整的使用它了,下面来聊聊内部实现。

首先来看看布局,我将键盘分为左右两部分,右边部分不用多说,左边的部分是将一个键位数组通过 v-for 循环生成。

那么是如何让 0 和 9 之间空出一格呢,下面看下初始化键盘组件的方法。

keyboard.vue

handleInit()

<template>
 <div>
 <div class='kb-left'>
 <div class='kb-item' v-for='item in keyArr'>{{item}}</div>
 <div class='kb-item kb-toggle'></div> //键盘图标
 </div>
 <div class='kb-right'>
 //... 
 </div>
 </div>
</template>
<script>
export default {
 data() {
 return {
 baseArr: []
 }
 },
 computed: {
 keyArr() {
 this.handleInit();
 return this.baseArr;
 }
 },
 methods: {
 handleInit() {
 this.baseArr = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];
 this.baseArr.splice(this.baseArr.length - 1, 0, '');
 }
 }
}
</script>

即在键位数组倒数第二位插入一个空字符,然后循环生成按键。下面看下自定义参数是如何生效的。

sp-key

<script>
export default {
 props: ['spKey'],
 data() {
 return {
 baseArr: []
 }
 },
 //....
 methods: {
 handleInit() {
 let spKey = this.spKey;
 this.baseArr = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];

       this.baseArr.splice(this.baseArr.length - 1, 0, spKey);
 }
 }
}
</script>

在组件内部通过 props 属性接收父组件传递的 spKey,之后就可在组件内的属性和方法中通过 this.spKey 进行访问。首先判断 spKey 值是否有效,并代替空字符插入键位数组倒数第二项。

random

<script>
export default {
 props: ['spKey', 'random'],
 data() {
 return {
 baseArr: []
 }
 },
 //....
 methods: {
 handleInit() {
 let spKey = this.spKey;
 this.baseArr = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];

   if (this.random && this.random != 'false') {
   this.baseArr.sort(function() {
    return Math.random() - Math.random();
     });
 }

 this.baseArr.splice(this.baseArr.length - 1, 0, spKey);
 }
 }
}
</script>

将键位打乱顺序其实也很简单,只要通过数组的 sort 方法。sort 方法可以接收一个函数作为参数,若函数返回正数则交换前后两项的位置,若函数返回负数则不作交换。所以将两个随机数相减的结果返回,即可将键位数组随机排序。

下面看看在组件内部是如何触发 change-event 的。

handleInput()

<template>
 <div>
 <div class='kb-left'>
 <div @click='handleInput(item)' class='kb-item' v-for='item in keyArr'>{{item}}</div>
 <div class='kb-item kb-toggle'></div> //键盘图标
 </div>
 //...
 </div>
</template>
<script>
export default {
 data() {
 return {
 inputStr: ''
 }
 },
 //...
 methods: {
 handleInput(value) {
 this.inputStr += value;
 this.$emit('change-event', this.inputStr, value);
 }
 }
}
</script>

增加了 max 属性后修改方法如下:

handleInput(value) {
 let max = Number(this.max);
 if (!isNaN(max) && this.inputStr.length+1 > max) {
 return;
 }

 this.inputStr += value;
 this.$emit('change-event', this.inputStr, value);
}

最后看看退格删除是如何实现的。

handleDelete()

handleDelete() {
 let str = this.inputStr;
   if (!str.length) return;

 this.inputStr = str.substring(0, str.length - 1);
 this.$emit('change-event', this.inputStr);
} 

上面的例子都是些核心代码的片段,并且其他辅助函数并未列出,想查看完整的代码请看源码。

 

下载本文
显示全文
专题