利用Vue構(gòu)造器創(chuàng)建Form組件的通用解決方法
前言
在前端平常的業(yè)務(wù)中,無論是官網(wǎng)、展示頁還是后臺運(yùn)營系統(tǒng)都離不開表單,它承載了大部分的數(shù)據(jù)采集工作。所以如何更好地實(shí)現(xiàn)它,是平常工作中的一個重要問題。
在應(yīng)用Vue框架去開發(fā)業(yè)務(wù)時,會將頁面上每個獨(dú)立的可視/可交互區(qū)域拆分為一個組件,再通過多個組件的自由組合來組成新的頁面。例如
<template> <header></header> ... <content></content> ... <footer></footer> </template>
當(dāng)用戶的某個行為觸發(fā)表單時(例如注冊、建立內(nèi)容等),期望在頁面中彈出一個From組件。通常的做法是在template中填入一個<form>組件用于開發(fā),并通過控制data中的UI.isOpen來對其display進(jìn)行控制,例如在當(dāng)前<template>組件內(nèi)開發(fā)
<template> <header></header> ... <content></content> ... <footer></footer> ... <register-form v-if="UI.isOpen"> <form-item></form-item> ... <submit-button></submit-button> </register-form> </template>
這樣開發(fā)有一點(diǎn)優(yōu)勢,F(xiàn)orm組件與其父組件之間可以通過prop以及$emit方便通信。但是也會有以下幾個缺陷:
- 當(dāng)前組件的data必須要有UI.isOpen來控制表單,如果存在多個表單時,就會有大量的狀態(tài)來維護(hù)表單的開關(guān);
- 如果表單多次彈出時,可能需要對表單的data進(jìn)行重置;
- 與組件化思想相違背,表單不屬于當(dāng)前頁面,它只是由于用戶行為觸發(fā)的結(jié)果。
為了解決以上缺陷,并且還能具備方便通信的優(yōu)勢,本文選擇用Vue.extend將原有<form>組件轉(zhuǎn)化為method function,并維護(hù)在當(dāng)前組件的method中,當(dāng)用戶觸發(fā)時,在頁面中掛載,關(guān)閉時自動注銷。
實(shí)例
演示地址:演示實(shí)例
代碼地址:FatGe github (本地下載)
APP組件
<template> <div id="app"> <el-button type="primary" icon="el-icon-edit-outline" @click="handleClick" >注冊</el-button> </div> </template> <script> import register from './components/register' import { transform } from './transform' export default { name: 'App', methods: { register: transform(register), handleClick () { this.register({ propsData: { name: '皮鞋' }, done: name => alert(`${name}牛B`) }) } } } </script>
當(dāng)<el-button>的點(diǎn)擊事件觸發(fā)時,調(diào)用register方法,將表單組件掛載在頁面中。
Form組件
<template> <div class="mock" v-if="isVisible"> <div class="form-wrapper"> <i class="el-icon-close close-btn" @click.stop="close"></i> ...<header /> ...<content /> <div class="footer"> <el-button type="primary" @click="handleClick" >確定</el-button> <el-button type="primary" @click="handleClick" >取消</el-button> </div> </div> </div> </template> <script> export default { porps: { ... }, data () { return { isVisible: true } }, watch: { isVisible (newValue) { if (!newValue) { this.destroyElement() } } }, methods: { handleClick ({ type }) { const handler = { close: () => this.close() } }, destroyElement () { this.$destroy() }, close () { this.isVisible = false } }, mounted () { document.body.appendChild(this.$el) }, destroyed () { this.$el.parentNode.removeChild(this.$el) } } </script>
在APP組件內(nèi)并未維護(hù)<form>組件的狀態(tài),其打開或關(guān)閉只維護(hù)在自身的data中。
原理
上述代碼中,最為關(guān)鍵的一步就是transform函數(shù),它將原有的`從single-file components轉(zhuǎn)化為了method function,其原理如下
const transform = (component) => { const _constructor = Vue.extend(component) return function (options = {}) { const { propsData } = options let instance = new _constructor({ propsData }).$mount(document.createElement('div')) return instance } }
首先利用Vue.extend(options)創(chuàng)建一個<Form/>組件的子類
const _constructor = Vue.extend(component)
然后return function,它的功能是:
- 將<form />組件轉(zhuǎn)化為method
- 在method調(diào)用時,將組件實(shí)例化并傳遞propsData
const { propsData } = options let instance = new _constructor({ propsData }).$mount(document.createElement('div'))
為了能夠控制實(shí)例化后的組件,選擇instance返回。
當(dāng)組件實(shí)例化時,它只是掛載到document.createElement('div')上,但是并沒有掛載到頁面上,所以需要將其appendChild到頁面中。為了更好的語義化,選擇在組件的生命周期中完成它在頁面中的掛載。實(shí)例化時,會觸發(fā)組件mounted生命周期,所以當(dāng)其觸發(fā)時可以掛載在document.body中,具體如下
mounted () { document.body.appendChild(this.$el) }
有了掛載,就必須要有注銷。對應(yīng)的生命周期應(yīng)該是destroyed,所以
method: { destroyElement () { this.$destroy() } }, destroyed () { this.$el.parentNode.removeChild(this.$el) }
組件注銷的時間與它在頁面中顯示息息相關(guān),當(dāng)<form />在頁面中不可見時候,需要注銷它
method: { destroyElement () { this.$destroy() } }, destroyed () { this.$el.parentNode.removeChild(this.$el) }
一般Form組件有兩個功能:
- done:代表用戶確認(rèn);
- cancel:代表用戶取消;
當(dāng)done或cancel觸發(fā)時,APP組件內(nèi)可能會有相應(yīng)的變化,所以在組件實(shí)例化之后,利用$on去監(jiān)聽對應(yīng)的done事件以及cancel事件。
done && inlineListen({ method: 'done', options, instance }) cancel && inlineListen({ method: 'cancel', options, instance })
其中inlineListen函數(shù)可以方便后續(xù)添加其他的event,其代碼為
const inlineListen = ({ method, options, instance }) => { let listener = `on${method}` instance[listener] = options[method] instance.$on(method, function (data) { this[listener](data) }) }
也可以將上述方案封裝成Promise形式,如下
export const transform = (component) => { const _constructor = Vue.extend(component) return function (options = {}) { const { propsData } = options return new Promise((resolve, reject) => { let instance = new _constructor({ propsData }).$mount(document.createElement('div')) instance.$on('done', data => resolve(data)) }) } }
使用
可以將上述屬于<Form/>公有的data以及method獨(dú)立出來,再通過mixins引入到每個表單內(nèi),例如
export default { data() { return { visible: true } }, watch: { visible(newValue) { if (!newValue) { this.destroyElement() } } }, mounted() { document.body.appendChild(this.$el) }, destroyed() { this.$el.parentNode.removeChild(this.$el) }, methods: { destroyElement() { this.$destroy() }, close() { this.visible = false } } }
再通過mixins混入。
<script> import popupWin from '../mixins/popup-win' export default { mixins: [popupWin], data () { return { input: '', gender: 1 } }, methods: { handleClick ({ type }) { const handler = { close: () => this.close(), confirm: () => { const { input } = this this.$emit('done', input) } } } } } </script>
調(diào)用時,只需
export default { name: 'App', methods: { register: transform(register), handleClick () { this.register({ propsData: { ... }, // done: data => function done () { // 外部關(guān)閉 this.close() } }) } } }
PS:如果業(yè)務(wù)場景需要,在外部控制表單的關(guān)閉時,只需要改變done function的context,也就是this指針指向<Form/>。
總結(jié)
通過上述的transform函數(shù),將原有的注入式組件轉(zhuǎn)化為了命令式,簡化了頁面狀態(tài)的維護(hù),在通過mixins混入公有data以及method,簡化了表單組件開發(fā)。上述方法也可用于開發(fā)toast、alert、confirm等組件,只需要將
Vue.prototype.method = transform(Toast-Component)
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
詳解Vue的computed(計算屬性)使用實(shí)例之TodoList
本篇文章主要介紹了詳解Vue的computed(計算屬性)使用實(shí)例之TodoList,具有一定的參考價值,有興趣的可以了解一下2017-08-08Vue 2閱讀理解之initRender與callHook組件詳解
這篇文章主要為大家介紹了Vue 2閱讀理解之initRender與callHook組件詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08axios封裝,使用攔截器統(tǒng)一處理接口,超詳細(xì)的教程(推薦)
這篇文章主要介紹了axios封裝使用攔截器處理接口,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05Vue 設(shè)置axios請求格式為form-data的操作步驟
今天小編就為大家分享一篇Vue 設(shè)置axios請求格式為form-data的操作步驟,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-10-10