最近在做vue项目有个需求,需要通过js来调起弹窗组件,从网上搜资料发现可以使用Vue.extend().$mount()的方式来实现,于是乎在网上搜索代码,找到了一位大佬的代码来改了改。
template
<template>
<div class="popup">
<div class="popup-main">
<div class="popup-header">
<div class="header-content">
<span class="title">{{title}}</span>
<span class="el-icon-close close" @click="cancel"></span>
</div>
</div>
<DsLoading v-if="showDsLoading" class="popup-body"></DsLoading>
<div v-else class="popup-body">
<div class="popup-table" ref="popupTable">
<el-table :data="dataList"
:height="tableHeight"
>
<el-table-column v-for="(item, i) in tableHeader" :key="i"
:property="item.prop"
:label="item.label"
align="center"
>
</el-table-column>
</el-table>
</div>
</div>
</div>
</div>
</template>
import DsLoading from '@/components/loading/dsLoading.vue';
export default {
name:'',
data () {
return {
showDsLoading: true,
title: '标题',
pageAble: false,
confirmText:'确定',
cancelText:'取消',
dataList: [], // 数据列表
tableHeader: [], // 表头数据
tableHeight: null, // 表格高度,数据超过这个高度出现滚动条
};
},
components: {
DsLoading,
},
watch: {
dataList: {
handler: function () {
this.showDsLoading = false;
},
deep: true
}
},
mounted() {
this.$nextTick(()=>{
this.tableHeight = this.$refs.popupTable? this.$refs.popupTable.offsetHeight: 0;
})
},
methods: {
show(cb){
typeof cb === 'function' && cb.call(this,this)
return new Promise(resolve=>{
this.resolve = resolve
})
},
confirm(){
this.resolve('confirm')
this.hide()
},
cancel(){
this.resolve('cancel')
this.hide()
},
hide(){
document.body.removeChild(this.$el)
this.$destroy()
}
},
}
import Vue from 'vue'
import popUpVue from './popup.vue'
let newInstance = null
//将vue组件变为构造函数
let PopUpConstructor = Vue.extend(popUpVue)
let init = (options)=>{
console.log(options)
//实例化组件
newInstance = new PopUpConstructor()
//合并配置选项
Object.assign(newInstance,options)
//使用$mount()后 可以理解为创建虚拟的dom
document.body.appendChild(newInstance.$mount().$el)
}
let caller = (options)=>{
//options 为调用组件方法时传入的配置选项
if(!newInstance){
init(options)
}
return newInstance.show(vm =>{newInstance = null})
}
export default {
install(vue){
vue.prototype.$popUp = caller
}
}
调用
let list = [],
title = '标题';
axios.post(`/getList`, param)
.then(res=>{
if (res.data.resultCode === '000000') {
if (res.data.list && res.data.list.length > 0) {
list = res.data.list;
title = res.data.title;
}
}
}).catch(
err => reject(err)
);
let tableHeader = [{
prop: 'name',
label: '名称'
}, {
prop: 'value',
label: '值'
},],
dataList = list;
that.$popUp({
tableHeader,
dataList,
title
}).then(res=>{
console.log(res)
})
执行结果发现table数据是可以异步刷新到弹窗dom里面的,但是标题是不能更新的。
是什么原因呢,想了很久突然想到字符串与列表对象在内存中存储的方式是不一样的,字符串在内存中存的是值,而引用数据类型在内存中存储的是地址,就是说list对应的内存地址跟dataList对应的内存地址是一样的,且vue的响应式原理data中的数据dataList属性被转为 getter/setter可以被追踪刷新的,当异步数据改变list的值时,dataList也跟着变化,所以列表数据被更新到dom中,但是弹窗中的title字符串的值未变化,所以不会被刷新。
对于一些场景中可能需要调用方的数据异步更新到弹窗组件中,对于如何更新还请路过大神给出宝贵的意见
参考命令式弹窗
{{title.title}}
没问题?newInstance.xxx
都是响应式数据,直接改就行