利用Vue構(gòu)造器創(chuàng)建Form組件的通用解決方法
前言
在前端平常的業(yè)務(wù)中,無論是官網(wǎng)、展示頁還是后臺(tái)運(yùn)營系統(tǒng)都離不開表單,它承載了大部分的數(shù)據(jù)采集工作。所以如何更好地實(shí)現(xiàn)它,是平常工作中的一個(gè)重要問題。
在應(yīng)用Vue框架去開發(fā)業(yè)務(wù)時(shí),會(huì)將頁面上每個(gè)獨(dú)立的可視/可交互區(qū)域拆分為一個(gè)組件,再通過多個(gè)組件的自由組合來組成新的頁面。例如
<template> <header></header> ... <content></content> ... <footer></footer> </template>
當(dāng)用戶的某個(gè)行為觸發(fā)表單時(shí)(例如注冊(cè)、建立內(nèi)容等),期望在頁面中彈出一個(gè)From組件。通常的做法是在template中填入一個(gè)<form>組件用于開發(fā),并通過控制data中的UI.isOpen來對(duì)其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方便通信。但是也會(huì)有以下幾個(gè)缺陷:
- 當(dāng)前組件的data必須要有UI.isOpen來控制表單,如果存在多個(gè)表單時(shí),就會(huì)有大量的狀態(tài)來維護(hù)表單的開關(guān);
- 如果表單多次彈出時(shí),可能需要對(duì)表單的data進(jìn)行重置;
- 與組件化思想相違背,表單不屬于當(dāng)前頁面,它只是由于用戶行為觸發(fā)的結(jié)果。
為了解決以上缺陷,并且還能具備方便通信的優(yōu)勢,本文選擇用Vue.extend將原有<form>組件轉(zhuǎn)化為method function,并維護(hù)在當(dāng)前組件的method中,當(dāng)用戶觸發(fā)時(shí),在頁面中掛載,關(guān)閉時(shí)自動(dòng)注銷。
實(shí)例
演示地址:演示實(shí)例
代碼地址:FatGe github (本地下載)
APP組件
<template>
<div id="app">
<el-button
type="primary" icon="el-icon-edit-outline"
@click="handleClick"
>注冊(cè)</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ā)時(shí),調(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)建一個(gè)<Form/>組件的子類
const _constructor = Vue.extend(component)
然后return function,它的功能是:
- 將<form />組件轉(zhuǎn)化為method
- 在method調(diào)用時(shí),將組件實(shí)例化并傳遞propsData
const {
propsData
} = options
let instance = new _constructor({
propsData
}).$mount(document.createElement('div'))
為了能夠控制實(shí)例化后的組件,選擇instance返回。
當(dāng)組件實(shí)例化時(shí),它只是掛載到document.createElement('div')上,但是并沒有掛載到頁面上,所以需要將其appendChild到頁面中。為了更好的語義化,選擇在組件的生命周期中完成它在頁面中的掛載。實(shí)例化時(shí),會(huì)觸發(fā)組件mounted生命周期,所以當(dāng)其觸發(fā)時(shí)可以掛載在document.body中,具體如下
mounted () {
document.body.appendChild(this.$el)
}
有了掛載,就必須要有注銷。對(duì)應(yīng)的生命周期應(yīng)該是destroyed,所以
method: {
destroyElement () {
this.$destroy()
}
},
destroyed () {
this.$el.parentNode.removeChild(this.$el)
}
組件注銷的時(shí)間與它在頁面中顯示息息相關(guān),當(dāng)<form />在頁面中不可見時(shí)候,需要注銷它
method: {
destroyElement () {
this.$destroy()
}
},
destroyed () {
this.$el.parentNode.removeChild(this.$el)
}
一般Form組件有兩個(gè)功能:
- done:代表用戶確認(rèn);
- cancel:代表用戶取消;
當(dāng)done或cancel觸發(fā)時(shí),APP組件內(nèi)可能會(huì)有相應(yīng)的變化,所以在組件實(shí)例化之后,利用$on去監(jiān)聽對(duì)應(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引入到每個(gè)表單內(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)用時(shí),只需
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)閉時(shí),只需要改變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)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
uni-app自定義導(dǎo)航欄按鈕|uniapp仿微信頂部導(dǎo)航條功能
這篇文章主要介紹了uni-app自定義導(dǎo)航欄按鈕|uniapp仿微信頂部導(dǎo)航條,需要的朋友可以參考下2019-11-11
vue DatePicker日期選擇器時(shí)差8小時(shí)問題
這篇文章主要介紹了vue DatePicker日期選擇器時(shí)差8小時(shí)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05
vue3+ts+vite打包后靜態(tài)資源404無法加載js和css問題解決辦法
這篇文章主要給大家介紹了關(guān)于vue3+ts+vite打包后靜態(tài)資源404無法加載js和css問題的解決辦法,文中通過代碼以及圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-04-04
VueCli3構(gòu)建TS項(xiàng)目的方法步驟
這篇文章主要介紹了VueCli3構(gòu)建TS項(xiàng)目的方法步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-11-11

