简介
在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。
弹窗几乎是项目中比不可少的一部分,它就是经典的异步组件需求。
同时,它也是动态的,需要的时候通过方法来调用执行。
调用示例:
import importAsyncComponent from '@/util.component'
const DialogComp = importAsyncComponent('./components/DialogComp.vue')
// 选项
const options = {
props: {}
events: {}
}
this.$dialog(DialogComp, options).then(() => {
// todo something
})
准备
Vue 官方也提供了相关的方法来实现此功能:
// Vue2
const AsyncComp = () => import('./components/MyComponent.vue')
// Vue3
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
import('./components/MyComponent.vue')
)
[import
] 方法会返回一个Promise, 因此可以在事件钩子里进行拦截,增加loading效果以及出错提示。
安装nprogress
npm install --save nprogress
封装引用方法
importAsyncComponent函数统一对引入组件进行处理
以ant-design-vue组件库来做示例
// util.component.js
import { notification } from 'element-plus';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
NProgress.configure({
easing: 'ease', // 动画方式
speed: 500, // 递增进度条的速度
showSpinner: false, // 是否显示加载ico
trickleSpeed: 200, // 自动递增间隔
minimum: 0.3 // 初始化时的最小百分比
})
function importAsyncComponent(path) {
NProgress.start()
return import(path).then(res => {
return res
})
.catch(err => {
notification.open({
message: '提示',
description: '加载失败,请检查你的网络...'
})
})
.finally(() => {
NProgress.done()
})
}
export default importAsyncComponent
封装弹窗函数
上一步已经有了加载组件的函数,现在就差实现$dialog函数了
// util.dialog.js
import Vue from 'vue'
/**
* 创建弹窗
* @params {Object|Promise} component 弹窗组件
* @params {Object} options 选项
*/
function createDialogComp(component, options) {
const { props = {}, events = {} } = options || {}
const comp = Vue.extend(component, {
propsData: props,
watch: {
// 预设弹出显隐属性
visible(nv) {
if(!nv) {
// 监听关闭时销毁
this.destroy()
}
}
},
methods: {
destroy() {
this.$destroy()
document.body.removeChild(this.$el)
}
}
})
const vm = new Vue(comp).$mount()
// 注册事件
Object.entries(events).forEach(([name, event]) => {
vm.$on(name, event)
})
// 挂载
document.body.appendChild(vm.$el)
// 打开
vm.$nextTick(() => {
vm.open() // 通过调用弹窗组件预设的open方法开弹出
})
return vm
}
export default {
install(Vue) {
Vue.prototype.$dialog = function dialog(component, options) {
if(component instanceof Promise) {
return component.then(res => {
return createDialogComp(res, options)
})
}
return createDialogComp(component, options)
}
}
}
绑定全局
弹窗开发完成,如何绑定全局使用?
// main.js
import Vue from 'vue'
import dialog from '@/util.dialog.js'
Vue.use(dialog)
编写弹窗
至此,弹窗支撑逻辑已经全部完成,下面需要编写一个弹窗组件。
以element UI组件库为例:
<template>
<div>
<el-modal :visible="visible" title="My Dialog">
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</el-modal>
</div>
</template>
<script>
export default {
data() {
return {
visible: false // 该属性控制弹窗弹出
}
},
methods: {
open() {
this.visible = true
},
close() {
this.visible = false
}
}
}
</script>
调用
最后,在父级组件里动态的加载上一步的弹窗组件。
<template>
<div>
<el-button type="primary" @click="openDialog">Open Dialog</el-button>
</div>
</template>
<script>
import importAsyncComponent from '@/utils/util.component'
const MyDialog = importAsyncComponent('./components/MyDialog.vue')
export default {
data() {
return {
}
},
methods: {
openDialog() {
// 选项
const options = {
props: {}
events: {}
}
this.$dialog(MyDialog, options).then(() => {
// todo something
})
}
}
}
</script>