使用效果
导入使用
//引入zc-ui组件库
import zcui from "./components/zc-ui"
//注册组件
Vue.use(zcui)
//使用
this.$alert({
title:"xxx提示标题", //可选项默认为 “提示”
text:"xxx提示文字", //必要项显示提示内容
icon:"info", //可选 弹窗文字旁的图标
confirmText:"确定",//可选确定按钮显示的文本,默认为 “确定”
})
运行效果
需求分析
1、要能自定义弹窗标题、消息文本,弹窗按钮文本
2、要支持全局的函数式调用,一处引入随从调用
3、弹窗组件要有比较高的权重,覆盖在顶层却不能像普通组件一样受到路由影响
4、弹窗按钮在用户点击后要拿到用户选中的转态,可以通过返回Promise的方式处理
前置知识
1、Vue.extend
在alert和confirm这类弹窗组件中,组件需要动态地创建和销毁,而且最好是像原生组件一样可以随从调用,为此需要用到vue给我们提供的Vue.extend
方法以供我们动态生成组件
Vue.extend
可与接收一个对象或者一个vue的模板作为参数,执行后返回此模板的构造函数
官方文档例子:https://cn.vuejs.org/v2/api/#Vue-extend
// 创建构造器
var Profile = Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 创建 Profile 实例,并挂载到一个元素上。
new Profile().$mount('#mount-point')
2、Vue.use( )
Vue.use( )
官方提供给我们用于插件安装的方法,此方法接收一个插件或方法作为参数,参数为一个函数时函数会直接被执行并把Vue作为参数传入。当传入参数为一个插件时该方法会找到插件下的install
方法执行并把Vue作为参数传入,稍后会用到
官方文档:https://cn.vuejs.org/v2/api/#Vue-use
3、import 导入规则
在import导入文件时如果只写目录名,打包时会自动找到目录下的index.js
导入,
如:zc-ui/index.js
导入时可以写成import zcuifrom "zc-ui"
开始撸代码
文件结构和流程
代码
alert.vue(样式和结构)
alert.vue
<template>
<div
:class="['zc-alert', { backgorundBlur: config.backgorundBlur }]"
ref="zc_alert"
>
<div class="zc-alert-centent">
<!-- 弹窗标题 -->
<div class="zc-alert-header">
{{ config.title || "提示" }}
</div>
<!-- 弹窗内容 -->
<div class="zc-alert-msg">
<i :class="['iconfont',config.icon]"></i><slot>{{ config.text }}</slot>
</div>
<!-- 弹窗按钮 -->
<div class="zc-alert-btn_box">
<zc-button v-if="config.type == 'confirm'" class="button" @click="cancel()" >
{{ config.cancelText || "取消" }}
</zc-button
>
<zc-button class="button" type="primary" @click="confirm()">
{{ config.confirmText || "确定" }}
</zc-button>
</div>
</div>
</div>
</template>
<script>
export default {
name: "zc-alert",
props: {
config: {
required: true,
}
}
};
</script>
<style lang="less" scoped>
.zc-alert {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 99999;
background-color: rgba(0, 0, 0, 0);
backdrop-filter: blur(0px);
transition: background-color 0.4s linear;
perspective: 2000;
-webkit-perspective: 2000;
.zc-alert-centent {
position: relative;
margin: 0 auto;
top: 30vh;
transform: translateY(-30%) rotateX(20deg);
min-width: 250px;
max-width: 400px;
padding: 15px;
// background-color: rgb(246, 246, 246);
background-color: #fff;
// background-image: linear-gradient(180deg,rgb(253, 253, 253),rgb(245, 245, 245));
border-top: 1px solid #fff;
border-left: 1px solid #fff;
border-radius: 5px;
box-shadow: 5px 10px 10px rgba(0, 0, 0, 0.2),0 20px 30px rgba(0, 0, 0, 0.2);
opacity: 0;
transition: all 0.3s cubic-bezier(0.17, 0.99, 0.46, 0.99);
//成功图标
i.success::before{
content: "\e644";
font-size: 16px;
margin-right: 5px;
color: #6bce9f;
}
//成功图标
i.info::before{
content: "\e60b";
font-size: 16px;
margin-right: 5px;
color: #ffa013;
}
}
.zc-alert-header {
padding: 5px 0 10px;
font-size: 16px;
color: rgb(63, 63, 63);
}
.zc-alert-msg {
padding: 5px 0 10px;
font-size: 14px;
color: #848484;
}
.zc-alert-btn_box {
display: flex;
width: 100%;
justify-content: flex-end;
margin-top: 10px;
.button {
margin-left: 20px;
height: 30px;
}
}
&.show {
background-color: rgba(0, 0, 0, 0.5);
.zc-alert-centent {
transform: translateY(0) rotateX(0deg);
opacity: 1;
}
}
}
</style>
zc-ui/alert/index.js(组件的事件逻辑处理,给组件添加install
方法,导出)
zc-ui/alert/index.js
import alert from './alert.vue'
alert.install = function (Vue) {
function create(Component, props) {
return new Promise((resolve, reject) => {
//方式一:使用Vue.extend创建
const Ctor = Vue.extend(Component);
//创建组件实例
const alertDom = new Ctor({ propsData: { config: props } })
//挂载
alertDom.$mount();
document.body.appendChild(alertDom.$el);//把元素追加到body后面
setTimeout(() => {
alertDom.$el.classList.add("show")
}, 50)
alertDom.flag = false
//监听组件的transitionend事件等动画结束后从dom移除并销毁组件
function handleTransitionend(e) {
if (alertDom.flag && e.target.classList.contains('zc-alert')) {
document.body.removeChild(alertDom.$el) //移除元素
alertDom.$destroy() //销毁
}
}
//监听过度动画
alertDom.$el.addEventListener("transitionend", handleTransitionend)
alertDom.$el.addEventListener("webkitTransitionEnd", handleTransitionend)
//移除弹窗组件
alertDom.remove = () => {
alertDom.$el.classList.remove("show");
alertDom.flag = true
}
//取消按钮事件处理
alertDom.cancel = (res) => {
reject(res)
alertDom.remove()
}
//确定按钮事件处理
alertDom.confirm = (res) => {
resolve(res)
alertDom.remove()
}
})
}
Vue.prototype.$alert = (obj) => {
return create(alert, obj)
}
Vue.prototype.$confirm = (obj) => {
return create(alert, { type: 'confirm', ...obj })
}
};
export default alert
zc-ui/index.js(导入所有组件内,打包导出)
zc-ui/index.js
import alert from './alert'
import button from './button'
//......
const components = [
alert,
button,
//......
]
const install = function (Vue) {
components.forEach((item)=>{
Vue.use(item)
})
}
export default {install}
总结
在其他vue的组件库中其实实现方法都大致雷同,核心都是封装组件模板样式结构,并预留“坑位”,使用时引入并通过Vue.use()
把组件注册到全局,使用时用数据把“坑”填上渲染,部分如alert
、toast
等组件通常会挂载到Vue
原型上,使用时通过this.$xxx
调用