手把手教你撸个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 现在只能单项传 ...
随机推荐
- SpringMVC入门一:基础知识(依赖、注解、文件上传/下载、拦截器、异常处理等)
为了使Spring可插入MVC架构,SpringFrameWork在Spring基础上开发SpringMVC框架,从而使用Spring进行WEB开发时可以选择使用Spring的SpringMVC框架作 ...
- Triple Shift
来源:Atcoder ARC 136 B - Triple Shift (atcoder.jp) 题解:这道题我们不可能去硬模拟(大多数这种题都不能这样去模拟的),然后我们就要去发现特性, 发现把 a ...
- 【python】人脸识别
#coding:utf-8# from __future__ import print_functionfrom time import time #有些步骤要计时,看每个步骤花多长时间import ...
- 怎么做 HDFS 的原地平滑缩容?
背景 当数据规模越来越大,存储成本也水涨船高.随着时间推移,数据热度分布往往呈 2⁄8 原则,即 80% 的访问集中在 20% 的数据上.对于那不经常访问的 80% 数据来说,使用多个 SSD 来存储 ...
- LGP6146题解
思维僵化了,习惯按照右端点排序,没想到是按照左端点排序... 考虑从左到右依次加入线段,考虑贡献. 设前 \(i\) 条线段的答案为 \(dp[i]\). 考虑两种情况: 不加,贡献为 \(dp[i- ...
- test 分支强制替换master 分支的办法
test分支改动太多,并且master 分支好久没有改动.直接合并到master 分支的话,会产生很多冲突,几十个文件,修复冲突会花很多时间,并且是没有意义的.因此只能使用test 分支强制替换. 代 ...
- java项目中日志的配置
1.日志相关比较详细的介绍:https://www.cnblogs.com/tanshaoxiaoji/p/log4j_config.html 2.总结1中博客说的内容 目前市场上常用的日志有log4 ...
- PowerSploit的使用
Invoke-Mimikatz(依赖管理员) Import-Module .\invoke-mimikatz.ps1 Invoke-Mimikatz –DumpCreds 使用Invoke-Mimik ...
- 实践丨SpringBoot整合Mybatis-Plus项目存在Mapper时报错
摘要:在SpringBoot运行测试Mybatis-Plus测试的时候报错的问题分析与修复 本文分享自华为云社区<SpringBoot整合MybatisPlus项目存在Mapper时运行报错的问 ...
- width:auto 和 width:100%有什么区别
width:auto 和 width:100%有什么区别 宽度计算不同 <div class="parent"> <span class="child& ...