ElementUI的Dialog和MessageBox的默認屬性修改方法

本文主要講下如何修改ElementUI的Dialog和MessageBox的默認屬性。主要包括如下幾個方面:vue

  1. Dialog和MessageBox簡介;
  2. 修改單次調用的默認屬性;
  3. 全局修改默認屬性:
    3.1 Dialog全局修改默認屬性;
    3.2 MessageBox全局修改默認屬性。

如下例子以修改close-on-click-modal屬性爲例進行講解。這個屬性的默認值是true,也就是點擊蒙層的時候彈框關閉。在實際項目中,咱們通常不想要這個默認行爲,因此拿這個屬性舉例。git

Dialog和MessageBox簡介

Dialog是一個組件Component
MessageBox這個名字雖然咋一看也像是組件名,可是倒是一個方法名
我試着猜想了下MessageBox沒有作成組件的緣由。根據官網的介紹:github

A set of modal boxes simulating system message box, mainly for alerting information, confirm operations and prompting messages.

MessageBox主要是爲了模仿系統原生的彈框,而系統原生彈框的API是window.alertwindow.confirmwindow.prompt,這些都是方法。因此,多是爲了和原生API保持一致的使用方式,因此把MessageBox作成了對應的方法:MessageBox.alert,MessageBox.confirm和MessageBox.prompt。此外,還有一個基礎的MessageBox方法也能夠調用。這四個方法也掛載到了Vue原型上,分別是$alert,$confirm,$prompt和$msgbox:element-ui

原生API MessageBox方法名 Vue實例方法名
- MessageBox $msgbox
window.alert MessageBox.alert $alert
window.confirm MessageBox.confirm $confirm
window.prompt MessageBox.prompt $prompt

正由於Dialog和MessageBox本質是不同的,因此修改默認屬性的方式也就不同了,下面會針對這兩種類型分別進行說明。函數

修改單次調用的默認屬性

修改單次調用的默認屬性就比較簡單了,Dialog組件經過屬性的方式修改:close-on-click-modal測試

<el-dialog
  ...
  :close-on-click-modal="false">
  ...
</el-dialog>

MessageBox經過傳參的方式修改:ui

// $alert, $confirm, $prompt方法與下面相似
this.$msgbox({
  ...
  closeOnClickModal: false
})

全局修改默認屬性

在這裏,首先說下我解決這個問題的思路:this

  1. 直接搜索,大部分問題都能搜到解決方案;
  2. 搜不到或者別人的方法不適用時,查看源碼。

我有一個習慣,若是開發時間還算寬裕的話,即便搜到了現成的解決方案,我仍是會對照源碼探索下爲何這個方案是可行的。下面的解決方案都對照源碼驗證過,因此就直接對照源碼講解決方案以及對應源碼的位置。.net

本文涉及到的源碼以v2.15.1版本爲例。prototype

Dialog全局修改默認屬性

Dialog組件源碼位於/packages/dialog文件夾下面github地址
查看/packages/dialog/component.vue文件會發現closeOnClickModal是一個props:

props: {
  // ...
  closeOnClickModal: {
    type: Boolean,
    default: true // 默認值是true
  },
  // ...
}

全局修改方式以下:

import Element from 'element-ui'
Element.Dialog.props.closeOnClickModal.default = false

MessageBox全局修改默認屬性

首先看下/src/index.js文件中和MessageBox相關的代碼:

// ...
import MessageBox from '../packages/message-box/index.js';
// ...
const install = function(Vue, opts = {}) {
  // ...
  Vue.prototype.$msgbox = MessageBox; // 對應於上面表格中的對應關係:MessageBox => $msgbox
  Vue.prototype.$alert = MessageBox.alert; // 對應於上面表格中的對應關係:MessageBox.alert => $alert
  Vue.prototype.$confirm = MessageBox.confirm; // 對應於上面表格中的對應關係:Message.confirm => $confirm
  Vue.prototype.$prompt = MessageBox.prompt; // 對應於上面表格中的對應關係:MessageBox.prompt => $prompt
  // ...
}
export default {
  // ...
  MessageBox,
  // ...
}

接下來看下MessageBox的定義文件/packages/message-box/src/main.js,MessageBox接受兩個參數:optionscallback,字面含義應該就是配置對象和回調函數:

const MessageBox = function(options, callback) {
  // ...
};

我使用了$msgbox,因此個人第一思路是修改$msgbox方法,調用MessageBox的時候添加自定義參數:

import Element from 'element-ui' 
import Vue from 'vue'

Vue.prototype.$msgbox = function (options, ...args) {
  options = Object.assign({
    closeOnClickModal: false
  }, options)
  Element.MessageBox(options, ...args)
}

測試了下,沒有問題,可是仔細看了下源碼,發現options並不必定是對象,也能夠是字符串:

const MessageBox = function(options, callback) {
  if (Vue.prototype.$isServer) return;
  if (typeof options === 'string' || isVNode(options)) { // 第一個參數是字符串的時候,看成message
    options = {
      message: options
    };
    if (typeof arguments[1] === 'string') { // 第二個參數是字符串的時候,看成title
      options.title = arguments[1];
    }
  } else if (options.callback && !callback) {
    callback = options.callback;
  }
  // ...
}

我測試沒有問題,是由於我調用的時候options都是當對象使用的。因此這個方案是有限定範圍的解決方案,並非通用的。主要有如下問題:

  1. options必須是對象的形式;
  2. 若是使用了$alert$confirm$prompt等方法,都須要作相似的處理,代碼比較囉嗦;
  3. 只能經過$msgbox的方式修改,若是你是直接調用的MessageBox,那就不適用了,由於我並無修改MessageBox

基於問題1,你能夠像MessageBox源碼裏面分析參數那樣,建立一個合適的options,而後傳給MessageBox。基於問題3,暫時沒有想到好的解決方案。

考慮到項目是團隊合做的結果,萬一有同窗調用$msgbox的時候第一個參數不是對象,或者直接經過MessageBox調用,我就在考慮是否有更好的解決方案。仔細查看源碼,發現一個更簡單的API:

MessageBox.setDefaults = defaults => {
  MessageBox.defaults = defaults;
};

再從新看下MessageBox函數的源碼:

const MessageBox = function(options, callback) {
  // ...
  if (typeof Promise !== 'undefined') {
    return new Promise((resolve, reject) => { // eslint-disable-line
      msgQueue.push({
        options: merge({}, defaults, MessageBox.defaults, options), // 注意這一行
        callback: callback,
        resolve: resolve,
        reject: reject
      });

      showNextMsg();
    });
  } else {
    msgQueue.push({
      options: merge({}, defaults, MessageBox.defaults, options), // 注意這一行
      callback: callback
    });

    showNextMsg();
  }
};

經過上面最終options參數的計算方法,咱們能夠給options排下優先級:

函數調用時傳入的options > MessageBox.defaults > 組件默認值options

因此,修改MessageBox.defaults能夠實現咱們的目的:

import Element from 'element-ui'

Element.MessageBox.setDefaults({
  closeOnClickModal: false
})

固然,該方法也並非十分完美的,問題以下:

  1. 未在官方文檔中發現該API的介紹,這種非官方API嚴重依賴當前使用的版本,官方應該在後續迭代中不保證這種API的穩定性和正確性;
  2. MessageBox.alert在調用Message的時候,默認覆蓋了幾個默認屬性,這幾個屬性經過MessageBox.setDefaults方法修改不生效,好比設置alert框的closeOnClickModal爲true:

    MessageBox.alert = (message, title, options) => {
      // ...
      return MessageBox(merge({
        // ...
        closeOnPressEscape: false,
        closeOnClickModal: false // 調用MessageBox時傳入的options會覆蓋MessageBox.defaults
      }, options));
    };

探索到這裏,對於我來講,MessageBox.setDefaults已經算是比較優的解決方案了,因此就再也不繼續探索了。
若是你還有更高的要求,好比修復問題2,簡單點,你能夠覆蓋這個默認方法或者探索更適合你的解決方案。

總結

但願你們能有所收穫。若有錯誤,歡迎留言討論。

參考文檔

  1. ElementUI 如何全局配置組件默認屬性
  2. 全局修改elementUI的$message默認顯示時間的方法
相關文章
相關標籤/搜索