本案例将会讲解如何使用 vue.js + ElementUI 开发一个简单的 可配置组合表单 Demo。
示例源代码 github
操作演示:
在左侧新建表单区块,选择区块标题和表单组件类型后点击确定,会在中间区域生成一个块新的表单,右侧展示了所有表单的数据合并结果。
在本示例中你主要可以看到以下知识点的运用:
- vue.js 单文件组件,
- 组件传参
- 自定义 v-model
- 数据监听
- 数据合并
- 批量自动注册组件
- 使用 mixin 抽取公用代码
- sass 语法
- BEM 规范
- 尽量避免使用 for 循环的写法
-
组件 - 动态绑定 v-model 到一组数据
上面列举的这些是因为以前有群里朋友询问相关的实现方法,在此列出,可能正在读这篇文章的你已经都掌握了,恭喜你!(本篇文章的起因也是群友提问)
下面开始正文
总览
这个 demo 的所有组件和逻辑如果写在一个文件中大概会有几百行,维护起来会有麻烦,所以首先设计这样的目录结构:
搭建基本框架
为了快速开发页面本项目使用 ElementUI 和 D2Admin 快速搭建,以下示例中组件都来自这两个开源项目,如果你不认识这些组件也没有关系,大致了解意思就可。
首先写出页面的大致框架:
可配置问卷示例
从左侧选择要添加的表单块,右侧查看结果
css / sass 暂时先忽略,在最后会展示样式代码
表单区块
新建 page1/components/Form/Form1.vue
作为第一个表单区块
男
女
这是用 ElementUI 构建的很简单的一个表单,甚至没有校验。
然后我们在页面组件上注册这个表单区块:
等等,假如我有 20 个区块,难道要写 20 遍注册,在 formList 里手动加 20 个对象吗?
所以我们先新建了 7 个区块,区块内容都大同小异,并将代码稍加改造:
表单区块示例
男
女
页面组件(只展示重点部分)
你可能要问,上面这一大坨是什么鬼 ???
首先介绍 webpack 的 require-context 你可以点击链接查看官方文档。
简单通俗来讲这个方法就是为了方便引入大量文件用的,它接收三个参数
- 你要引入文件的目录
- 是否要查找该目录下的子级目录
- 匹配要引入的文件
然后会返回一个 require 对象,对象有三个属性:resolve 、keys、id
- resolve: 是一个函数,他返回的是被解析模块的id
- keys: 也是一个函数,他返回的是一个数组,该数组是由所有可能被上下文模块解析的请求对象组成
- id:上下文模块的id
所以在上面代码中
const req = context => context.keys().map(context)
const forms = req(require.context('./components/Form/', false, /\.vue$/))
最后得到的 forms 就是 ./components/Form/
目录下所有的 vue 文件对象
然后通过
sortby(forms.map(e => {
const component = e.default
const { index, title, name } = component
return { component, title, index, name }
}), ['index']).forEach(form => {
const { component, title, name } = form
components[name] = component
formList.push({ title, name })
})
处理 forms 对象,得到 vue 注册组件时需要的的 components 格式,并且将所有的组件信息保存进 formList 供页面逻辑使用。具体的转换方式请查看上面的代码。
这样不管我们在 ./components/Form/
下写了多少单文件组件,webpack 都会自动帮我们引入并通过我们的代码注册到页面中。
大量组件注册的问题解决了,接下来我们还要一个需要优化的问题:
不管是 Form1 还是 Form2 还是 FormN,大家会发现其实代码里有一些重复内容,还有一些是有逻辑关系的重复内容,下面我们通过写一个 mixin 来减少重复代码:
mixin.js:
export default function (form) {
return {
props: {
value: {
default: () => form
}
},
data () {
return {
form
}
},
watch: {
form: {
handler (value) {
this.$emit('input', value)
},
deep: true,
immediate: true
}
}
}
}
这个 js 文件导出了一个函数,该函数接收一个 form 参数,并将这个参数赋值给 value prop 以及 data 中的 form 字段并返回一个对象。
然后我们将这个 mixin 注册进每个 Form 组件中,并且改造每个 Form 组件:
男
女
这样每个 Form 组件都节省下了十几行代码,关键是这些代码是重复冗余的。
最后页面组件是这个样子:
可配置问卷示例
{{form.title}}
从左侧选择要添加的表单块,右侧查看结果
左侧页面组件
左侧右侧组件不是重点内容,所以一次性展示出带有注释的代码
新建 page1/components/AsideLeft/index.vue
作为左侧页面组件
{{item.title}}
新增
区块标题
区块组件
{{item.title}}
取 消
确 定
右侧页面组件
左侧右侧组件不是重点内容,所以一次性展示出带有注释的代码
新建 page1/components/AsideRight/index.vue
作为右侧页面组件
{{item.keyName}}
{{item.value === '' ? '未填写' : item.value}}
所有代码就结束了,其实我们就写了五个文件
- 页面组件
- 两个侧边栏
- 表单区块
- 表单区块 mixin
这是一个很小但是涉及知识还不算少的小例子,如果上面的代码你有疑惑,可以来 D2 Projects 的 QQ 交流群 806395827 提问。
本文首发于 D2 开源项目组官方公众号 D2 Projects
参考
地址 | 描述 |
---|---|
掘金专栏 | 掘金专栏 |
团队主页 | 开源团队主页 |
D2Admin 中文文档 | 中文文档 |
D2Admin 预览地址 | 完整版 预览地址 |
D2Admin github | 完整版 Github 仓库 |
ElementUI | ElementUI 组件库 |