日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

詳解如何在vue+element-ui的項目中封裝dialog組件

瀏覽:157日期:2022-10-20 08:02:39
1、問題起源

由于 Vue 基于組件化的設(shè)計,得益于這個思想,我們在 Vue 的項目中可以通過封裝組件提高代碼的復(fù)用性。根據(jù)我目前的使用心得,知道 Vue 拆分組件至少有兩個優(yōu)點:

1、代碼復(fù)用。

2、代碼拆分

在基于 element-ui 開發(fā)的項目中,可能我們要寫出一個類似的調(diào)度彈窗功能,很容易編寫出以下代碼:

<template> <div> <el-dialog :visible.sync='cnMapVisible'>我是中國地圖的彈窗</el-dialog> <el-dialog :visible.sync='usaMapVisible'>我是美國地圖的彈窗</el-dialog> <el-dialog :visible.sync='ukMapVisible'>我是英國地圖的彈窗</el-dialog> <el-button @click='openChina'>打開中國地圖</el-button> <el-button @click='openUSA'>打開美國地圖</el-button> <el-button @click='openUK'>打開英國地圖</el-button> </div></template><script>export default { name: 'View', data() { return { // 對百度地圖和谷歌地圖的一些業(yè)務(wù)處理代碼 省略 cnMapVisible: false, usaMapVisible: false, ukMapVisible: false, }; }, methods: { // 對百度地圖和谷歌地圖的一些業(yè)務(wù)處理代碼 省略 openChina() {}, openUSA() {}, openUK() {}, },};</script>

上述代碼存在的問題非常多,首先當(dāng)我們的彈窗越來越多的時候,我們會發(fā)現(xiàn)此時需要定義越來越多的變量去控制這個彈窗的顯示或者隱藏。

由于當(dāng)我們的彈窗的內(nèi)部還有業(yè)務(wù)邏輯需要處理,那么此時會有相當(dāng)多的業(yè)務(wù)處理代碼夾雜在一起(比如我調(diào)用中國地圖我需要用高德地圖或者百度地圖,而調(diào)用美國、英國地圖我只能用谷歌地圖,這會使得兩套業(yè)務(wù)邏輯分別位于一個文件,嚴(yán)重加大了業(yè)務(wù)的耦合度)

我們按照分離業(yè)務(wù),降低耦合度的原則,將代碼按以下思路進行拆分:

1、View.vue

<template> <div> <china-map-dialog ref='china'></china-map-dialog> <usa-map-dialog ref='usa'></usa-map-dialog> <uk-map-dialog ref='uk'></uk-map-dialog> <el-button @click='openChina'>打開中國地圖</el-button> <el-button @click='openUSA'>打開美國地圖</el-button> <el-button @click='openUK'>打開英國地圖</el-button> </div></template><script>export default { name: 'View', data() { return { /** 將地圖的業(yè)務(wù)全部抽離到對應(yīng)的dialog里面去,View只存放調(diào)度業(yè)務(wù)代碼 */ }; }, methods: { openChina() { this.$refs.china && this.$refs.china.openDialog(); }, openUSA() { this.$refs.usa && this.$refs.usa.openDialog(); }, openUK() { this.$refs.uk && this.$refs.uk.openDialog(); }, },};</script>

2、ChinaMapDialog.vue

<template> <div> <el-dialog :visible.sync='baiduMapVisible'>我是中國地圖的彈窗</el-dialog> </div></template><script>export default { name: 'ChinaMapDialog', data() { return { // 對中國地圖業(yè)務(wù)邏輯的封裝處理 省略 baiduMapVisible: false, }; }, methods: { // 對百度地圖和谷歌地圖的一些業(yè)務(wù)處理代碼 省略 openDialog() { this.baiduMapVisible = true; }, closeDialog() { this.baiduMapVisible = false; }, },};</script>

3、由于此處僅僅展示偽代碼,且和 ChinaMapDialog.vue 表達(dá)的含義一致, 為避免篇幅過長 USAMapDialog.vue 和 UKMapDialog.vue 已省略

2、問題分析

我們通過對這幾個彈窗的分析,對剛才的設(shè)計進行抽象發(fā)現(xiàn),這里面都有一個共同的部分,那就是我們對 dialog 的操作代碼都是可以重用的代碼,如果我們能夠編寫出一個抽象的彈窗,然后在恰當(dāng)?shù)臅r候?qū)⑵浜蜆I(yè)務(wù)代碼進行組合,就可以實現(xiàn) 1+1=2 的效果。

3、設(shè)計

由于 Vue 在不改變默認(rèn)的 mixin 原則(默認(rèn)也最好不要改變,可能會給后來的維護人員帶來困惑)的情況下,如果在混入過程中發(fā)生了命名沖突,默認(rèn)會將方法合并(數(shù)據(jù)對象在內(nèi)部會進行遞歸合并,并在發(fā)生沖突時以組件數(shù)據(jù)優(yōu)先),因此,mixin 無法改寫本來的實現(xiàn),而我們期望的是,父類提供一個比較抽象的實現(xiàn),子類繼承父類,若子類需要改表這個行為,子類可以重寫父類的方法(多態(tài)的一種實現(xiàn))。

因此我們決定使用 vue-class-component 這個庫,以類的形式來編寫這個抽象彈窗。

import Vue from 'vue';import Component from 'vue-class-component';@Component({ name: 'AbstractDialog',})export default class AbstractDialog extends Vue {}3.1 事件處理

查看 Element-UI 的官方網(wǎng)站,我們發(fā)現(xiàn) ElDialog 對外拋出 4 個事件,因此,我們需要預(yù)先接管這 4 個事件。因此需要在我們的抽象彈窗里預(yù)設(shè)這個 4 個事件的 handler(因為對于組件的行為的劃分,而對于彈窗的處理本來就應(yīng)該從屬于彈窗本身,因此我并沒有通過$listeners 去穿透外部調(diào)用時的監(jiān)聽方法)

import Vue from 'vue';import Component from 'vue-class-component';@Component({ name: 'AbstractDialog',})export default class AbstractDialog extends Vue { open() { console.log('彈窗打開,我啥也不做'); } close() { console.log('彈窗關(guān)閉,我啥也不做'); } opened() { console.log('彈窗打開,我啥也不做'); } closed() { console.log('彈窗關(guān)閉,我啥也不做'); }}3.2 屬性處理

dialog 有很多屬性,默認(rèn)我們只需要關(guān)注的是 before-close 和 title 兩者,因為這兩個屬性從職責(zé)上劃分是從屬于彈窗本身的行為,所以我們會在抽象彈窗里面處理開關(guān)和 title 的任務(wù)

import Vue from 'vue';import Component from 'vue-class-component';@Component({ name: 'AbstractDialog',})export default class AbstractDialog extends Vue { visible = false; t = ''; loading = false; //定義這個屬性的目的是為了實現(xiàn)既可以外界通過傳入屬性改變dialog的屬性,也支持組件內(nèi)部預(yù)設(shè)dialog的屬性 attrs = {}; get title() { return this.t; } setTitle(title) { this.t = title; }}3.3 slots 的處理

查看 Element-UI 的官方網(wǎng)站,我們發(fā)現(xiàn),ElDialog 有三個插槽,因此,我們需要接管這三個插槽

1、對 header 的處理

import Vue from 'vue';import Component from 'vue-class-component';@Component({ name: 'AbstractDialog',})class AbstractDialog extends Vue { /* 構(gòu)建彈窗的Header */ _createHeader(h) { // 判斷在調(diào)用的時候,外界是否傳入header的插槽,若有的話,則以外界傳入的插槽為準(zhǔn) var slotHeader = this.$scopedSlots['header'] || this.$slots['header']; if (typeof slotHeader === 'function') { return slotHeader(); } //若用戶沒有傳入插槽,則判斷用戶是否想改寫Header var renderHeader = this.renderHeader; if (typeof renderHeader === 'function') { return <div slot='header'>{renderHeader(h)}</div>; } //如果都沒有的話, 返回undefined,則dialog會使用我們預(yù)設(shè)好的title }}

2、對 body 的處理

import Vue from 'vue';import Component from 'vue-class-component';@Component({ name: 'AbstractDialog',})class AbstractDialog extends Vue { /** * 構(gòu)建彈窗的Body部分 */ _createBody(h) { // 判斷在調(diào)用的時候,外界是否傳入default的插槽,若有的話,則以外界傳入的插槽為準(zhǔn) var slotBody = this.$scopedSlots['default'] || this.$slots['default']; if (typeof slotBody === 'function') { return slotBody(); } //若用戶沒有傳入插槽,則判斷用戶想插入到body部分的內(nèi)容 var renderBody = this.renderBody; if (typeof renderBody === 'function') { return renderBody(h); } }}

3、對 footer 的處理

由于 dialog 的 footer 經(jīng)常都有一些相似的業(yè)務(wù),因此,我們需要把這些重復(fù)率高的代碼封裝在此,若在某種時候,用戶需要改寫 footer 的時候,再重寫,否則使用默認(rèn)行為

import Vue from 'vue';import Component from 'vue-class-component';@Component({ name: 'BaseDialog',})export default class BaseDialog extends Vue { showLoading() { this.loading = true; } closeLoading() { this.loading = false; } onSubmit() { this.closeDialog(); } onClose() { this.closeDialog(); } /** * 構(gòu)建彈窗的Footer */ _createFooter(h) { var footer = this.$scopedSlots.footer || this.$slots.footer; if (typeof footer == 'function') { return footer(); } var renderFooter = this.renderFooter; if (typeof renderFooter === 'function') { return <div slot='footer'>{renderFooter(h)}</div>; } return this.defaultFooter(h); } defaultFooter(h) { return ( <div slot='footer'><el-button type='primary' loading={this.loading} on-click={() => { this.onSubmit(); }}> 保存</el-button><el-button on-click={() => { this.onClose(); }}> 取消</el-button> </div> ); }}

最后,我們再通過 JSX 將我們編寫的這些代碼組織起來,就得到了我們最終想要的抽象彈窗代碼如下:

import Vue from 'vue';import Component from 'vue-class-component';@Component({ name: 'AbstractDialog',})export default class AbstractDialog extends Vue { visible = false; t = ''; loading = false; attrs = {}; get title() { return this.t; } setTitle(title) { this.t = title; } open() { console.log('彈窗打開,我啥也不做'); } close() { console.log('彈窗關(guān)閉,我啥也不做'); } opened() { console.log('彈窗打開,我啥也不做'); } closed() { console.log('彈窗關(guān)閉,我啥也不做'); } showLoading() { this.loading = true; } closeLoading() { this.loading = false; } openDialog() { this.visible = true; } closeDialog() { if (this.loading) { this.$message.warning('請等待操作完成!'); return; } this.visible = false; } onSubmit() { this.closeDialog(); } onClose() { this.closeDialog(); } /* 構(gòu)建彈窗的Header */ _createHeader(h) { var slotHeader = this.$scopedSlots['header'] || this.$slots['header']; if (typeof slotHeader === 'function') { return slotHeader(); } var renderHeader = this.renderHeader; if (typeof renderHeader === 'function') { return <div slot='header'>{renderHeader(h)}</div>; } } /** * 構(gòu)建彈窗的Body部分 */ _createBody(h) { var slotBody = this.$scopedSlots['default'] || this.$slots['default']; if (typeof slotBody === 'function') { return slotBody(); } var renderBody = this.renderBody; if (typeof renderBody === 'function') { return renderBody(h); } } /** * 構(gòu)建彈窗的Footer */ _createFooter(h) { var footer = this.$scopedSlots.footer || this.$slots.footer; if (typeof footer == 'function') { return footer(); } var renderFooter = this.renderFooter; if (typeof renderFooter === 'function') { return <div slot='footer'>{renderFooter(h)}</div>; } return this.defaultFooter(h); } defaultFooter(h) { return ( <div slot='footer'><el-button type='primary' loading={this.loading} on-click={() => { this.onSubmit(); }}> 保存</el-button><el-button on-click={() => { this.onClose(); }}> 取消</el-button> </div> ); } createContainer(h) { //防止外界誤傳參數(shù)影響彈窗本來的設(shè)計,因此,需要將某些參數(shù)過濾開來,有title beforeClose, visible var { title, beforeClose, visible, ...rest } = Object.assign({}, this.$attrs, this.attrs); return ( <el-dialog{...{ props: { ...rest, visible: this.visible, title: this.title || title || '彈窗', beforeClose: this.closeDialog, }, on: { close: this.close, closed: this.closed, opened: this.opened, open: this.open, },}} >{/* 根據(jù)JSX的渲染規(guī)則 null、 undefined、 false、 ’’ 等內(nèi)容將不會在頁面顯示,若createHeader返回undefined,將會使用默認(rèn)的title */}{this._createHeader(h)}{this._createBody(h)}{this._createFooter(h)} </el-dialog> ); } render(h) { return this.createContainer(h); }}4.應(yīng)用4.1組件調(diào)用

我們就以編寫 ChinaMapDialog.vue 為例,將其進行改寫

<script>import Vue from 'vue';import AbstractDialog from '@/components/AbstractDialog.vue';import Component from 'vue-class-component';@Component({ name: 'ChinaMapDialog',})class ChinaMapDialog extends AbstractDialog { get title() { return '這是中國地圖'; } attrs = { width: '600px', } //編寫一些中國地圖的處理業(yè)務(wù)邏輯代碼 //編寫彈窗的內(nèi)容部分 renderBody(h) { return <div>我是中國地圖,我講為你呈現(xiàn)華夏最壯麗的美</div>; }}</script>4.2 使用 Composition API

由于我們是通過組件的實例調(diào)用組件的方法,因此我們每次都需要獲取當(dāng)前組件的 refs 上面的屬性,這樣會使得我們的調(diào)用特別長,寫起來也特別麻煩。我們可以通過使用 Composition API 來簡化這個寫法

<template> <div> <china-map-dialog ref='china'></china-map-dialog> <usa-map-dialog ref='usa'></usa-map-dialog> <uk-map-dialog ref='uk'></uk-map-dialog> <el-button @click='openChina'>打開中國地圖</el-button> <el-button @click='openUSA'>打開美國地圖</el-button> <el-button @click='openUK'>打開英國地圖</el-button> </div></template><script>import { ref } from '@vue/composition-api';export default { name: 'View', setup() { const china = ref(null); const usa = ref(null); const uk = ref(null); return { china, usa, uk, }; }, data() { return { /** 將地圖的業(yè)務(wù)全部抽離到對應(yīng)的dialog里面去,View只存放調(diào)度業(yè)務(wù)代碼 */ }; }, methods: { // 對百度地圖和谷歌地圖的一些業(yè)務(wù)處理代碼 省略 openChina() { this.china && this.china.openDialog(); }, openUSA() { this.usa && this.usa.openDialog(); }, openUK() { this.uk && this.uk.openDialog(); }, },};</script>總結(jié)

開發(fā)這個彈窗所用到的知識點:1、面向?qū)ο笤O(shè)計在前端開發(fā)中的應(yīng)用;2、如何編寫基于類風(fēng)格的組件(vue-class-component 或 vue-property-decorator);3、JSX 在 vue 中的應(yīng)用;4、$attrs和$listeners 在開發(fā)高階組件(個人叫法)中的應(yīng)用;5、slots 插槽,以及插槽在 JSX 中的用法;6、在 Vue2.x 中使用 Composition API;

到此這篇關(guān)于詳解如何在vue+element-ui的項目中封裝dialog組件的文章就介紹到這了,更多相關(guān)vue element封裝dialog內(nèi)容請搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Vue
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
美女少妇全过程你懂的久久| 亚洲黄色影院| 视频在线观看91| 三级亚洲高清视频| 亚洲精品一区二区在线播放∴| 成人免费电影网址| 热三久草你在线| 亚洲一区二区免费看| 亚洲综合精品| 国产乱子精品一区二区在线观看 | 国产剧情一区| 欧美一区网站| 久久久久欧美精品| 蜜臀av在线播放一区二区三区| 欧美激情麻豆| 成人福利av| 日韩精品高清不卡| 精品91久久久久| 久久国产精品免费精品3p | 国产精品99一区二区三区| 91视频久久| 蜜臀精品一区二区三区在线观看 | 欧美天堂一区二区| 岛国精品一区| 午夜亚洲福利| 久久精品1区| 国产伦理久久久久久妇女| 在线观看免费一区二区| 成人在线视频中文字幕| 免费不卡在线视频| 国产精品美女| 久久久一本精品| 综合国产视频| 日韩欧美国产精品综合嫩v| 亚洲综合另类| 亚洲精品一级二级| 国产探花在线精品一区二区| 国产一区91| 韩国三级一区| 国产精品一级| 国产免费成人| 欧美日韩视频| 国产精品一区二区三区av| 国产日韩精品视频一区二区三区| 中日韩男男gay无套| 日韩区欧美区| 久久国产精品99国产| 久久久精品久久久久久96| 欧美日韩国产传媒| 91精品蜜臀一区二区三区在线| zzzwww在线看片免费| 蜜桃久久久久| 亚洲欧洲日韩| 黄色av一区| 亚洲黄色在线| 午夜一级在线看亚洲| 狠狠爱成人网| 麻豆亚洲精品| 丝袜美腿高跟呻吟高潮一区| 美女国产一区| 久久亚洲专区| 欧美日韩一二| 日韩电影二区| 高清日韩中文字幕| 国产66精品| 性欧美69xoxoxoxo| 好吊一区二区三区| 久久婷婷久久| 丝袜诱惑制服诱惑色一区在线观看| 日韩欧美中文字幕电影| 日韩福利视频导航| 亚洲精品乱码久久久久久蜜桃麻豆| 麻豆国产精品视频| 精品国产一区二区三区av片| 欧美日本精品| 福利片在线一区二区| 亚洲成人国产| 亚洲精品护士| 婷婷综合一区| 91欧美日韩在线| 91精品国产自产精品男人的天堂| 国产一区二区三区成人欧美日韩在线观看| 麻豆国产一区| 色婷婷亚洲mv天堂mv在影片| 午夜国产一区二区| 日韩在线欧美| 国产亚洲精品自拍| 国产精品视频一区二区三区综合 | 欧美亚洲tv| 麻豆网站免费在线观看| 久久精品播放| 欧美在线首页| 激情婷婷综合| 美女尤物国产一区| 91高清一区| 国产精品一区毛片| 亚洲精品a级片| 麻豆久久一区二区| 色婷婷久久久| 国产一区二区三区黄网站| 99久精品视频在线观看视频| 日韩一区二区三区高清在线观看| 五月天激情综合网| 亚洲另类av| 蜜桃tv一区二区三区| 四虎国产精品免费观看| 成人国产综合| 奇米狠狠一区二区三区| 亚洲手机视频| 国产成人精品三级高清久久91| 麻豆中文一区二区| 亚洲香蕉久久| 自由日本语亚洲人高潮| 色欧美自拍视频| 国产精品白丝一区二区三区| 91精品视频一区二区| 日韩一区二区久久| 精品一区在线| 九一精品国产| 日韩精品诱惑一区?区三区| 国产精品一区二区av日韩在线| 国产精品一区二区中文字幕| 午夜精品免费| 欧美午夜精彩| 久久精品免费一区二区三区 | 亚洲理论在线| 欧美激情福利| 国产精久久一区二区| 国产精品日本一区二区三区在线| 久久99精品久久久野外观看| 三级欧美韩日大片在线看| 日韩一区二区免费看| 91精品推荐| 激情久久中文字幕| 亚洲激情二区| 亚洲一区二区三区中文字幕在线观看| 国产精品www.| 国产劲爆久久| 精品视频91| 久久激情一区| 日韩成人亚洲| 石原莉奈一区二区三区在线观看| 国产美女精品视频免费播放软件| 亚洲精品一区三区三区在线观看| 国产精久久一区二区| 国产麻豆一区二区三区精品视频| 成人三级高清视频在线看| 国产精品日本一区二区不卡视频| 91久久黄色| 日韩中文字幕| 激情黄产视频在线免费观看| 亚洲三区欧美一区国产二区| 日本v片在线高清不卡在线观看| 久久亚洲国产精品尤物| 亚洲精品一区三区三区在线观看| 国产精品密蕾丝视频下载| 精品美女在线视频| 亚洲少妇一区| 精品深夜福利视频| 亚洲福利免费| 日韩av不卡一区二区| 欧美一区三区| 欧美午夜网站| 久久av一区| 国产videos久久| 日韩精品成人在线观看| 99久久亚洲精品蜜臀| 国产精品色在线网站| 亚洲一区免费| 日韩国产一区| 麻豆久久一区| 视频一区二区三区中文字幕| 欧美 日韩 国产一区二区在线视频 | 日韩一级网站| 精品午夜av| 天堂成人免费av电影一区| 天堂√8在线中文| 日韩在线视频精品| 日本免费在线视频不卡一不卡二| 尹人成人综合网| 首页欧美精品中文字幕| 色在线视频观看| 9国产精品视频| 亚洲欧美日本国产| 日韩久久视频| 91嫩草亚洲精品| 亚洲三级欧美| 欧产日产国产精品视频| 国产精品精品国产一区二区| 日产欧产美韩系列久久99| 亚洲理论在线| 欧美日韩视频免费看| 国产日韩电影| 女同性一区二区三区人了人一| 久久免费国产| 国产精品九九| 成人一区而且| 91综合网人人| 日本不卡在线视频| 婷婷综合电影|