基于Vue构造器创建Form组件的通用解决方案
栏目: JavaScript · 发布时间: 6年前
内容简介:在前端平常的业务中,无论是官网、展示页还是后台运营系统都离不开表单,它承载了大部分的数据采集工作。所以如何更好地实现它,是平常工作中的一个重要问题。在应用Vue框架去开发业务时,会将页面上每个独立的可视/可交互区域拆分为一个组件,再通过多个组件的自由组合来组成新的页面。例如当用户的某个行为页面触发表单时(例如注册、建立内容等),我们会在页面中弹出一个From组件。通常的做法是在
在前端平常的业务中,无论是官网、展示页还是后台运营系统都离不开表单,它承载了大部分的数据采集工作。所以如何更好地实现它,是平常工作中的一个重要问题。
在应用Vue框架去开发业务时,会将页面上每个独立的可视/可交互区域拆分为一个组件,再通过多个组件的自由组合来组成新的页面。例如
<template> <header></header> ... <content></content> ... <footer></footer> </template> 复制代码
当用户的某个行为页面触发表单时(例如注册、建立内容等),我们会在页面中弹出一个From组件。通常的做法是在 template
中填入一个 <register-form>
组件用于开发注册表单,并通过控制 data
中的 UI.isOpen
来对其 display
进行控制。
<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
以及 event
方便通信以及数据传递。但是也会有以下几个缺陷:
-
当前组件的
data
必须要有UI.isOpen
来控制表单,如果存在多个表单时,就会有大段的代码来维护表单的状态; -
如果多个
button
触发同一个表单时,可能需要对表单的data
进行重置; - 与组件化思想相违背,表单不属于当前页面,它只是由于用户行为触发的结果。
为了解决以上缺陷,并且还能具备方便通信的优势,本文选择用 Vue.extend
将原有 <register-form></register-form>
组件转化为函数,并维护在当前组件的 method
中,当用户触发时,在页面中挂载,关闭时自动注销。
实例:
演示地址:[[ http://localhost:3000/blog/juejin-example-1#/ ]]
代码地址:[[ github.com/FatGe/examp… ]]
- 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> 复制代码
当APP组件中 <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> <div class="header"> ... </div> <div class="content"> ... </div> <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 { props: { name: { type: String, default: '' } }, 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 } }, created () { // init data } mounted () { document.body.appendChild(this.$el) }, destroyed () { this.$el.parentNode.removeChild(this.$el) } } </script> 复制代码
两者的通信通过 register
的 options
还完成,并且Form组件中维护了自身的 isVisible
,当用户点击 close
或者取消时,表单会自动关闭并从页面中移除。
原理:
上述代码中,最为关键的一步就是 transform
函数,它将原有的Form组件从原文的 single-file components
转化为了 method
,其原理如下
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的API- Vue.extend(options)
[ cn.vuejs.org/v2/api/#Vue…
]创建一个Form组件的子类
const _constructor = Vue.extend(component) 复制代码
然后 return
一个 function
,它的功能是,在 method
调用时,将组件 实例化
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() } }, destroyed () { this.$el.parentNode.removeChild(this.$el) } 复制代码
组件注销的时间与它在页面中显示息息相关,所以
watch: { isVisible (newValue) { if (!newValue) { this.destroyElement() // 如果需要添加过渡动画 // this.$el.addEventListener('transitionend', this.destroyElement) } } } 复制代码
一般Form组件有两个功能:
- done:代表用户确认;
- cancel:代表用户取消;
当done或cancel触发时,APP组件内可能会有相应的变化,所以在组件实例化之后,利用 $on
去监听对应的 done
事件以及 cancel
事件。
done && inlineListen({ method: 'done', options, instance }) 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) }) } 复制代码
以上是解决Form组件的原理以及方案。
使用:
可以将上述属于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 } } } 复制代码
再通过 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() } } } } </script> 复制代码
总结:
通过上述的 transform
函数,将原有的注入式组件转化为了命令式,简化了页面状态的维护,在通过 mixins
混入公有 data
以及 method
,简化了表单组件开发。上述方法也可用于开发toast、alert、confirm等组件,只需要将 Vue.prototype = transform(Toast-Component)
。
原创声明: 该文章为原创文章,转载请注明出处。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 如何构造一个 MySQL Master 组件
- Shadow DOM 内部构造及如何构建独立组件
- 【golang-GUI开发】struct tags系统(二)qt的自定义组件和构造函数
- Java类 静态代码块、构造代码块、构造函数初始化顺序
- TS 的构造签名和构造函数类型是啥?傻傻分不清楚
- 只有你能 new 出来!.NET 隐藏构造函数的 n 种方法(Builder Pattern / 构造器模式)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
jQuery in Action
Bear Bibeault、Yehuda Katz / Manning Publications / 2008-2-17 / USD 39.99
A good web development framework anticipates what you need to do and makes those tasks easier and more efficient; jQuery practically reads your mind. Developers of every stripe-hobbyists and professio......一起来看看 《jQuery in Action》 这本书的介绍吧!