作vue开发,基本的操做会了以后是否是特想撸一撸vue的插件,让本身的代码可(骚)复(骚)用(的)。别急,今天和你一块儿手摸手,哦呸,是手把手,一块儿撸一管,哦再呸,是封装一个基于vue的支付宝密码弹窗插件。而后还会介绍如何书写插件的markdown说明文档、发布到github、在github设置项目的运行地址、发布npm包。javascript
简单看下效果图,主要实现密码输入完成自动触发输入完成的回调函数,自定义标题、自定义高亮颜色、自定义加载动画,密码错误弹窗,可清空密码等基本操做。html
开发vue插件以前呢,先说下封装插件的目的是什么?封装插件的目的就是为了代码的可复用,既然是为了可复用,那么只要能实现可复用的操做,封装方式就能够多样化。这和jq的$.fn.myPlugin =
function(){}
有些区别。先来看下官方文档对于vue插件的说明:vue
插件一般会为 Vue 添加全局功能。插件的范围没有限制——通常有下面几种:java
添加全局方法或者属性,如: vue-custom-elementnode
添加全局资源:指令/过滤器/过渡等,如 vue-touchgit
经过全局 mixin 方法添加一些组件选项,如: vue-routergithub
添加 Vue 实例方法,经过把它们添加到 Vue.prototype 上实现。算法
一个库,提供本身的 API,同时提供上面提到的一个或多个功能,如 vue-routervue-router
MyPlugin.install = function (Vue, options) {
// 第一种方法. 添加全局方法或属性
Vue.myGlobalMethod = function () {
// 逻辑...
}
// 第二种方法. 添加全局资源
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
...
})
// 第三种方法. 注入组件
Vue.mixin({
created: function () {
// 逻辑...
}
...
})
// 第五种方法. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
// 逻辑...
}
// 第六种方法,注册组件
Vue.component(组件名, 组件)
}复制代码 |
这些形式均可以做为咱们的插件开发。插件的本质不就是为了代码的复用吗?根据你的封装需求选择不一样的形式,例如toast提示
能够选择Vue.prototype
,输入框自动获取焦点能够选择Vue.directive
指令,自定义组件能够选择Vue.component
的形式。vue-cli
由于咱们的支付密码框能够看做为一个组件,因此才有Vue.compnent
的形式。下面开始搭建基本的一个壳:
为了你们能看到项目运行起来,先初始化一个vue的项目。而后在src新建一个lib文件夹用于存放各个插件,在lib下新建一个vpay文件夹存放咱们的插件。
在vpay文件夹下,新建一个index.js和一个lib文件夹,而咱们真正的插件是写在新建的这个lib文件夹中的。这里之因此这么建立是由于后面还有在这里初始化npm包,发布到npm上:
暂时先无论npm包的内容,这个最后再说。先看插件这一块,根据上述内容建立对应文件。
而后咱们开始搭建插件开发的基本壳子,在插件的index.js中:
// pay.vue写咱们的组件
import vpay from './pay'
// 定义咱们的插件
const myPlugin = {
// 该插件有一个install方法
// 方法的第一个参数是传入的Vue,第二个参数能够插件的自定义参数
install (Vue, options) {
// 将其注册为vue的组件,'vpay'是组件名,keyboard是咱们开发的组件
Vue.component('vpay', vpay)
}
}
// 最后将插件导出,并在main.js中经过Vue.use()便可使用插件
export default myPlugin复制代码
待插件开发好后,就能够在mian.js中这样使用:
// 引入插件
import vpay from './lib/vpay'
// 使用插件
Vue.use(vpay);复制代码
然后在咱们的页面中就能够直接像使用组件的方式使用插件:
<!--支付密码弹窗-->
<vpay
ref="pays"
v-model="show"
@close="close"
@forget="forget"
@input-end="inputEnd"
></vpay>复制代码
是否是跟咱们平时使用插件的形式一模一样?基本思路和插件的壳子有了,下面就开始开发pay.vue,这插件的具体内容了。这部分的开发,说的简单些,其实就和平时写个父子组件般,由于咱们最后想向上面那种自定义组件的形式使用。
pay.vue文件:
<template>
<div class="zfb-pay" v-if="show">
<div class="content">
<!--标题栏-->
<header class="pay-title">
<div class="ico-back" @click="cancel"></div>
<h3>{{title}}</h3>
</header>
<!--密码框-->
<div class="pass-box">
<ul class="pass-area">
<li class="pass-item"
:class="{on: password.length > index}"
v-for="(item, index) in digit"
:key="index"></li>
</ul>
</div>
<!--忘记密码-->
<div class="forget-pass">
<div class="forget-pass-btn" @click="forget">忘记密码</div>
</div>
<!--键盘区-->
<ul class="keyboard">
<li @click="onKeyboard(1)">
<p class="num"><strong>1</strong></p>
<p class="character"></p>
</li>
<li @click="onKeyboard(2)">
<p class="num"><strong>2</strong></p>
<p class="character">ABC</p>
</li>
<li @click="onKeyboard(3)">
<p class="num"><strong>3</strong></p>
<p class="character">DEF</p>
</li>
<li @click="onKeyboard(4)">
<p class="num"><strong>4</strong></p>
<p class="character">GHI</p>
</li>
<li @click="onKeyboard(5)">
<p class="num"><strong>5</strong></p>
<p class="character">JKL</p>
</li>
<li @click="onKeyboard(6)">
<p class="num"><strong>1</strong></p>
<p class="character">MNO</p>
</li>
<li @click="onKeyboard(7)">
<p class="num"><strong>7</strong></p>
<p class="character">PQRS</p>
</li>
<li @click="onKeyboard(8)">
<p class="num"><strong>8</strong></p>
<p class="character">TUV</p>
</li>
<li @click="onKeyboard(9)">
<p class="num"><strong>9</strong></p>
<p class="character">WXYZ</p>
</li>
<li class="none"></li>
<li class="zero" @click="onKeyboard(0)"><strong>0</strong></li>
<li class="delete" @click="deleteKey"></li>
</ul>
<!--加载中状态-->
<div class="loading-wrap" v-if="payStatus !== 0">
<div class="loading">
<!--加载图标-->
<img src="@/assets/loading.png" class="loading-ico" alt="" v-if="payStatus === 1">
<img src="@/assets/success.png" class="success-ico" alt="" v-if="payStatus === 2">
<!--加载文字-->
<p v-if="payStatus === 1">{{loadingText}}</p>
<p v-if="payStatus === 2">{{finishedText}}</p>
</div>
</div>
<!--支付失败提示框-->
<div class="pay-fail" v-if="isShowFail">
<div class="pay-fail-lay">
<h3 class="title">{{failTip}}</h3>
<div class="btns">
<div @click="reInput">从新输入</div>
<div @click="forget">忘记密码</div>
</div>
</div>
</div>
</div>
</div>
</template>复制代码
布局没什么可说的,大致说下分为哪些板块吧:
this.$refs.pays.$success(true).then(res => {})
告知插件成功结果并在成功提示停留结束后触发then
里面的操做。this.$refs.pays.$fail(false)
调用插件的方法告知插件支付结果为密码错误,插件会弹出错误的提示框。以上代码因为是已经写好了插件而后直接拷贝的,因此上面插入了变量和方法等。布局完成后,下面开始介绍实现:
export default {
data() {
return {
password: '', // 支付密码
payStatus: 0, // 支付状态,0无状态, 1正在支付,2支付成功
failTip: '支付密码错误', //
isShowFail: false
}
},
}复制代码
onKeyboard
方法:
// 点击密码操做
onKeyboard(key) {
// 只截取前六位密码
this.password = (this.password + key).slice(0, 6);
},复制代码
同时经过在watch钩子里面,监听password,在password长度达到设置的长度后(例如输入完6位后)经过$emit('input-end')通知父组件的自定义事件,能够理解为输入完成后的回调函数:
watch: {
// 监听支付密码,支付密码输入完成后触发input-end回调
// 通知父组件的同时会把输入完成的密码做为参数告知父组件,即input-end函数的参数能拿到密码
password (n, o) {
if (n.length === this.digit) {
this.payStatus = 1;
this.$emit('input-end', this.password)
}
}
},复制代码
this.digit是经过prop传递的值,用于设置支付密码框的位数,默认六位:
// 支付密码框位数
props: {
// 支付密码框位数 digit: {
type: Number,
default: 6
},
}复制代码
2. 完成了密码的输入,开始作密码的回删,即每次回删,删除password的最后一位数,有点相似算法里面的堆(后进先出),有点扯远了,这里用不到:
// methods中的方法,密码回删
deleteKey(){
// 密码已经为空时,不回删
if (this.password.length === 0) return;
// 回删一位密码
this.password = this.password.slice(0, this.password.length - 1);
},复制代码
密码长度为0时再也不回删,不为0时每次截取0到长度减一的位置为新密码,即去掉了最后一位密码。
3.说到了密码输入达到指定的长度后会自动触发父组件的自定义事件,这个是写在父组件的,等下演示调用插件的时候会介绍。那么对应的也会有取消支付弹窗的操做,即点击弹窗左上角的返回按钮取消支付,关闭弹窗:
// 取消支付
cancel () {
// 支付过程当中,不容许取消支付
if (this.payStatus === 1) return;
// 清空密码
this.password = '';
// 恢复支付状态
this.payStatus = 0;
// 关闭组件,并触发父子组件数据同步
this.$emit('change', false);
// 触发父组件close自定义事件
this.$emit('close');
},复制代码
取消的时候首先要判断当前的支付状态,若是是正在支付的状态,则是不容许取消的。这里的在data中已经有过定义,主要payStatus有三个值:0无状态,1支付中,2支付成功。而后就是清空密码、初始化支付的状态、关闭弹窗、同时告知父组件使得父组件能够有一个close的自定义事件写回调函数。
因为支付弹窗的显示隐藏是由父组件传递的值控制的,因此这里在组件内改变了父组件传递的值后也要对应的更新父组件的值,即this.$emit('change', false)
:
如图咱们在使用插件的时候,想经过v-model的值来控制组件的显示隐藏,就像不少库同样使用v-model。因此这里展现如何操做能经过v-model实现:
仍是组件的prop属性,经过prop的show属性来控制组件的显示隐藏:
// 组件的显示隐藏
show: {
type: Boolean,
required: true,
default: false
},复制代码
而后在model的钩子中:
model: {
prop: 'show',
event: 'change'
},复制代码
最后是在html中经过该字段控制显示隐藏。v-model的内容这里很少作介绍,回头这里更新一个链接地址,在另外一篇里面有详细的介绍。
4. 下面就是当你密码输入完成后,自动触发父组件的input-end回调,你在input-end回调中发起了支付请求,这时后台会返回给你支付状态,例如支付成功或者密码错误。未设置支付能够在吊起支付弹窗之前就去判断或者提示。当你拿到支付的结果后,要经过调用插件的$success
方法或者$fail
方法告知插件你的支付结果。以下图咱们先看怎么使用的,而后再说怎么在插件中实现$success
方法和$fail
方法:
调用pay-keyboard
的时候,定义了ref="key"
,因而咱们就能够经过this.$refs.pay
指向当前插件实例。用法很简单,注释写的也比较清楚就不解释了。下面是插件的$success方法:
//支付成功
$success () {
return new Promise((resolve, reject) => {
// 支付成功当即显示成功状态
this.payStatus = 2;
// 待指定间隔后,隐藏整个支付弹窗,并resolve
setTimeout(() => {
this.cancel();
resolve();
}, this.duration);
})
},复制代码
该方法返回一个promise对象,promise会当即执行。返回promise
是为了调用时经过then
方法写回调,更优雅一些。在promise
内,进行了支付状态的更改,而后在设置的支付成功状态显示的时间结束后关闭弹出并resolve
。因此this.$refs.pay.$success().then()
方法中的操做会在等待成功提示结束、也就是弹窗隐藏掉以后执行。
// 支付失败
// 隐藏加载提示框,显示支付失败确认框
$fail (tip) {
tip && typeof tip === 'string' && (this.failTip = tip);
// 隐藏掉支付状态窗口
this.payStatus = 0;
// 显示密码错误弹窗
this.isShowFail = true;
},复制代码
$fail
方法就是插件在得知支付结果为密码错误的时候,显示出密码错误的弹窗。密码错误弹窗的提示是可自定义的。经过tip参数获取自定义的错误提示。
该错误提示弹窗,有两个按钮,有一个从新输入,一个忘记密码。
点击从新输入,确定是要隐藏该弹窗,而后状况已经输入的密码。由此,在methods中写一个reInput方法来实现该功能:
// 从新输入
// 清空以前输入的密码,隐藏支付失败的提示框
reInput () {
this.password = '';
this.isShowFail = false;
},复制代码
这样就能够从新输入支付密码了。
忘记密码,这里咱们能够将点击后操做的控制权交给父组件。因此,咱们写一个忘记密码的点击后的方法,这里的忘记忘记密码能够和上面密码区的忘记密码做为同一个操做。固然了你也根据本身的需求开发出区分开的操做。这里是做为同一个的操做:
// 忘记密码,触发父组件的forget自定义事件
forget () {
this.$emit('forget');
}复制代码
由此,即可以在父组件的@forget自定义事件中进行一个链接的跳转等操做。
固然了,咱们的prop接收的参数还有如下这些:
// 弹窗标题
title: {
type: String,
default: '请输入支付密码'
},
// 正在支付的文字提示
loadingText: {
type: String,
default: '正在支付'
},
// 支付成功的文字提示
finishedText: {
type: String,
default: '支付成功'
},
// 支付成功的提示显示时间
duration: {
type: Number,
default: 500
}复制代码
经过这些prop参数,咱们能够在初始化插件的时候进行配置。
插件开发完成后就是调用啦,在咱们的main.js中先引入咱们的插件,然后进行初始化插件:
// 引入插件,位置是你的插件位置
import payPassword from './lib/zfc-password'
// 初始化插件
Vue.use(payPassword)复制代码
而后在咱们的页面内这样使用:
<vpay
v-model="show"
ref="pays"
@close="close"
@forget="forget"
@input-end="inputEnd"
></vpay>
复制代码
到此咱们的插件基本就开发完成了。已经提交github,送上github项目地址和demo地址,自行查阅。
是否是很熟悉呢!没错,这就是markdown格式的说明文档。说到markdown,其实也很多内容的,可是咱们只说一些经常使用的写markdown文档的语法,这些已经足够咱们平时写文档用了。
Markdown 是一种轻量级标记语言,创始人为约翰·格鲁伯(John Gruber)。它容许人们“使用易读易写的纯文本格式编写文档,而后转换成有效的XHTML(或者HTML)文档”。这种语言吸取了不少在电子邮件中已有的纯文本标记的特性。
markdown使用途径好久,不少写做等都是使用的markdown文档,包括咱们的掘金写做也是能够切换markdown妥当的。下面就说说markdown的常见语法吧。在咱们vue建立的根目录,你会发现有一个reaame.md文件,这个文件就能够用来书写项目的说明文档。若是项目传到github上面,这个文档对应的就是如上图所示的使用文档说明。
markdown文档每种格式都有其余的书写方式,这里只介绍其中常见的一种或者说话是我的比较喜欢的形式:
# 一级标题
## 二级标题
### 三级标题
#### 四级标题
##### 五级标题
###### 六级标题复制代码
效果图:
* 1111
* 2222
* 3333
1. 1111
2. 2222
3. 33334. 4444复制代码
效果图:
| 事件名 | 说明 | 参数 |
| - | :- | :- | :-: |
| input-end | 密码输入完成后的回调函数 | - |
| close | 密码弹窗关闭后的回调函数 | - |
| forget | 点击忘记密码的回调函数 | - |复制代码
表格这里说一下,第一行对应表格的标题,第二行将表格标题和表格内容分开,而且在这里能够定义表格对齐方式,即| - | 默认左对齐,| :- |在左边添加:
表示这一列左对齐,右边加:
则这一列右对齐,两边都加:
则居中对齐。
```javascript
this.$refs.pays.$success(true).then(res => {
console.log('支付成功')
this.$router.push('/success')
})
```复制代码
即两个```
之间能够添加代码块。是esc按键下的那个键,不是单引号。
复制代码
经过
即可以引入图片。效果如上图演示的动态图。
[demo演示页面](https://chinaberg.github.io/vpay/dist/#/, '支付密码弹窗demo演示页面')复制代码
[]
设置连接名称,()
中第一个参数为连接地址,第二个参数为连接的title
属性值,第二个参数可选。效果如图:
markdown文档的书写语法暂且介绍这些最最经常使用的一些。更多的markdown语法,请查看相关文档。
插件开发好了,文档说明也写好了,下面就是上传github、设置github的项目展现页,而后分享给更多的人。
git init
初始化git项目git checkout -b master
新建一个master分支git
忽略监控咱们打包后的dist文件夹,因此这里我要取消忽略,让git
监控dist
文件夹,否则提交远程库的时候不会提交dist
文件夹:打开根目录下的.gitignore文件,这是git的配置文件,能够在这里设置git不须要监控的文件类型。因此咱们在这里把/dist/
这行代码删除,这样,后面咱们再git add .
的时候就会监控dist
文件夹了。git add .
注意,这里有一个点,并且点和add直接有空格。git commit -m"初始化支付宝密码弹窗"
git本地提交git remote add origin git@github.com:chinaBerg/my-project.git
,后面的地址是你的远程库的ssh地址,这里不要拷贝个人地址了,你链接个人远程库是不会成功的。而后运行git remote -v
查看一下是否链接成功:第一次提交,因为本地的分支没有和远程的分支进行关联,因此咱们第一次push的时候,要先关联远程库:git push -u origin master
, 其中-u origin master
表示将当前的分支关联到远程的master分支,这样你提交的时候当前的本地分支的内容就会提交到远程的master分支上。
能够看到已经提交成功了。接下来咱们打开github上面的项目看下,刷新后看到,项目以上传到远程了,其中既有咱们的源码,也有打包后的dist文件,也有咱们以前写好的readme.md说明文档:
文件上传之后,咱们怎么设置才能在github上查看运行的项目的呢?
在当前项目中,点击setttings选项,选择Options选项卡,拉倒底部的位置,找到GitHub Pages区域,点击下拉菜单将选择master branch选项,而后点击save按钮保存。
而后咱们就会看到,这个位置有一个url地址。没错,这个就是咱们的项目路径,可是呢,咱们打包后能容许的项目是在dist文件夹下,因此咱们想访问打包后的项目的话,只要在这个路径后面拼接,例如:https://chinaberg.github.io/my-project/dist/index.html 你如今访问我这个项目可能不存在了,由于我已经删除了。
至此,咱们上传github的操做已经完成了。
其实这里主要用到的知识就是git的使用。使用git对咱们的代码进行版本控制和链接远程库等,都是咱们做为开发人员基本必需要会的一项技能,但愿还不会git的小伙伴要抽点时间学习一下。否则这里估计会坑的想放弃呢!
咱们已经把插件代码上传到github上面了,那么咱们是否能够也作成一个npm包发布到npm上呢?答案是确定的,如此一次,咱们在须要使用到该插件的时候就能够直接npm i vpay
等的实现安装到咱们的项目中,岂不是很方便呢?
正如文章刚开始提到的文件目录,咱们是把插件都写在了第二个lib中的。而整个vpay文件夹都是咱们要做为npm包发布的。其中readme.md是说明文档,跟github上的同样,你能够简单的先拷贝根目录下的那个。
而后是vpay文件下面的index.js,里面就依据代码,即导出咱们的插件:
module.exports = require('./lib')复制代码
因为咱们在使用插件的时候, 其实引入的就是这个index.js,因此在这个文件中,咱们导出了咱们的插件。
而下面重点要说的是这个package.json文件,这个一开始是没有的,这是npm包的配置文件。咱们要首先进入vpay文件夹的位置,而后终端运行npm init
命令来初始化一个npm包配置文件,此时他会问你一些列问题来完成配置文件:
Index.js
,能够自定义。.git
目录做为这一项的默认值。没使用则回车略过。到这一步,其实咱们已经作好了本地包的开发,咱们要测试一下包能不能使用。怎么测试呢?咱们知道,平时是使用npm安装插件的时候,实际上是把插件安装在了根目录下的node_modules文件中。那么,咱们既然已经开发好了本地包,咱们就把这个vpay文件夹直接拷贝到node_modules文件夹中。而后在main.js中像平时同样使用插件:
// 直接引入vpay,不须要写路径
import vpay from 'vpay'
Vue.use(vpay);复制代码
测试一看,一切正常,说明咱们的包是没问题的。
下面就是要发布了。发布以前,咱们首先要有一个npm的帐号,若是你尚未帐号的,自行去npmjs官网注册一个帐号,这里提示一下,注册用户名的时候常常可能不经过,因此仍是字母开头加一些数字方便些。这里的full name 能够理解为昵称,email邮箱,后面两个是用户名和密码。有些时候用户名老是会提示不合规范,因此仍是字母加数字快些。
回归正题,先切换到咱们的vpay文件目录,而后执行npm login命令登陆咱们的npm帐号,他会提示你输入npm的用户名和密码,注意输入密码的时候不会有任何提示,这里不要觉得是否是没有反应。你只管输入就好,只不过什么都看不到,输入完成回车,而后输入npm的邮箱。最后回车,这里有一点要注意的,若是你是以前安装了淘宝镜像,那是不会成功的,由于你的要把npm包发布到npm镜像上,而不是淘宝的镜像。咱们平时能够经过淘宝镜像来加快下载速度,由于淘宝镜像其实就是每隔十分钟把npm的上的最新资源同步到本身身上而已。因此你要经过npm的镜像登陆和发布。
若是配置了淘宝镜像,先设置回npm镜像:
npm config set registry http://registry.npmjs.org 复制代码
而后在终端执行npm login
命令:
看到最后一行Logged in as ……
就说明登陆成功了。
登陆成功后,运行npm publish
命令将npm包发布到npm上。
这样就发布成功了。咱们去npm官网查一下咱们的包:
若是发布成功后没有搜到,就稍微等几分钟。
点击进去看下,一切正常。之后就直接能够经过npm i vpay将插件撞到咱们的项目中了。
到这,咱们基本已经完成了插件从开发、到写说明文档、再到发布github、最后发布npm包,一切已经能够正常使用了。插件的个别地方的样式有些略显粗糙,可是本文的主要目的在于演示插件的开发过程。但愿能对有须要的小伙伴起到帮助。
❤若是以为喜欢就收藏一下或者start一下呗~~❤