在前端日常的業務中,不管是官網、展現頁仍是後臺運營系統都離不開表單,它承載了大部分的數據採集工做。因此如何更好地實現它,是日常工做中的一個重要問題。前端
在應用Vue框架去開發業務時,會將頁面上每一個獨立的可視/可交互區域拆分爲一個組件,再經過多個組件的自由組合來組成新的頁面。例如app
<template> <header></header> ... <content></content> ... <footer></footer> </template>
當用戶的某個行爲觸發表單時(例如註冊、創建內容等),指望在頁面中彈出一個From組件。一般的作法是在template中填入一個<form>組件用於開發,並經過控制data中的UI.isOpen來對其display進行控制,例如在當前<template>組件內開發框架
<template> <header></header> ... <content></content> ... <footer></footer> ... <register-form v-if="UI.isOpen"> <form-item></form-item> ... <submit-button></submit-button> </register-form> </template>
Form組件與其父組件之間能夠經過prop以及$emit方便通訊。可是也會有如下幾個缺陷:函數
爲了解決以上缺陷,而且還能具有方便通訊的優點,本文選擇用Vue.extend將原有<form>組件轉化爲method function,並維護在當前組件的method中,當用戶觸發時,在頁面中掛載,關閉時自動註銷。組件化
實例學習
APP組件this
<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`) })//歡迎加入前端全棧開發交流圈一塊兒吹水聊天學習交流:864305860 } } } </script>
當<el-button>的點擊事件觸發時,調用register方法,將表單組件掛載在頁面中。prototype
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() }//歡迎加入前端全棧開發交流圈一塊兒吹水聊天學習交流:864305860 }, destroyElement () { this.$destroy() }, close () { this.isVisible = false } }, mounted () { document.body.appendChild(this.$el) }, destroyed () { this.$el.parentNode.removeChild(this.$el) }//歡迎加入前端全棧開發交流圈一塊兒學習交流:864305860 } </script>
在APP組件內並未維護<form>組件的狀態,其打開或關閉只維護在自身的data中。code
原理
上述代碼中,最爲關鍵的一步就是transform函數,它將原有的`從single-file components轉化爲了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 }//歡迎加入前端全棧開發交流圈一塊兒學習交流:864305860 }
首先利用Vue.extend(options)建立一個<Form/>組件的子類
const _constructor = Vue.extend(component)
而後return function,它的功能是:
const { propsData } = options let instance = new _constructor({ propsData }).$mount(document.createElement('div'))
爲了可以控制實例化後的組件,選擇instance返回。 當組件實例化時,它只是掛載到document.createElement('div')上,可是並無掛載到頁面上,因此須要將其appendChild到頁面中。爲了更好的語義化,選擇在組件的生命週期中完成它在頁面中的掛載。實例化時,會觸發組件mounted生命週期,因此當其觸發時能夠掛載在document.body中,具體以下
mounted () { document.body.appendChild(this.$el) }
有了掛載,就必需要有註銷。對應的生命週期應該是destroyed,因此
method: { destroyElement () { this.$destroy() } //歡迎加入前端全棧開發交流圈一塊兒學習交流:864305860 }, destroyed () { this.$el.parentNode.removeChild(this.$el) }
組件註銷的時間與它在頁面中顯示息息相關,當<form />在頁面中不可見時候,須要註銷它
method: { destroyElement () { this.$destroy() } //歡迎加入前端全棧開發交流圈一塊兒學習交流:864305860 }, destroyed () { this.$el.parentNode.removeChild(this.$el) }
通常Form組件有兩個功能:
當done或cancel觸發時,APP組件內可能會有相應的變化,因此在組件實例化以後,利用$on去監聽對應的done事件以及cancel事件。
done && inlineListen({ method: 'done', options, instance })//歡迎加入前端全棧開發交流圈一塊兒吹水聊天學習交流:864305860 cancel && inlineListen({ method: 'cancel', options, instance })
其中inlineListen函數能夠方便後續添加其餘的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)) })//歡迎加入前端全棧開發交流圈一塊兒學習交流:864305860 } }//幫助突破技術瓶頸,提高思惟能力
使用
能夠將上述屬於<Form/>公有的data以及method獨立出來,再經過mixins引入到每一個表單內,例如
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 }//歡迎加入前端全棧開發交流圈一塊兒學習交流:864305860 }//面向1-3年前端人員 }//幫助突破技術瓶頸,提高思惟能力
再經過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) }//歡迎加入前端全棧開發交流圈一塊兒學習交流:864305860 } }//面向1-3年前端人員 } }//幫助突破技術瓶頸,提高思惟能力 </script>
調用時,只需
export default { name: 'App', methods: { register: transform(register), handleClick () { this.register({ propsData: { ... }, // done: data => function done () { // 外部關閉 this.close() }//歡迎加入前端全棧開發交流圈一塊兒學習交流:864305860 }) }//面向1-3年前端人員 } }//幫助突破技術瓶頸,提高思惟能力
PS:若是業務場景須要,在外部控制表單的關閉時,只須要改變done function的context,也就是this指針指向<Form/>。
總結
經過上述的transform函數,將原有的注入式組件轉化爲了命令式,簡化了頁面狀態的維護,在經過mixins混入公有data以及method,簡化了表單組件開發。上述方法也可用於開發toast、alert、confirm等組件,只須要將
Vue.prototype.method = transform(Toast-Component)
結語
感謝您的觀看,若有不足之處,歡迎批評指正。