之前先接觸了ElementUI,然后后面又接觸了Ant Design,在這里做個對比,希望通過對比這兩前端ui框架,能夠更加深入的了解和使用這些框架。
表格對比
????首先,通過一張表格來對比這兩框架的異同吧
對比項 | ElementUI | Ant Design |
---|---|---|
logo | ||
官網(wǎng)/文檔 | vue: https://element.eleme.cn | vue: https://www.antdv.com/docs/vue react: https://ant.design/docs/react/introduce-cn |
團隊 | 餓了么 | 螞蟻金服 |
簡介 | 基于 Vue 2.0 的桌面端組件庫 | 開發(fā)和服務于企業(yè)級后臺產(chǎn)品 |
最新版本 | 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
這個框架更加適合于面向外部開發(fā)。
而作為對比的Ant Design
,也有一定的優(yōu)勢。
從功能上來講,后者更加齊全。比如回到頂部組件:樹形選擇:<a-tree-select />
,Ant Design
更加適合管理平臺的開發(fā)。
????從實用上來看:
對于pro版本,vue-element-admin允許初始化基礎版,而ant-design-pro這個初始化后有大量的例子,開發(fā)之前還得把例子刪掉,這點感覺不太好。
總之,兩個框架的pro版本做的都非常棒,但個人更加傾向于ant-design
,畢竟組件多占有非常大的優(yōu)勢。
????總之:
如果是想快速上手,又希望ui更加漂亮,建議用elementUI
;如追求的是比較復雜的后臺管理平臺,可以考慮采用ant-design-pro
,而且ant-design-pro
無論表格還是表單,都是高度可配置化的。這點相對于elementUI
來說,ant-design-pro
雖然稍微復雜了點,但是換來更大的便利。
一些建議和經(jīng)驗
以下代碼部分都是vue,不涉及react
1. elementUI
的菜單組件<el-dropdown>
在手機端點擊會回彈的問題
修改trigger
為click
的方式,因為默認hover
時,手機上并不能有很好的體驗。
2. elementUI
如何實現(xiàn)通用表單的配置
采用form-create這個庫可以很方便的實現(xiàn)表單完全的json配置
主頁:http://www.form-create.com/
git地址:https://github.com/xaboy/form-create
另外還有一種方式,就是利用Vue
的插槽實現(xiàn),這種方式也適合ant-design
的可配置表單的實現(xiàn)。
ps: 注意插槽名稱不要帶數(shù)字,最好不要
下面是利用插槽實現(xiàn)可配置的例子,這里以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
文件夾下的狀態(tài)管理中,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
下的文件不一定都是狀態(tài)文件,所以可以對于value進行進一步判斷,比如說判斷value是否存在,是否有state這個屬性。