重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
这篇文章主要讲解了Vue如何自定义render统一项目组弹框功能,内容清晰明了,对此有兴趣的小伙伴可以学习一下,相信大家阅读完之后会有帮助。
创新互联建站主要从事成都网站制作、成都做网站、外贸营销网站建设、网页设计、企业做网站、公司建网站等业务。立足成都服务綦江,10多年网站建设经验,价格优惠、服务专业,欢迎来电咨询建站服务:18980820575
一、本文收获
pick
二、为什么要统一封装弹框;
要封装成怎样
通过举例常规弹框的写法。我们可以体会到,通常要弹出一个页面,需要创建一个页面 normalDialog.vue
包裹 dialogBody.vue
(弹框主体);需要 parent.vue
设置flag控制弹框显示隐藏, normalDialog.vue
关闭的时候设置 parent.vue
对应 flag
。缺点: 流程繁杂、配置繁琐、不灵活、样式不统一和参数传递麻烦等 。如果一个项目弹框较多的时候,弊端将会更明显,大量的 isXxxDialogShow
,大量的 vue
文件。因此项目组急需一个能简单配置就能弹出弹框的 API
。
1. 常规弹框写法 dialoBody.vue
(弹框主体),此处采用 Composition API
的写法。只做了简单的页面,包含校验,抽取保存数据的常规逻辑。
名称
2.normalDialog.vue
包裹弹框主体 dialoBody.vue
parent.vue
// html 部分// Js部分 data(){ isNormalDialogShow:false } methods:{ onDialogShow(){ // ******控制弹框显示***** this.isNormalDialogShow = true } }
2. 要封装成怎样
2.1 API诉求:
isXxxDialogShow el-dialog
2.2 理想API:
import dialogBody from './dialogBody.vue' const dialog = new JSDialog({ comonent: dialogBody, dialogOpts: { // 可扩展配置 title: 'JSDialog设置的弹框标题', width: '400px' }, props: { defaultName: 'JSDialog传递的参数', }, onOK() { const inner = dialog.getInner() // 能取到dialogBody的引用 // 控制流程 if (inner.apiMethods.isCanSave()) { // 获取保存数据 const postData = inner.apiMethods.getSaveData() console.log('>>>>> postData >>>>>', postData) // 关闭弹框 dialog.close() } }, onCancel() { dialog.close() // 弹框关闭 }, }) dialog.show() // 弹框显示
三、如何封装
动态控制显示内容,脑海浮现的三个方案: 卡槽、动态组件和重写 render。下面在动态弹框场景下简单对比三个方案。
1. 整体代码
先整体预览一下代码,下面再细分讲解。
import Vue from 'vue' import merge from 'lodash/merge' import orderBy from 'lodash/orderBy' // 按钮配置项构造器 function btnBuilder(options) { const defaultBtn = { text: '按钮', // 显示文本 clickFn: null, // 点击回调 type: 'default', // 样式 isHide: false, // 是否隐藏 order: 2 // 顺序 } return { ...defaultBtn, ...options } } export default class JSDialog { constructor(originOptions) { this.options = {} this.vm = null this._mergeOptions(originOptions) this._initVm() } // 参数合并 _mergeOptions(originOptions) { const defaultOptions = { component: '', // 弹框主体vue页面 // 可扩展el-dialog官方api所有配置项,小驼峰aaaBbbCcc dialogOpts: { width: '40%', title: '默认标题' }, // 传入弹框主体vue组件的参数 props: {}, // 点击确定回调 onOK: () => { console.log('JSDialog default OK'), this.close() }, // 点击取消回调 onCancel: () => { console.log('JSDialog default cancel'), this.close() }, footer: { ok: btnBuilder({ text: '确定', type: 'primary', order: 0 }), cancel: btnBuilder({ text: '取消', order: 1 }) } } // 参数合并到this.options merge(this.options, defaultOptions, originOptions) const footer = this.options.footer Object.entries(footer).forEach(([key, btnOptions]) => { // 确定和取消默认按钮 if (['ok', 'cancel'].includes(key)) { const clickFn = key === 'ok' ? this.options.onOK : this.options.onCancel // 默认按钮回调优先级: footer配置的clickFn > options配置的onOK和onCancel btnOptions.clickFn = btnOptions.clickFn || clickFn } else { // 新增按钮 // 完善配置 footer[key] = btnBuilder(btnOptions) } }) } _initVm() { const options = this.options const beforeClose = this.options.footer.cancel.clickFn // 弹框右上角关闭按钮回调 this.vm = new Vue({ data() { return { // 需要响应式的数据 footer: options.footer, // 底部按钮 visible: false // 弹框显示及关闭 } }, methods: { show() { // 弹框显示 this.visible = true }, close() { // 弹框关闭 this.visible = false }, clearVm() { // 清除vm实例 this.$destroy() } }, mounted() { // 挂载到body上 document.body.appendChild(this.$el) }, destroyed() { // 从body上移除 document.body.removeChild(this.$el) }, render(createElement) { // 弹框主体 const inner = createElement(options.component, { props: options.props, // 传递参数 ref: 'inner' // 引用 }) // 控制按钮显示隐藏 const showBtns = Object.values(this.footer).filter(btn => !btn.isHide) // 控制按钮顺序 const sortBtns = orderBy(showBtns, ['order'], ['desc']) // 底部按钮 jsx 写法 const footer = ({sortBtns.map(btn => () // 弹框主体 const elDialog = createElement( 'el-dialog', { // el-dialog 配置项 props: { ...options.dialogOpts, visible: this.visible, beforeClose }, // **** 看这里,visible置为false后,el-dialog销毁后回调 ***** on: { closed: this.clearVm }, ref: 'elDialog' }, // 弹框内容:弹框主体和按钮 [inner, footer] ) return elDialog } }).$mount() } // 封装API // 关闭弹框 close() { this.vm.close() } // 显示弹框 show() { this.vm.show() } // 获取弹框主体实例,可访问实例上的方法 getInner() { return this.vm.$refs.inner } }{btn.text} ))}
2. 参数合并
要做到 API 诉求中的:调用简单、传参简便和可扩展控制弹框样式。参数合并便是 成本最小的实现方案,配合 TS 效果更佳。定义默认参数,通过 lodash 的 merge ,合并深层属性。通过参数合并还能做到自定义 footer 按钮,控制文本,样式,顺序和执行回调。
// 参数合并 _mergeOptions(originOptions) { const defaultOptions = { component: '', // 弹框主体vue页面 // 可扩展el-dialog官方api所有配置项,小驼峰aaaBbbCcc dialogOpts: { width: '40%', title: '默认标题' }, // 传入弹框主体vue组件的参数 props: {}, // 点击确定回调 onOK: () => { console.log('JSDialog default OK'), this.close() }, // 点击取消回调 onCancel: () => { console.log('JSDialog default cancel'), this.close() }, footer: { ok: btnBuilder({ text: '确定', type: 'primary', order: 0 }), cancel: btnBuilder({ text: '取消', order: 1 }) } } // 参数合并到this.options merge(this.options, defaultOptions, originOptions) const footer = this.options.footer Object.entries(footer).forEach(([key, btnOptions]) => { // 确定和取消默认按钮 if (['ok', 'cancel'].includes(key)) { const clickFn = key === 'ok' ? this.options.onOK : this.options.onCancel // 默认按钮回调优先级: footer配置的clickFn > options配置的onOK和onCancel btnOptions.clickFn = btnOptions.clickFn || clickFn } else { // 新增按钮 // 完善配置 footer[key] = btnBuilder(btnOptions) } }) }
3. render函数
摘取一段 渲染函数 & JSX 官方文档关于 render 的描述: Vue 推荐在绝大多数情况下使用模板来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力。这时你可以用 渲染函数,它比模板更接近编译器。 官方文档对渲染函数的写法,参数,对应JSX写法介绍已经很详细,这里就不再赘述。下面代码是在最新vue-cli创建项目上运行的,尝试了JS参数创建元素和JSX创建元素两种写法。
render(createElement) { // 弹框主体 const inner = createElement(options.component, { props: options.props, // 传递参数 ref: 'inner' // 引用 }) // 控制按钮显示隐藏 const showBtns = Object.values(this.footer).filter(btn => !btn.isHide) // 控制按钮顺序 const sortBtns = orderBy(showBtns, ['order'], ['desc']) // 底部按钮 jsx 写法 const footer = ({sortBtns.map(btn => () // 弹框主体 const elDialog = createElement( 'el-dialog', { // el-dialog 配置项 props: { ...options.dialogOpts, visible: this.visible }, on: { closed: this.clearVm }, ref: 'elDialog' }, // 弹框内容:弹框主体和按钮 [inner, footer] ) return elDialog }{btn.text} ))}
4. 封装API
暂时只封装了三个 API ,可根据不同的场景扩展 API ,比如弹框不销毁隐藏,弹框刷新等。
show() ,弹框显示
显示主要是修改 el-dialog 的 visible 为 true ,控制挂载到 body 上的弹框显示。
show() { this.vm.show() }
close() ,弹框关闭
关闭处理流程:修改 el-dialog 的 visible 为 false ;触发 el-dialog 的 closed 事件;执行 clearVm ;执行 vm 的 $destroy() ; destroyed() 回调中将 $el 从 body 中移除。
close() { this.vm.close() }
getInner() ,获取弹框主体实例,可用于访问实例上的方法,控制按钮流程
getInner() { return this.vm.$refs.inner }
四、如何使用
1. 最简单场景,只配置页面
按钮事件回调采用默认的回调,确定和取消按钮都可关闭弹框
import dialogBody from './renderJsx/dialogBody' const dialog = new JSDialog({ component: dialogBody, }) dialog.show() // 弹框显示
效果如下:
2. 控制弹框样式及确定流程
可自定义el-dialog支持的配置项,见 Dialog 对话框 ;比如:title、 customClass 。通过customClass可统一控制项目内弹框的风格;可控制确定取消按钮代码回调。
import dialogBody from './renderJsx/dialogBody' const dialog = new JSDialog({ component: dialogBody, dialogOpts: { title: '靓仔,美女欧嗨呦', customClass:'js-dialog' }, props: { defaultName: 'JSDialog传递的参数' }, onOK() { const inner = dialog.getInner() // 能取到dialogBody的引用 // 控制流程 if (inner.apiMethods.isCanSave()) { // 获取保存数据 const postData = inner.apiMethods.getSaveData() console.log('>>>>> postData >>>>>', postData) // 关闭弹框 dialog.close() } }, onCancel() { dialog.close() // 弹框关闭 } })
效果如下:
3. 自定义footer
自定义按钮可控制执行回调,样式,顺序,显示与隐藏
import dialogBody from './renderJsx/dialogBody' const dialog = new JSDialog({ component: dialogBody, footer: { ok: { // 修改默认按钮 text: '新增' }, cancel: { // 隐藏默认按钮 isHide: true }, add: { // 新增按钮 text: '另存为', clickFn() { dialog.close() }, order: -1 // 控制按钮顺序,order小的显示在右边 }, add2: { text: '新增按钮2', clickFn() { dialog.close() }, order: 3 } } }) dialog.show() // 弹框显示
效果如下:
看完上述内容,是不是对Vue如何自定义render统一项目组弹框功能有进一步的了解,如果还想学习更多内容,欢迎关注创新互联行业资讯频道。