在平常,我们使用vue组件的时候,都要先在.vue文件中引入我们要使用的组件。虽然这样能满足大部分日常开发的需求,但这种方法在某些场景下,就有些难以应对。
- 组件的模板是通过调用接口从服务端获取的,需要动态渲染组件;
- 实现类似原生 window.alert() 的提示框组件,它的位置是在 下,而非 ,并且不会通过常规的组件自定义标签的形式使用,而是像 JS 调用函数一样使用。
extend
Vue.extend 的作用,就是基于 Vue 构造器,创建一个“子类”,它的参数跟 new Vue 的基本一样,但 data 要跟组件一样,是个函数,再配合 $mount ,就可以让组件渲染,并且挂载到任意指定的节点上,比如 body。
import Vue from 'vue';
<!-- 创建了一个构造器,这个过程就可以解决异步获取 template 模板的问题 -->
const AlertComponent = Vue.extend({
template: '<div>{{ message }}</div>',
data () {
return {
message: 'Hello, Aresn'
};
},
});
<!-- 调用 $mount 方法对组件进行了手动渲染, -->
const component = new AlertComponent().$mount();
<!-- 挂载节点 -->
document.body.appendChild(component.$el);
<!-- 快捷的挂载方式 -->
new AlertComponent().$mount('#app');
这样就可以满足我们用js方法调用的方式来控制组件,但是在平常的开发中,我们用的vue的runtime编译环境,不支持template模板,而且用字符串来描述组件的模板,也有些不太友好。那么,我们可以试试用new Vue()的方式。
new Vue
new Vue()也可以直接创建 Vue 实例,并且用一个 Render 函数来渲染一个 .vue 文件。
import Vue from 'vue';
import Notification from './notification.vue';
const props = {}; // 这里可以传入一些组件的 props 选项
const Instance = new Vue({
render (h) {
return h(Notification, {
props: props
});
}
});
const component = Instance.$mount();
document.body.appendChild(component.$el);
这样既可以使用 .vue 来写复杂的组件,还可以根据需要传入适当的 props。渲染后,如果想操作 Render 的 Notification 实例,也是很简单的:
const notification = Instance.$children[0];
实例
接下来就给大家,看下我在项目中用new Vue()的方式来实现的弹框组件。首先我们要新建一个vue文件来,完成弹框的基本布局和样式。
modal-dialog
此文件实现了弹框的基本布局,蒙层和关闭事件,弹框内容可以通过插槽来控制,支持的props有width,height,bgSrc(弹框的背景图片),visible,hasClose(是否有关闭‘X’)
<template>
<transition name="fade">
<div
style="position:fixed;top: 0;left: 0;width:100vw;height:100vh;z-index:100;"
v-if="isShow"
>
<div class="mask" @click.stop="closeDialog"></div>
<div
class="dialog"
:class="classObject"
:style="{
width,
height,
backgroundImage: `url(${bgSrc})`,
backgroundRepeat: 'no-repeat',
...wrapStyle
}"
>
<slot></slot>
<div
v-if="hasClose"
class="close"
@click.stop="closeDialog"
:style="closeStyle"
></div>
</div>
</div>
</transition>
</template>
<script>
export default {
name: "modalDialog",
props: {
visible: {
type: Boolean,
default: false
},
width: {
type: String,
default: "6.52rem"
},
bgSrc: {
type: String,
required: true
},
height: {
type: String,
default: "6.83rem"
},
hasClose: {
type: Boolean,
default: true
},
wrapClass: {
type: String,
default: ""
},
wrapStyle: {
type: Object,
default: function() {
return {};
}
},
closeStyle: {
type: Object,
default: function() {
return {};
}
}
},
watch: {
visible(val) {
<!-- 同步visible的变化 -->
this.isShow = val;
}
},
data() {
return {
<!-- vue单向数据流,因此我们重新定义一个属性isShow -->
isShow: this.visible,
classObject: {
[this.wrapClass]: true
}
};
},
created() {},
methods: {
closeDialog() {
this.isShow = false;
<!-- 通知父组件 visible的变化 父组件使用.sync修饰符即可 -->
this.$emit("update:visible", false);
}
}
};
</script>
<style lang="less" scoped>
@imgPath: "../assets/img/dialog";
.translateXCenter {
left: 50%;
transform: translateX(-50%) !important;
-webkit-transform: translateX(-50%) !important;
}
.translateCenter {
top: 50%;
left: 50%;
transform: translate(-50%, -50%) !important;
-webkit-transform: translate(-50%, -50%) !important;
}
.translateY(@y) {
left: 50%;
top: 50%;
transform: translate(-50%, @y);
-webkit-transform: translate(-50%, @y);
}
.background(@imgName) {
background: url("@{imgPath}/@{imgName}") no-repeat;
background-size: 100% auto;
}
.mask {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(10, 13, 24, 0.8);
z-index: 100;
}
.dialog {
// background-repeat: no-repeat
background-size: 100% auto;
position: absolute;
z-index: 100;
// .translateXCenter;
.translateCenter;
}
.close {
width: 0.56rem;
height: 0.56rem;
position: absolute;
top: -0.4rem;
right: 0rem;
.background("btn_close.png");
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.2s ease-in;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-enter-to,
.fade-leave {
opacity: 1;
}
</style>
弹框的基本功能实现后,我们就用它来建一个常用的通知组件 alert-box。文件中的图片大家自行替换呀。样式方面也请自行调整呀。
<template>
<modal-dialog
<!-- 这里一定要加sync修饰符 -->
:visible.sync="isShow"
width="6.7rem"
height="5.51rem"
:bgSrc="require('@img/dialog/tuan_bg.png')"
>
<div class="content">
<ul>
<template v-if="textArr.length > 0">
<li v-for="(text, index) in textArr" :key="index">{{ text }}</li>
</template>
</ul>
<div class="btn-wrap">
<div class="konw" @click="hideBox"></div>
</div>
</div>
</modal-dialog>
</template>
<script>
import ModalDialog from "@/components/modalDialog.vue";
export default {
name: "AlertBox",
props: {
<!-- 显示隐藏 -->
visible: {
type: Boolean,
default: false
},
<!-- 消息的内容 -->
textArr: {
type: Array,
default() {
return [];
}
}
},
components: {
ModalDialog
},
data: function() {
return {
<!-- props单向数据流,组件内部用isShow记录组件状态,最后同步给父级 -->
isShow: this.visible,
};
},
methods: {
hideBox() {
this.isShow = false;
}
},
watch: {
visible(newval) {
this.isShow = newval;
},
isShow(newval) {
<!-- 通知父组件,同步组件显示隐藏状态 -->
this.$emit("update:visible", newval);
}
}
};
</script>
<style lang="less" scoped>
@imgPath: "../assets/img/dialog";
.background(@imgName) {
background: url("@{imgPath}/@{imgName}") no-repeat;
background-size: 100% auto;
}
.translateCenter {
top: 50%;
left: 50%;
transform: translate(-50%, -50%) !important;
-webkit-transform: translate(-50%, -50%) !important;
}
.content {
width: 100%;
height: 100%;
position: relative;
ul {
position: absolute;
// background-color: skyblue;
width: 5.24rem;
height: 2.83rem;
color: #914e4c;
font-size: 0.32rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
top: 0.9rem;
left: 0.76rem;
// font-weight: 600;
li {
text-align: justify;
}
}
.btn-wrap {
position: absolute;
top: 4.2rem;
width: 100%;
.konw {
width: 2.66rem;
height: 1.05rem;
margin: 0 auto;
display: block;
.background("btn_know.png");
}
}
}
</style>
最后,是重头戏,如何将这个alertBox的实例挂载在vue的原型上呢?下面我们用new Vue来实现
alert.js
import Vue from "vue";
import AlertBox from "./alertBox.vue";
let contain = document.body,
messageInstance = null,
component = null;
AlertBox.newInstance = properties => {
const props = properties || {};
// props : visible ; textType 传入props visible,我们还可以传入很多的props 详情查看官方文档
const Instance = new Vue({
render(h) {
return h(AlertBox, {
props,
nativeOn: {
"update:visible": function(val) {
// 监听alerBox isShow变化的事件,如果组件隐藏,我们移除挂载节点,并销毁实例
if (!val) {
contain.removeChild(component.$el);
Instance.$destroy();
}
}
}
});
}
});
// 组件渲染
component = Instance.$mount();
// 组件挂载
contain.appendChild(component.$el);
};
// 保证单一实例
function getMessageInstance(props) {
messageInstance = messageInstance || AlertBox.newInstance(props);
}
export default {
// 暴露info方法,后面可以增加新的方法
info(props) {
return getMessageInstance(props);
}
};
最后我们在项目的main.js文件引入alert.js
import MyAlert from "./components/alert";
Vue.prototype.$myAlert = MyAlert;
就可以在vue文件中,用 this.$myAlert.info() 实现通知弹框了。
this.$myAlert.info({
visible: true,
textArr: ["团队解散失败,请重试"]
});