重慶分公司,新征程啟航
為企業提供網站建設、域名注冊、服務器等服務
為企業提供網站建設、域名注冊、服務器等服務
這篇文章主要講解了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統一項目組彈框功能有進一步的了解,如果還想學習更多內容,歡迎關注創新互聯行業資訊頻道。