前情提要
前端开发中会难免会遇到复杂表单提交的情况,我自己也实现过一个非常非常巨大的表单,是关于个人信息录入的。数据结构大体如下;
{
basic: {
name: { familyName, givenName},
birthday: new Date(),
sex: '', //28 different types
phone: [],
address: [/*{nation, zip, prefecture, line1, line2,... }*/ ],
additionItems: [],
},
education: [/*{category, school, degree, department, major ...} */],
...
}
我当时的实现是把各级表单项动态存入vuex里,点击提交后一并发给后端。但这里的问题是,我需要写很多很多的action和mutation方法;如果是education
这样的数组结构的话,还需要动态支持增删改操作;数据结构的维护变得及其麻烦,此外还得注意类型检查。头痛一番后,业务是跑起来了,但是代码一直很脆,bug不断。这时候就特别怀念数据库了,假如能有表单结构来管理这类数据就好了。
vuex-orm
最近我在github上看到了一个叫vuex-orm的项目,star超过700,顿生敬意。它是vuex的一款插件,顾名思义,实现了Vuex Store的对象关系映射。我这里就稍事介绍一下用法:
创建Model
ORM需要先定义对象的数据结构(schema),vuex-orm的实现方式是继承它的Model类:
// @/model/User.js
import { Model } from '@vuex-orm/core'
export default class User extends Model {
// This is the name used as module name of the Vuex Store.
static entity = 'users'
// list all the fields (schema)
static fields () {
return {
id: this.increment(), // id increases automatically
name: this.string(''), // define as a string and the defualt value is ''
}
}
}
插件安装
接着是给vuex安装插件。如下所示,我们先在vuex-orm的database里注册自定义的model,然后把它安装到Vuex store里。
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
import VuexORM from '@vuex-orm/core'
import User from '@/model/User'
Vue.use(Vuex)
// Create a new database instance.
const database = new VuexORM.Database()
// Register Models to the database.
database.register(User)
// Create Vuex Store and register database through Vuex ORM.
const store = new Vuex.Store({
plugins: [VuexORM.install(database)]
})
export default store
Model in VUE
这时候就可以在vue里自在地使用model了,
// user.vue
<script>
import UserModel from '@/model/User'
export default {
...
created () {
const data = [{name: 'Onion'}, {name: 'Garlic'}]
UserModel.create({data})
// this.$store.dispatch('entities/users/create', {data} )
},
}
</script>
用vue devtool查看一下state,结构如下:
{
entities: {
$name: 'entities',
users: {
$connection: 'entities',
$name: 'users',
data: {
1: {
$id: 1,
id: 1,
name: 'Onion'
},
2: {
$id: 2,
id: 2,
name: 'Garlic'
}
}
}
}
}
嗯,数据成功写入,user的id自增从1开始。事实上UserModel.create({data})
只是封装了this.$store.dispatch('entities/users/create', {data} )
方法。Vuex-orm加载后会帮你创建一系列的getters
和mutations
方法,它们分别映射了Model
的增删改查。如果不想import UserModel
,你也可以直接使用相应的this.$store.dispatch
实现数据读写。
CRUD
再列几个读写方法
-
create
UserModel.insert({data: {name: 'Ginger'} })
-
update
UserModel.update({ where: 2, name: 'Ginger', })
-
delete
delete可以直接删除id指向的对象,也可以使用where语句。
UserModel.delete(1) UserModel.delete({ where: (obj) => obj.id === 1 })
-
retrive data
读取数据可以用
all
、find
、query
等方法,query
甚至可以添加where
、orderBy
、limit
等语句。const user = UserModel.query().where('name', 'Onion').get()
关系映射
vuex-orm还实现了一套主外键的关系映射,有One To One
、 One to Many
、Many to Many
等八九种关联。写一个简单的例子:
class Todo extends Model {
static entity = 'todos'
static fields () {
return {
id: this.increment(),
user_id: this.number(0),
title: this.string(''),
done: this.boolean(false),
}
}
}
class User extends Model {
static entity = 'users'
static fields () {
return {
id: this.increment(),
name: this.string(''),
todos: this.hasMany(Todo, 'user_id')
}
}
}
如上所示,我们将User与Todo一起关联,hasMany
表示一对多的关系,user_id
为外键。创建一个todo如下,user_id
是1,就是Onion这个人。
TodoModel.insert({
data: {
title: 'Hello',
user_id: 1,
}})
再看一下state
users: {
$connection: 'entities',
$name: 'users',
data: {
1: {
$id: 1,
id: 1,
name: 'Onion',
todos: [
1: {
$id: 1,
id: 1,
title: 'Hello',
user_id: 1,
done: false,
}
]
},
...
}
}
嗯,成功加到user的todos数组里了。
小结
除了上面一些基本功能外,vuex-orm还有其他一些高级用法,这里就不一一介绍了,大家有兴趣的话可以去官方wiki查看。
自从前端渲染框架和flux设计模式流行后,现代web开发把主要的业务实现放在了前端。后端愈发轻领域设计,更像是一个单纯的数据读写接口;rest设计也变得大颗粒化。新工具的不断跟进在这里起到了关键性的作用。
Vuex-orm也来自于Redux相关插件的设计理念。以前我还断言Graphql出现后Redux之类的state管理工具会开始式微,现在想想还是自己太狭隘了。无知与狭隘共生。
学无止境,互勉。