手把手教你撸个vue2.0弹窗组件
手把手教你撸个vue2.0弹窗组件
在开始之前需要了解一下开发vue插件的前置知识,推荐先看一下vue官网的插件介绍
预览地址 http://haogewudi.me/kiko/inde...
源码地址 https://github.com/rascalHao/...
搭建项目
- vue-cli将你的vue项目初始化建好 vue init webpack my-project
平常我们引入插件的流程是:
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)
- 在你的项目目录下通过npm init指令来初始化一个package.json文件,默认指定你的入口文件index.js,并在你的项目根目录下新建一个index.js入口文件
- 这里会构建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 发包
- 确保你的项目的根目录的package.json文件已经建好
- 登录npm官网注册
- 在你的项目目录下登录npm login(之后按提示填写信息)
- 发包npm publish
如果执行npm publish出现错误,可能是你的包名已经被注册过,在npm 官网上搜索一下是否已被注册了。若发包成功,你就可以在npm官网上搜索到自己的包。
发包成功后,就可以通过
`
import Vue from 'vue'
// 我的npm包是kiko-rascalhao
import Kiko from 'kiko-rascalhao'
Vue.use(Kiko)
`
引入你的插件啦
由于本人学识有限,有很多需要提升的地方,望大家多多指教。
手把手教你撸个vue2.0弹窗组件的更多相关文章
- vue.js+koa2项目实战(五)axios 及 vue2.0 子组件和父组件之间的传值
axios 用法: 1.安装 npm install axios --save-dev 2.导入 import axios from 'axios'; 3.使用 axios.post(url,para ...
- Vue2.0进阶组件 短信倒计时组件
原本我想隔个几天再发文章,刚好今天项目上线,环境有问题,导致只有干等,刚好要为公司打造一套属于公司自己的一系列功能组件,这个使命就交给我了,大家也一直叫我来点干货,说实话我只是一个湿货,肚子里干一点就 ...
- Vue2.0父子组件间事件派发机制
从vue1.x过来的都知道,在vue2.0中,父子组件间事件通信的$dispatch和$broadcase被移除了.官方考虑是基于组件树结构的事件流方式实在是让人难以理解,并且在组件结构扩展的过程中会 ...
- 基于vue2.0前端组件库element中 el-form表单 自定义验证填坑
eleme写的基于vue2.0的前端组件库: http://element.eleme.io 我在平时使用过程中,遇到的问题. 自定义表单验证出坑: 1: validate/resetFields 未 ...
- vue2.0父子组件之间通信
父组件是通过props属性给子组件通信的来看下代码: 父组件: <parent> <child :child-com="content"></chil ...
- vue2.0 父子组件通信 兄弟组件通信
父组件是通过props属性给子组件通信的来看下代码: 父组件: <parent> <child :child-com="content"></chil ...
- vue2.0子组件修改父组件props数据的值
从vue1.0升级至2.0之后 prop的.sync被去除 因此直接在子组件修改父组件的值是会报错的如下: 目的是为了阻止子组件影响父组件的数据那么在vue2.0之后 如何在子组件修改父组件props ...
- vue2.0 父子组件数据传递prop
vue的一个核心概念就是组件,而组件实例的作用域是孤立的,所以组件之间是不能直接引用其他组件的数据的.极端点举例来说,就是可以在同一个项目中,每一个组件内都可以定义相同名称的数据. data () { ...
- vue2.0父子组件通信的方法
vue2.0组件通信方法:props传值和emit监听.(.sync方法已经移除.详情请点击)(dispatch-和-broadcast方法也已经废弃) props方法传值:Props 现在只能单项传 ...
随机推荐
- 解决 “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 ...
- Python回顾笔记(此讲大致说明,详情请看之前的笔记)
内容概要 数据分析(numpy,pandas,matplib) 数据清洗 爬虫 teableau软件 今日内容概要 Python知识回顾 数据分析 ipython模块 anaconda软件 numpy ...
- C#10新特性-lambda 表达式和方法组的改进
C# 10 中对Lambda的语法和类型进行了多项改进: 1. Lambda自然类型 Lambda 表达式现在有时具有"自然"类型. 这意味着编译器通常可以推断出 lambda 表 ...
- ARP攻击的发现、攻击原理、攻击方式、防护,竟然这么简单?!
ARP协议概述 ARP协议(address resolution protocol)地址解析协议. 一台主机和另一台主机通信,要知道目标的IP地址,但是在局域网中传输数据的网卡却不能直接识别IP地址, ...
- laravel resource风格
resource 风格 概念 一种软件架构风格.设计风格,而不是标准,只是提供了一组设计原则和约束条件.它主要用于客户端和服务器交互类的软件.基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存 ...
- Applied Social Network Analysis in Python 相关笔记
- 微信Native支付
微信Native支付对接(扫码) 由于有业务需求对接了微信和paypal支付,这边做个记录 微信支付开发文档:https://pay.weixin.qq.com/wiki/doc/api/index. ...
- springcloud学习04- 断路器Spring Cloud Netflix Hystrix
依赖上个博客:https://www.cnblogs.com/wang-liang-blogs/p/12072423.html 1.断路器存在的原因 引用博客 https://blog.csdn.ne ...
- CTF--Do you like xml
题目链接:http://47.94.221.39:8008/ 扫描目录得到/.DS_Store文件 下载文件,直接用脚本进行还原操作. https://github.com/lijiejie/ds_s ...
- (一)【转】asp.net mvc生成验证码
网站添加验证码,主要为防止机器人程序批量注册,或对特定的注册用户用特定程序暴力破解方式,以进行不断的登录.灌水等危害网站的操作.验证码被广泛应用在注册.登录.留言等提交信息到服务器端处理的页面中. ...