之前先接触了ElementUI,然后后面又接触了Ant Design,在这里做个对比,希望通过对比这两前端ui框架,能够更加深入的了解和使用这些框架。
表格对比
首先,通过一张表格来对比这两框架的异同吧
| 对比项 | ElementUI | Ant Design |
|---|---|---|
| logo |
|
|
| 官网/文档 | vue: https://element.eleme.cn | vue: https://www.antdv.com/docs/vue react: https://ant.design/docs/react/introduce-cn |
| 团队 | 饿了么 | 蚂蚁金服 |
| 简介 | 基于 Vue 2.0 的桌面端组件库 | 开发和服务于企业级后台产品 |
| 最新版本 | vue: 2.13.1 | vue: 1.5.3 react: 4.1.4 |
| 组件前缀 | el- | a- |
| github | vue: https://github.com/ElemeFE/element | vue: https://github.com/vueComponent/ant-design-vue react: https://github.com/ant-design/ant-design |
| start/fork | vue: 44.8k/10.3k | vue: 10k/1.4k react: 58.8k/21.5k |
| pro版 | https://github.com/PanJiaChen/vue-element-admin | https://pro.ant.design/ |
个人感受
从体验上来看:
我更加倾向于elementUI, UI上更加漂亮,使用起来更加容易上手。
一开始,我最新接触的就是elementUI,感觉elementUI这个框架更加适合于面向外部开发。
而作为对比的Ant Design,也有一定的优势。
从功能上来讲,后者更加齐全。比如回到顶部组件:树形选择:<a-tree-select />,Ant Design更加适合管理平台的开发。
从实用上来看:
对于pro版本,vue-element-admin允许初始化基础版,而ant-design-pro这个初始化后有大量的例子,开发之前还得把例子删掉,这点感觉不太好。
总之,两个框架的pro版本做的都非常棒,但个人更加倾向于ant-design,毕竟组件多占有非常大的优势。
总之:
如果是想快速上手,又希望ui更加漂亮,建议用elementUI;如追求的是比较复杂的后台管理平台,可以考虑采用ant-design-pro,而且ant-design-pro无论表格还是表单,都是高度可配置化的。这点相对于elementUI来说,ant-design-pro虽然稍微复杂了点,但是换来更大的便利。
一些建议和经验
以下代码部分都是vue,不涉及react
1. elementUI的菜单组件<el-dropdown>在手机端点击会回弹的问题
修改trigger为click的方式,因为默认hover时,手机上并不能有很好的体验。
2. elementUI如何实现通用表单的配置
采用form-create这个库可以很方便的实现表单完全的json配置
主页:http://www.form-create.com/
git地址:https://github.com/xaboy/form-create
另外还有一种方式,就是利用Vue的插槽实现,这种方式也适合ant-design的可配置表单的实现。
ps: 注意插槽名称不要带数字,最好不要
下面是利用插槽实现可配置的例子,这里以ant-design为例:
form.json
{ "props": { "layout": "inline" }, "dataSource": [ { "label": "咨询ID", "cmp": "Input", "decorator": ["qaId", { "initialValue": "" }], "props": { "allowClear": true } } ]
然后是编写通用表单组件:
分为两个文件:baseForm.vue和commonForm.vue,对外使用commonForm.vue即可:
baseForm.vue
<template>
<a-form v-bind="props" :form="form" @submit="onSubmit">
<template v-for="item in dataSource">
<a-form-item :key="item.key" :label="item.label">
<slot :name="item.cmp" :item="item" :form="props.name"> </slot>
</a-form-item>
</template>
<slot name="footer">
<a-form-item label>
<a-button type="primary" html-type="submit"> 提交 </a-button>
</a-form-item>
</slot>
</a-form>
</template>
<script>
export default {
props: {
props: {
type: Object,
default() {
return {}
}
},
dataSource: {
type: Array,
default() {
return []
}
}
},
data() {
return {
form: this.$form.createForm(this, { name: ’base-form‘ })
}
},
methods: {
handleReset() {
this.form.resetFields()
},
validate() {
this.form.validateFields({ force: true }, () => {})
},
getFields() {
return new Promise((resolve, reject) => {
this.form.validateFields({ force: true }, (err, values) => {
if (err) {
reject(err)
return
}
resolve(values)
})
})
},
onSubmit(e) {
e && e.preventDefault()
this.form.validateFields((err, values) => {
if (!err) {
this.$emit('change', values)
}
})
}
}
}
</script>
commonForm.vue
<template>
<div v-if="dataSource.length">
<base-form
ref="form"
:props="props"
:dataSource="dataSource"
@change="change"
>
<template v-slot:Input="{ item }">
<a-input v-decorator="item.decorator" v-bind="item.props" v-on="item.events" />
</template>
<template v-slot:Radio="{ item }">
<a-radio v-decorator="item.decorator" v-bind="item.props" v-on="item.events" />
</template>
<template v-slot:RadioGroup="{ item }">
<a-radio-group v-decorator="item.decorator" :options="item.options" v-bind="item.props" v-on="item.events" />
</template>
<template v-slot:Switch="{ item }">
<a-switch v-decorator="item.decorator" v-bind="item.props" v-on="item.events" />
</template>
<template v-slot:DatePicker="{ item }">
<a-date-picker v-decorator="item.decorator" v-bind="item.props" v-on="item.events" />
</template>
<template v-slot:Checkbox="{ item }">
<a-checkbox v-decorator="item.decorator" v-bind="item.props" v-on="item.events" />
</template>
<template v-slot:CheckboxGroup="{ item }">
<a-checkbox-group v-decorator="item.decorator" :options="item.options" v-bind="item.props" v-on="item.events" />
</template>
<template v-slot:Select="{ item }">
<a-select v-decorator="item.decorator" v-bind="item.props" :options="item.options" v-on="item.events" />
</template>
</base-form>
</div>
</template>
<script>
export default {
props: {
value: {
type: Object,
default() {
return {}
}
},
props: {
type: Object,
default() {
return {}
}
},
dataSource: {
type: Array,
default() {
return []
}
}
},
components: {
BaseForm: () => import('./BaseForm')
},
model: {
prop: 'value',
event: 'change'
},
methods: {
reset() {
this.$refs.form.handleReset()
},
validate() {
this.$refs.form.validate()
},
getFields() {
return this.$refs.form.getFields()
},
isExistedSlot(name) {
return this.registedSlot.includes(name)
},
change(values) {
this.$emit('change', values)
}
}
}
</script>
ps: 这里需要注意
v-bind和v-on,平时我们都是用:<属性名称>以及@<事件名称>,而这里属性和事件并不是固定的,通过v-bind="props"以及v-on="events"可以批量的不固定的设置属性和事件
3. 关于手动文件上传
注意,ant-design中,file对象可以通过绑定<a-upload-dragger :beforeUpload="beforeUpload" />的beforeUpload方法得到。
export default { methods:{ importDoc(data, file, progress) { return this.$axios({ url: '<上传地址>', method: 'post', headers: { 'Content-Type': 'multipart/form-data' }, onUploadProgress: function(progressEvent) { const complete = ((progressEvent.loaded / progressEvent.total) * 100) | 0 progress(file, complete) }, data }) } }, upload(file){ var formData = new FormData() formData.append('file', file) file.status = 'uploading' const res = await importDoc(formData, file, this.progress) file.status = 'success' } }
4. Ant Design表单自定义组件,且支持校验
实际上这个很好做:
export default { props:['value'], model:{ prop: 'value', event: 'change' }, methods:{ changeValue(value){ this.$emit('change', value) } } }
这里需要注意,属性只能是value,这样也同时支持了value和v-model。然后假设有自定义组件demo.vue,则该表单组件可以这样写:
<demo v-decorator="['demo',{ 'initialValue': '', rules:[ { required: true, message: '请输入demo' } ] }]" />
5. store文件夹下的状态管理中,modules文件夹下所有文件的自动引入
这个可以采纳vue-element-admin中的写法:
替换strore/index.vue文件:
import Vue from 'vue' import Vuex from 'vuex' import getters from './getters' Vue.use(Vuex) const modulesFiles = require.context('./modules', true, /\.js$/) // 遍历modules文件 const modules = modulesFiles.keys().reduce((modules, modulePath) => { const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1') const value = modulesFiles(modulePath) modules[moduleName] = value.default return modules }, {}) const store = new Vuex.Store({ modules, getters }) export default store
这里需要注意,strore/modules下的文件不一定都是状态文件,所以可以对于value进行进一步判断,比如说判断value是否存在,是否有state这个属性。
原文地址:
https://www.jianshu.com/p/63d8ea97d932
