如何抽象一个 Vue 公共组件
之前一直想写一篇关于抽象 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);
}
上面的例子都是些核心代码的片段,并且其他辅助函数并未列出,想查看完整的代码请看源码。
感谢你的浏览,希望能有所帮助。
如何抽象一个 Vue 公共组件的更多相关文章
- .vue公共组件裁减导航
场景: 有一个公共头部和底部,vue搭建的框架,在app.vue里写的公共方法,首页是个登录页面,不需要公共部分,在这基础上进行公共部分的显示隐藏. 即注册页.登录页.404页面都不要导航 代码: ( ...
- 从0到1发布一个Vue Collapse组件
需求背景 最近在项目中遇到了一个类似Collapse的交互需求,因此到github上找了一圈关于Vue Collapse的相关轮子,但是多少都有些问题.有的是实现问题,例如vue2-collapse, ...
- 从头开始开发一个vue幻灯片组件
首先新建项目vue init webpack projectName 安装依赖包npm i这些就不说了 接下来就是构建我们的swiper组件 因为我写的代码不规范, 通不过eslint的检测, 会频繁 ...
- 使用vue配合组件--转载
1.由饿了么 UED (知乎专栏)设计的桌面端组件库已经开源,文档:Element,仓库: GitHub - ElemeFE/element: Desktop UI elements for Vue. ...
- 使用Vue脚手架(vue-cli)从零搭建一个vue项目(包含vue项目结构展示)
注:在搭建项目之前,请先安装一些全局的工具(如:node,vue-cli等) node安装:去node官网(https://nodejs.org/en/)下载并安装node即可,安装node以后就可以 ...
- 一个vue管理系统的初步搭建总结
ps:目前这个项目只是有一个大致的框架,并没有做完 前期准备工作 前端构建工具:Visual Studio Code后端构建工具:IDEA数据库和服务器构建工具:WampServer (使用的是2.4 ...
- java+springBoot+Thymeleaf+vue分页组件的定义
导读 本篇着重介绍java开发环境下,如何写一个vue分页组件,使用到的技术点有java.springBoot.Thymeleaf等: 分页效果图 名称为vuepagerbasic的分页组件,只包含上 ...
- 手把手教你如何构建Vue前端组件库
在前端开发中可能会遇到将相同的功能模板集合成一个组件,供他人调用,这样可以减少重复造轮子,也可以节约人力.财力,更能够提高代码的可维护度:下面将通过详细的步骤教你如何构建一个Vue前端组件. 1.在本 ...
- 一个技术汪的开源梦 —— 基于 .Net Core 的公共组件之目录结构
一个技术汪的开源梦 —— 目录 这篇文章是开源公共组件的开篇那就先说说项目的 Github 目录结构和 .Net Core 的项目结构. 1. GitHub 目录结构和相关文件 - src 源码项目目 ...
随机推荐
- MongoDB学习之路(三)
数据库 一个MongoDB可以建立多个数据库. MongoDB的默认数据库为"db",该数据库存储在data目录中. MongoDB的单个实例可以容纳多个独立的数据库,每一个都有自 ...
- 团队作业1 团队展示&选题
团队展示&选题 Coding项目地址:https://git.coding.net/wjunren/running.git 一.团队展示 1.队名:Runing Guys 2.队员: 组长:骆 ...
- 201521123083《Java程序设计》第5周学习总结
1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容. 2. 书面作业 1. 代码阅读:Child压缩包内源代码 1.1 com.par ...
- 201521123065 《Java程序设计》第5周学习总结
1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容. 1.ArrayList只能存放对象: 2.对象包装类之间使用equals进行比较 ...
- 201521123099《java程序设计》第五周学习总结
本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 2. 书面作业 代码阅读:Child压缩包内源代码 1.1 com.parent包中Child.java文件能否编译通过?哪句会出现 ...
- 201521123054 《Java程序设计》第5周学习总结
1. 本周学习总结 2. 书面作业 作业参考文件下载 代码阅读:Child压缩包内源代码 1.1 com.parent包中Child.java文件能否编译通过?哪句会出现错误?试改正该错误.并分析输出 ...
- 201521123122 《java程序设计》 第四周学习总结
1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. 这个思维导图比较简单,详细内容点击此处 2. 书面作业 注释的应用 使用类的注释与方法的注释 ...
- 201521123104 《JAVA程序设计》第二周学习总结
1. 本周学习总结 认识了JAVA编程中一些类型与变量,了解了一些基本运算符的使用 变量在命名时,不可以使用数字或一些特殊字符作为开头 不可以声明局部变量后未指定任何值给它之前就使用变量 在程序中写下 ...
- 201521123029《Java程序设计》第14周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 答:1.数据库定义:为了实现一定目的按某种规则组织起来的"数据"的"集合" ...
- 201521123066 《Java程序设计》第十四周学习总结
1. 本周学习总结 2. 书面作业 1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自己的学号.姓名) 在自己建立的数据库上执行常见SQL语句(截图) - ...