您现在的位置是:首页 > 文章详情

利用Vue构造器创建Form组件的通用解决方法

日期:2018-12-07点击:439

在前端平常的业务中,无论是官网、展示页还是后台运营系统都离不开表单,它承载了大部分的数据采集工作。所以如何更好地实现它,是平常工作中的一个重要问题。

在应用Vue框架去开发业务时,会将页面上每个独立的可视/可交互区域拆分为一个组件,再通过多个组件的自由组合来组成新的页面。例如

<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方便通信。但是也会有以下几个缺陷:

  • 当前组件的data必须要有UI.isOpen来控制表单,如果存在多个表单时,就会有大量的状态来维护表单的开关;
  • 如果表单多次弹出时,可能需要对表单的data进行重置;
  • 与组件化思想相违背,表单不属于当前页面,它只是由于用户行为触发的结果。

为了解决以上缺陷,并且还能具备方便通信的优势,本文选择用Vue.extend将原有<form>组件转化为method function,并维护在当前组件的method中,当用户触发时,在页面中挂载,关闭时自动注销。

实例

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`) })//欢迎加入前端全栈开发交流圈一起吹水聊天学习交流:864305860 } } } </script> 

当<el-button>的点击事件触发时,调用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() }//欢迎加入前端全栈开发交流圈一起吹水聊天学习交流: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中。

原理

上述代码中,最为关键的一步就是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,它的功能是:

  • 将<form />组件转化为method
  • 在method调用时,将组件实例化并传递propsData
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:代表用户取消;

当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) 

结语

感谢您的观看,如有不足之处,欢迎批评指正。

原文链接:https://my.oschina.net/u/3982182/blog/2981163
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章