歡迎您光臨本站 註冊首頁

Vue自定義render統一項目組彈框功能

←手機掃碼閱讀     f2h0b53ohn @ 2020-06-11 , reply:0

一、本文收穫

pick

二、為什麼要統一封裝彈框;

要封裝成怎樣

通過舉例常規彈框的寫法。我們可以體會到,通常要彈出一個頁面,需要創建一個頁面 normalDialog.vue 包裹 dialogBody.vue (彈框主體);需要 parent.vue 設置flag控制彈框顯示隱藏, normalDialog.vue 關閉的時候設置 parent.vue 對應 flag 。缺點: 流程繁雜、配置繁瑣、不靈活、樣式不統一和參數傳遞麻煩等 。如果一個項目彈框較多的時候,弊端將會更明顯,大量的 isXxxDialogShow ,大量的 vue 文件。因此項目組急需一個能簡單配置就能彈出彈框的 API

1. 常規彈框寫法 dialoBody.vue (彈框主體) ,此處採用 Composition API 的寫法。只做了簡單的頁面,包含校驗,抽取保存數據的常規邏輯。

  名稱{ // 相關邏輯 }的形式定義   * 好處1: onXxx定義的位置和相關業務邏輯代碼關聯一起   * 好處2: 可以統一通過...methods的形式在setup統一解構   * 好處3: 當頁面邏輯複雜,需要操作的數據關聯性強,不可拆解組件;   *  可將相關業務的代碼在獨立模塊定義;   *  獨立模塊暴露API handleXxx(methods,state),流水線加工methods;   *  和Vue2源碼一樣,流水線加工的思想.   */   const methods = {}   // 校驗名稱   methods.onNameBlur = () => {}     // ************************ 向外暴露的API ************************   const apiMethods = {   // 保存前校驗   isCanSave() {   if (state.attention !== ATTENTIONED || state.like !== LIKED) {    Message.error('未關注或者點贊,不能關閉,嘻嘻')    return false   }   return true   },   // 獲取保存數據   getSaveData() {   // ******* lodash pick 從對象中抽取數據   return pick(state, ['name', 'attention', 'like'])   },   }   return {   ...toRefs(state),   ...methods,   apiMethods,   }   },  }" _ue_custom_node_="true">

 

2.normalDialog.vue 包裹彈框主體 dialoBody.vue

     取 消確 定   >>>> postData >>>>>', postData)   // 保存成功後關閉彈框   this.onClose()   }   },   },  }" _ue_custom_node_="true">

 

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 。下面在動態彈框場景下簡單對比三個方案。

  • slot(卡槽) ,和 el-dialog 原理類似,只是再封裝了一層,少定義了 normalDialog.vue 文件。 缺點:調用複雜,不靈活;不容易控制關閉的流程;只能在 template 中定義 。

  • component(動態組件) ,創建 commonDialog.vue ,統一掛在 App.vue 下,利用

    動態切換彈框主體, commonDialog.vue 監聽 componentId 變化來切換彈框主體。 缺點:要提前將所有彈框主體組件註冊到commonDialog.vue頁面的components上;依賴於vuex,侵入性較強;純js文件通過vuex彈出彈框相對複雜,不靈活 。

  • 重寫 render , render 是 Vue 對造輪子開發者開放的後門。動態彈框可作為獨立的功能模塊,內部通過new Vue ,重寫 render 控制渲染內容。 獨立 Vue 實例,可預先創建,可在任何位置控制彈框,靈活,清晰 。 缺點:暫無

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 => ({btn.text}))})   // 彈框主體   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   }  }

 

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 => ({btn.text}))})   // 彈框主體   const elDialog = createElement(   'el-dialog',   {   // el-dialog 配置項   props: {   ...options.dialogOpts,   visible: this.visible   },   on: {   closed: this.clearVm   },   ref: 'elDialog'   },   // 彈框內容:彈框主體和按鈕   [inner, footer]   )   return elDialog  }

 

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() // 彈框顯示

 

效果如下:

        

   


[f2h0b53ohn ] Vue自定義render統一項目組彈框功能已經有249次圍觀

http://coctec.com/docs/vue-js/show-post-238062.html