背景
在項目中使用對話框的通常做法是把對話框封裝成組件,在使用的地方引入,然后添加到template,使用visible.sync控制對話框的顯示/隱藏,監聽confirm事件處理用戶點擊確定。如下:
1 <confirm-dialog 2 v-if="confirmDialogVisible" 3 :title="$t(`mineData.tips.deleteDataset`)" 4 :visible.sync="confirmDialogVisible" 5 @confirm="confimHandler" 6 ></confirm-dialog>
在封裝的dialog內部也需要在關閉時更新visible,確定時觸發confirm事件:
1 methods: { 2 close() { 3 this.$emit("update:visible", false); 4 }, 5 confirm() { 6 this.close(); 7 this.$emit("confirm"); 8 } 9 }
這樣的做法不僅僅導致頁面初始化時引入所有對話框組件而影響加載速度,更頭疼的是頁面中引入了很多對話框時,會導致頁面很雜亂:需要為每個對話框插入一段html,為每個對話框維護一個單獨的visible變量,為每個對話框添加confirm事件監聽...
而這些操作大部分是和業務無關的,且這些操作又是極其相似的。
那么,有沒有通過js動態創建dialog的方法呢?
1 createDialog("confirm-dialog.vue");
就像上面這樣根據文件名即可打開對話框,不用定義visible及添加一堆html和事件回調,甚至不需要先引入對話框組件!
是不是很簡單!心動了吧?看下去吧。
實現
1.封裝的/utils/dialogControl.js
1 import Vue from 'vue' 2 async function createDialog (fileName, data) { 3 const dialogsContext = require.context( 4 '../components', // 定義查找文件的范圍 5 true, 6 /([a-zA-Z\-0-9]+)\.vue$/, // 定義文件名規則 7 'lazy' 8 ) 9 // 查找到傳入名字的文件並加載該文件 10 let match = dialogsContext.keys().find((key) => key.includes(fileName)) 11 if (!match) return 12 let componentContext = await dialogsContext(match) 13 let temp = componentContext.default 14 return new Promise(function (resolve, reject) { 15 // 初始化配置參數 16 let opt = { 17 data 18 } 19 let component = Object.assign({}, temp) 20 let initData = { 21 visible: true 22 } 23 Object.assign(initData, component.data()) 24 opt.data && Object.assign(initData, JSON.parse(JSON.stringify(opt.data))) 25 component.data = function () { 26 return initData 27 } 28 // 創建構造器創建實例掛載 29 let DialogC = Vue.extend(component) 30 let dialog = new DialogC() 31 // 關閉事件 32 let _onClose = dialog.$options.methods.onClose 33 dialog.onClose = function () { 34 resolve() 35 dialog.$destroy() 36 _onClose && _onClose.call(dialog) 37 document.body.removeChild(dialog.$el) 38 } 39 // 回調事件 40 let _onCallback = dialog.$options.methods.onCallback 41 dialog.onCallback = function (...arg) { 42 try { 43 _onCallback && _onCallback() 44 resolve(arg) 45 dialog.$destroy() 46 _onClose && _onClose.call(dialog) 47 document.body.removeChild(dialog.$el) 48 } catch (e) { 49 console.log(e) 50 } 51 } 52 dialog.$mount() 53 // 點擊關閉按鈕時會改變visible 54 dialog.$watch('visible', function (n, o) { 55 dialog === false && dialog.onClose() 56 }) 57 document.body.appendChild(dialog.$el) 58 }) 59 } 60 61 export { createDialog }
說明:
1.需要指定查找文件的路徑及匹配名稱的正則表達式,這樣能過濾掉一些不需要的文件
2.接收一個fileName參數用於匹配要打開的對話框文件,data參數是傳遞給對話框的數據,會合並到組件的data中
3.使用visible變量控制對話框的顯示/隱藏
4.定義了一個onClose方法用於關閉對話框,對話框中可以使用該方法進行關閉
5.onCallback方法用於向調用對話框的父組件傳值,如點擊確定按鈕時向父組件傳值
2.dialog文件定義
如/components/ConfirmDialog.vue,使用visible變量控制顯示/隱藏,onClose處理關閉事件,確定按鈕的回調是onCallback(和dialogControl.js中的定義一致)。
另外,一定要有data屬性
1 <template> 2 <el-dialog title="提示" :visible.sync="visible" width="30%"> 3 <span>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Nesciunt quis 4 perspiciatis fugiat molestiae provident accusantium repudiandae fugit 5 minima, eaque, repellat quibusdam iste sed ad? Debitis qui praesentium 6 minus incidunt esse!</span> 7 <span slot="footer" class="dialog-footer"> 8 <el-button @click="onClose">取 消</el-button> 9 <el-button type="primary" @click="onCallback(true)">確 定</el-button> 10 </span> 11 </el-dialog> 12 </template> 13 14 <script> 15 export default { 16 data () { 17 return {} 18 }, 19 methods: { 20 }} 21 </script>
3.使用
引入dialogControl中的createDialog方法,直接傳入文件名稱即可打開。
如果有其他的屬性,則以鍵值對的形式放入第二個參數,這些屬性會合並到對話框組件的data中,因此對話框組件中可以直接使用這些屬性。
createDialog方法得到一個promise對象,其then方法能得到confirm返回的結果。
1 <template> 2 <div> 3 <h1>This is an show page</h1> 4 <el-button type="primary" @click="openDialog">打開</el-button> 5 </div> 6 </template> 7 8 <script> 9 import { createDialog } from "@/utils/dialogControl"; 10 export default { 11 methods: { 12 openDialog() { 13 let dialog = createDialog("confirm-dialog.vue"); 14 dialog.then((v) => { 15 if (v) { 16 console.info("確定"); 17 } 18 }); 19 }, 20 }, 21 }; 22 </script>
效果如下:
如果你還在使用文章開始的方式調用對話框,那么趕緊把這個方法用起來吧!
補充:
如果需要在對話框中使用多語言的插件vue-i18n時,需要像vue實例那樣在new時添加,
i18n.js
1 import Vue from "vue"; 2 import VueI18n from "vue-i18n"; 3 4 Vue.use(VueI18n); 5 6 const messages = { 7 en: { 8 message: { 9 haha: "yi ge xc mz nv. ha ha h..." 10 } 11 } 12 }; 13 14 export const i18n = new VueI18n({ 15 locale: "en", 16 messages: messages 17 });
dialogControl.js
1 import { i18n } from "../i18n" 2 3 ... 4 5 let dialog = new DialogC({ 6 i18n 7 }) 8 9 ...
對話框就可以使用$.t("message.haha")了。
參考:
https://www.freesion.com/article/43311065748/