手把手教你撸个vue2.0弹窗组件

在开始之前需要了解一下开发vue插件的前置知识,推荐先看一下vue官网的插件介绍

预览地址 http://haogewudi.me/kiko/inde...

源码地址 https://github.com/rascalHao/...

搭建项目

  1. vue-cli将你的vue项目初始化建好 vue init webpack my-project
  2. 平常我们引入插件的流程是:

    npm i <package> -S
    
    import Vue from 'vue'
    import xxx from 'xxx'
    Vue.use(xxx)

所以可以在node_modules下面新建一个你的开发目录,我这里命名为kiko,
所以现在大概引入我们的开发插件的步骤为(项目最终构建完会采取发布npm包的形式)

  import Vue from 'vue'
import Kiko from '../node_modules/kiko/index.js'
Vue.use(Kiko)
  1. 在你的项目目录下通过npm init指令来初始化一个package.json文件,默认指定你的入口文件index.js,并在你的项目根目录下新建一个index.js入口文件
  2. 这里会构建4中类型的弹窗组件(message、toolTip、confirm、loading),基本的结构如图所示

入口文件(可以先略过)

Vue.js 的插件应当有一个公开方法 install 。这个方法的第一个参数是 Vue 构造器 , 第二个参数是一个可选的选项对象;通过全局方法 Vue.use() 使用插件;可以再次看下vue官网的插件介绍

    import KikoMessage from './packages/message/index.js'
import KikoToolTip from './packages/tips/index.js'
import KikoConfirm from './packages/confirm/index.js'
import KikoLoading from './packages/loading/index.js' const install = function(Vue) {
Vue.component(KikoMessage.name, KikoMessage)
Vue.component(KikoToolTip.name, KikoToolTip)
Vue.component(KikoConfirm.name, KikoConfirm)
Vue.component(KikoLoading.name, KikoLoading) Vue.prototype.$kiko_tooltip = KikoToolTip.installToolTip
Vue.prototype.$kiko_message = KikoMessage.installMessage
}
export default install

message

在项目的根目录创建message组件,通过

Vue.prototype.$kiko_message = function (methodOptions) {

// 逻辑...

}
来添加实例方法全局以调用this.$kiko_message()的方式来调用message

  • message组件结构

  • main.vue
    <template>
<transition name="fade">
<div class="kiko-message" v-if="isShow">
{{message}}
</div>
</transition>
</template> <script type="text/javascript">
export default {
name: 'kiko-message',
data () {
return {
message: '',
time: 3000,
isShow: true
}
},
mounted () {
this.close()
},
methods: {
close () {
var that = this
window.setTimeout(function() {
that.isShow = false
}, this.time);
}
}
}
</script>
  • index.js
    import Vue from 'vue'
import Message from './src/main.vue' Message.installMessage = function(options) {
if (options === undefined || options === null) {
options = {
message: ''
}
} else if (typeof options === 'string' || typeof options === 'number') {
options = {
message: options
}
}
var message = Vue.extend(Message) var component = new message({
data: options
}).$mount()
document.querySelector('body').appendChild(component.$el)
} export default Message

到这里的时候可以看下前面的入口文件介绍,你需要通过Vue.component注册为组件,并把Message.installMessage方法绑定到Vue.prototype.$kiko_message上。

toolTip

没有选择通过固化在页面中的方式来引入toolTip,因为考虑到可能在页面中的任何地方引入toolTip,如果固化了,将会大大限制toolTip的使用场景。所以采用了一个绑定到Vue.prototype的this.$kiko_tooltip全局方法来触发,这样就可以自定义触发方式,只需要通过传入$event就可以自动地定位任何有需要的元素了。

  • toolTip组件结构

同message组件结构

  • main.vue
  <template>
<div v-if="isShow" id="kiko_tool_tip" class="kiko-tool-tip" :class="{'left': direction === 'left', 'right': direction === 'right', 'top': direction === 'top', 'bottom': direction === 'bottom'}" :style="{'background-color': background, 'color': color, 'top': top, 'left': left}">
{{content}}
<div class="arrow" :style="arrowStyleObject"></div>
</div>
</template> <script type="text/javascript">
export default {
name: 'kikoToolTip',
data () {
return {
isShow: true,
time: 3000,
content: '',
direction: 'right',
background: 'red',
color: '#fff',
arrowStyleObject: ''
}
},
beforeMount () {
let node = document.querySelector('#kiko_tool_tip')
if (node && node.parentNode) {
node.parentNode.removeChild(node)
}
},
computed: {
top () {
switch (this.direction) {
case 'top':
return (this.rect.top - 12) + 'px'
case 'bottom':
return (this.rect.top + 12) + 'px'
case 'left':
return (this.rect.top + this.rect.height / 2) + 'px'
case 'right':
return (this.rect.top + this.rect.height / 2) + 'px'
}
},
left () {
switch (this.direction) {
case 'top':
return (this.rect.left + this.rect.width / 2) + 'px'
case 'bottom':
return (this.rect.left + this.rect.width / 2) + 'px'
case 'left':
return (this.rect.left - 12) + 'px'
case 'right':
return (this.rect.left + this.rect.width + 12) + 'px'
}
}
},
mounted () {
this.initColor()
this.hidden()
},
methods: {
initColor () {
switch (this.direction.toLowerCase()) {
case 'left':
this.arrowStyleObject = {
borderLeftColor: this.background
}
break;
case 'right':
this.arrowStyleObject = {
borderRightColor: this.background
}
break;
case 'top':
this.arrowStyleObject = {
borderTopColor: this.background
}
break;
case 'bottom':
this.arrowStyleObject = {
borderBottomColor: this.background
}
break;
} },
hidden () {
let that = this
window.setTimeout(function(){
that.isShow = false
}, this.time)
}
}
}
</script> <style type="text/css">
.kiko-tool-tip {
display: block;
position: absolute;
position: fixed;
background-color: #3695CC;
padding: 10px 10px;
border-radius: 5px;
color: #fff;
white-space: nowrap;
z-index: 99999999
}
.kiko-tool-tip.left {
transform: translate(-100%, -50%);
}
.kiko-tool-tip.right {
transform: translate(0, -50%);
}
.kiko-tool-tip.top {
transform: translate(-50%, -100%);
}
.kiko-tool-tip.bottom {
transform: translate(-50%, 100%);
}
.kiko-tool-tip.right .arrow {
display: inline-block;
position: absolute;
content: '';
width: 0;
height: 0;
top: 50%;
left: -10px;
border-top: 10px solid transparent;
border-right: 15px solid #3695CC;
border-bottom: 10px solid transparent;
transform: translate(0, -50%);
}
.kiko-tool-tip.left .arrow {
display: inline-block;
position: absolute;
content: '';
width: 0;
height: 0;
top: 50%;
right: -10px;
border-top: 10px solid transparent;
border-left: 15px solid #3695CC;
border-bottom: 10px solid transparent;
transform: translate(0, -50%);
}
.kiko-tool-tip.top .arrow {
display: inline-block;
position: absolute;
content: '';
width: 0;
height: 0;
left: 50%;
bottom: -10px;
border-top: 15px solid #3695CC;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
transform: translate(-50%, 0);
}
.kiko-tool-tip.bottom .arrow {
display: inline-block;
position: absolute;
content: '';
width: 0;
height: 0;
left: 50%;
top: -10px;
border-bottom: 15px solid #3695CC;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
transform: translate(-50%, 0);
}
</style>
  • index.js
  import Vue from 'vue'
import ToolTip from './src/main.vue' ToolTip.installToolTip = function(event, opt) { var options = opt var rect = {};
['top', 'left'].forEach(function(property) {
var scroll = property === 'top' ? 'scrollTop' : 'scrollLeft'
rect[property] = event.target.getBoundingClientRect()[property] +
document.body[scroll] +
document.documentElement[scroll]
});
['height', 'width'].forEach(function(property) {
rect[property] = event.target.getBoundingClientRect()[property]
});
options.rect = rect
var toolTip = Vue.extend(ToolTip) var component = new toolTip({
data: options
}).$mount()
event.target.appendChild(component.$el)
} export default ToolTip

通过Element.getBoundingClientRect()方法获取元素的大小及其相对于视口的位置,之后对提示信息进行fixed定位。

confirm

confirm在保留页面的情况下会弹出一个对话框,适合一些场景更大的情况。可以用来进行一些复杂带校验的弹窗信息展示,也可以只用于简单信息的展示。可以通过title属性来显示任意标题,通过width属性来修改显示区域的宽度。

  • confirm组件结构

同message组件

  • main.vue
  <template>
<transition name="bounce">
<div class="kiko-confirm" v-if="visible">
<div class="bg"></div>
<div class="kiko-container" :style="{width: width}">
<div class="header">
{{title}}
<i @click="close" class="icon-remove icon-large kiko-close-btn" v-if="closeVisible"></i>
</div>
<div class="content">
<slot></slot>
</div>
<slot name="footer">
<!-- <div class="kiko-footer" slot="footer">
<a href="javscript:void(0)" class="kiko-btn make-sure">确定</a>
<a href="javscript:void(0)" class="kiko-btn cancel">取消</a>
</div> -->
</slot>
</div>
</div>
</transition>
</template> <script type="text/javascript">
import '../../../lib/icon/css/font-awesome.css'
export default {
name: 'kiko-confirm',
props: {
width: {
type: String,
default: '260px'
},
title: {
type: String,
default: '信息'
},
visible: {
type: Boolean,
default: false
},
closeVisible: {
type: Boolean,
default: true
}
},
data () {
return {
}
},
methods: {
close () {
this.$emit('update:visible', false)
}
}
}
</script>
  • index.js
  import Vue from 'vue'
import Confirm from './src/main.vue' export default Confirm

这里通过组件的方式进行引入,可以只是简单地信息提示,也可以自己进行一些复杂的校验。对组件的显示与隐藏这里引用了.sync修饰符,也可以通过v-if指令。运用slot来分发内容。

loading

考虑到可能不需要整屏渲染,只需要局部加载loading,在指定的位置可以按需通过自定义指令来实现,通过Element.getBoundingClientRect()方法根据需要的元素位置、区域大小自动定位;若想整屏渲染,则需要加个.fullscreen修饰符。

  • loading组件结构
    同message组件
  • main.vue
  <template>
<div class="kiko-loading" :style="{'top': top, 'left': left, 'width': width, 'height': height}">
<div class="bg"></div>
<div class="kiko-container">
<i class="icon-spinner icon-spin icon-4x"></i>
</div>
</div>
</template> <script type="text/javascript">
export default {
name: 'kiko-loading',
data () {
return {
top: 0,
left: 0,
width: '100%',
height: '100%'
}
}
}
</script>
  • index.js
  import Vue from 'vue'
import Loading from './src/main.vue' const loadingConstructor = Vue.extend(Loading) Vue.directive('kiko-loading', {
update: function(el, binding) {
if (binding.oldValue != binding.value) {
const options = {}
options.fullScreen = binding.modifiers.fullscreen ? true : false;
if (options.fullScreen) {
options.top = 0
options.left = 0
options.width = '100%'
options.height = '100%'
} else {
['top', 'left'].forEach(function(property) {
var scroll = property === 'top' ? 'scrollTop' : 'scrollLeft'
options[property] = el.getBoundingClientRect()[property] +
document.body[scroll] +
document.documentElement[scroll] +
'px'
});
['height', 'width'].forEach(function(property) {
options[property] = el.getBoundingClientRect()[property] + 'px'
});
}
var component = new loadingConstructor({
data: options
}).$mount()
var node = document.querySelector('.kiko-loading')
if (node && node.parentNode) {
node.parentNode.removeChild(node)
}
if (binding.value === true) {
document.querySelector('body').appendChild(component.$el)
} else {
var node = document.querySelector('.kiko-loading')
if (node && node.parentNode) {
node.parentNode.removeChild(node)
}
}
}
}
}) export default Loading

npm 发包

  1. 确保你的项目的根目录的package.json文件已经建好
  2. 登录npm官网注册
  3. 在你的项目目录下登录npm login(之后按提示填写信息)
  4. 发包npm publish

如果执行npm publish出现错误,可能是你的包名已经被注册过,在npm 官网上搜索一下是否已被注册了。若发包成功,你就可以在npm官网上搜索到自己的包。

发包成功后,就可以通过

`
import Vue from 'vue'
// 我的npm包是kiko-rascalhao
import Kiko from 'kiko-rascalhao'
Vue.use(Kiko)
`
引入你的插件啦

由于本人学识有限,有很多需要提升的地方,望大家多多指教。

手把手教你撸个vue2.0弹窗组件的更多相关文章

  1. vue.js+koa2项目实战(五)axios 及 vue2.0 子组件和父组件之间的传值

    axios 用法: 1.安装 npm install axios --save-dev 2.导入 import axios from 'axios'; 3.使用 axios.post(url,para ...

  2. Vue2.0进阶组件 短信倒计时组件

    原本我想隔个几天再发文章,刚好今天项目上线,环境有问题,导致只有干等,刚好要为公司打造一套属于公司自己的一系列功能组件,这个使命就交给我了,大家也一直叫我来点干货,说实话我只是一个湿货,肚子里干一点就 ...

  3. Vue2.0父子组件间事件派发机制

    从vue1.x过来的都知道,在vue2.0中,父子组件间事件通信的$dispatch和$broadcase被移除了.官方考虑是基于组件树结构的事件流方式实在是让人难以理解,并且在组件结构扩展的过程中会 ...

  4. 基于vue2.0前端组件库element中 el-form表单 自定义验证填坑

    eleme写的基于vue2.0的前端组件库: http://element.eleme.io 我在平时使用过程中,遇到的问题. 自定义表单验证出坑: 1: validate/resetFields 未 ...

  5. vue2.0父子组件之间通信

    父组件是通过props属性给子组件通信的来看下代码: 父组件: <parent> <child :child-com="content"></chil ...

  6. vue2.0 父子组件通信 兄弟组件通信

    父组件是通过props属性给子组件通信的来看下代码: 父组件: <parent> <child :child-com="content"></chil ...

  7. vue2.0子组件修改父组件props数据的值

    从vue1.0升级至2.0之后 prop的.sync被去除 因此直接在子组件修改父组件的值是会报错的如下: 目的是为了阻止子组件影响父组件的数据那么在vue2.0之后 如何在子组件修改父组件props ...

  8. vue2.0 父子组件数据传递prop

    vue的一个核心概念就是组件,而组件实例的作用域是孤立的,所以组件之间是不能直接引用其他组件的数据的.极端点举例来说,就是可以在同一个项目中,每一个组件内都可以定义相同名称的数据. data () { ...

  9. vue2.0父子组件通信的方法

    vue2.0组件通信方法:props传值和emit监听.(.sync方法已经移除.详情请点击)(dispatch-和-broadcast方法也已经废弃) props方法传值:Props 现在只能单项传 ...

随机推荐

  1. 解决 “Project ERROR: Unknown module(s) in QT: webengine”以及“Your MaintenanceTool appears to be older than 3.0.2. .” 的办法

    1.环境 Windows10,Qt5.8.0 2.问题描述 需要使用到WebEngineView组件,在工程.pro中增加webengine后,Qt Creator应用程序输出中打印了 Project ...

  2. Python回顾笔记(此讲大致说明,详情请看之前的笔记)

    内容概要 数据分析(numpy,pandas,matplib) 数据清洗 爬虫 teableau软件 今日内容概要 Python知识回顾 数据分析 ipython模块 anaconda软件 numpy ...

  3. C#10新特性-lambda 表达式和方法组的改进

    C# 10 中对Lambda的语法和类型进行了多项改进: 1. Lambda自然类型 Lambda 表达式现在有时具有"自然"类型. 这意味着编译器通常可以推断出 lambda 表 ...

  4. ARP攻击的发现、攻击原理、攻击方式、防护,竟然这么简单?!

    ARP协议概述 ARP协议(address resolution protocol)地址解析协议. 一台主机和另一台主机通信,要知道目标的IP地址,但是在局域网中传输数据的网卡却不能直接识别IP地址, ...

  5. laravel resource风格

    resource 风格 概念 一种软件架构风格.设计风格,而不是标准,只是提供了一组设计原则和约束条件.它主要用于客户端和服务器交互类的软件.基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存 ...

  6. Applied Social Network Analysis in Python 相关笔记

  7. 微信Native支付

    微信Native支付对接(扫码) 由于有业务需求对接了微信和paypal支付,这边做个记录 微信支付开发文档:https://pay.weixin.qq.com/wiki/doc/api/index. ...

  8. springcloud学习04- 断路器Spring Cloud Netflix Hystrix

    依赖上个博客:https://www.cnblogs.com/wang-liang-blogs/p/12072423.html 1.断路器存在的原因 引用博客 https://blog.csdn.ne ...

  9. CTF--Do you like xml

    题目链接:http://47.94.221.39:8008/ 扫描目录得到/.DS_Store文件 下载文件,直接用脚本进行还原操作. https://github.com/lijiejie/ds_s ...

  10. (一)【转】asp.net mvc生成验证码

    网站添加验证码,主要为防止机器人程序批量注册,或对特定的注册用户用特定程序暴力破解方式,以进行不断的登录.灌水等危害网站的操作.验证码被广泛应用在注册.登录.留言等提交信息到服务器端处理的页面中.   ...