之前一段时间就大概把这个轮子弄好了,但是一直没有整理成文章。今天完善一下使用这个轮子的例子,顺便发现之前的几个bug,一同修复。看来后面还是需要把测试用例补充,否则很难避免使用起来遗漏的地方。目前这个工具托管在friendly-query,欢迎大家尝试使用并提issue。
背景
通常路由库可以处理从:/path1
=> path2
的跳转切换不同的页面;但是对/path1?foo=1
切换到/path2?foo=2
这种情况支持不是很好。如果页面对应的参数不增加到url中,用户在页面更改部分参数后,刷新页面,用户之前选择的这一部分参数就会被重置而不会选中后的页面处理。
在开发的过程中,想实现页面的部分表单参数同步到url的query参数;
- 当用户在浏览器点击前进或者后退按钮,则需要监听
popstate
的事件,然后根据回调事件来监听参数发生变化,根据更改后的url参数,再进行处理;参数发生改变可能只影响一部分,通常只需要触发该部分的回调就可以了; - 而且url的参数都是字符串,而实际可能还需要把字符串转换为需要的复杂数据类型;异步请求数据的时候,可能也需要把复杂的数据类型转换为字符串类型。
总体来说,这个工具就是解决以上两个问题
应用过程
上面图主要描述了三种情况:
- 初始化进入页面
enter page
- 页面数据发生改变
update data
- 用户点击浏览器前进/后退按钮
popstate
初始化进入页面
- 初始化进入页面的时候,需要调用
friendly-query
的init
方法生成实例 - 然后调用实例的
instance.load()
方法,该方法会从url上获取所有参数,并根据初始化传入的转换的数据类型,进行处理;返回的数据则为我们定义的数据类型 - 有时候还会加多一步,把对应的数据,存到对应的
store
- 调用异步请求方法,通常在方法里面需要调用
instance.convert
方法,把原有复杂的数据类型转换为字符串类型,把参数发送到后端
页面数据发生改变
通常这一步是用户触发更改不同的参数,前端根据不同的参数请求相应的数据
- 更新用户更改后的数据
- 通过
instance.convert
方法,获取新的数据转换后的字符串参数 - 根据生成的字符串参数,往
history
添加记录;添加记录这一步可能部分路由已支持;若不支持,则需要手动调用:history.pushState()
添加记录 - 调用异步请求方法,发送更新后的参数到后端,以获取新的展示内容
用户点击浏览器前进/后退按钮
- 用户触发前进/后退按钮,触发
popstate
事件(监听事件这一步,在init
的时候进行了绑定) - 调用
callback
函数;这个callback
函数为初始化init
传入的参数;若对参数进行分多组处理,则改变的参数对应的分组回调才被调用,例如:
1 2 3 4 5 6 7 8 9 10 11 |
init([{
type: {
foo: {}
},
callback () {}
}, {
type: {
bar: {}
},
callback () {}
}]) |
若在popstate
事件发生的时候,foo
参数相比旧url发生了改变,则foo
对应分组的callback
会调用;bar
对应的分组的callback
不会被调用;这种情况适用于页面有多个不同请求,并且这些请求都需要更新到url。
处理过程
从图可以看出,核心方法是load
与convert
的处理,是字符串数据与复杂数据之间桥梁。
instance.load
方法的核心是parse
的方法,parse
不是只有一个,而是对于多种不同的数据类型,从字符串转换为需要类型的转换函数,例如:Int
与Array
的parse
过程是不同的,parse
函数主要接收三个参数:
{String} str
url 对应参数的数据{Any} value
初始化传入该参数的默认数据{Object} option
该参数对应的数据类型的配置
与parse
对应的处理函数stringify
,在instance.convert()
实现,则是把不同的数据类型转换为字符串,例如把数组:['foo', 'bar']
转换为字符串:'foo,bar'
,该函数主要接收两个参数:
{Array|Object} groupQuery
需要从设定的数据类型转换为url参数所用的字符串类型{Boolean} isMerged
对转换后的数组字符串数据合并到一个对象中,默认合并
更改类型配置
默认支持的类型有:
Int
Float
String
Date
Boolean
Array
IntArray
FloatArray
每种类型都有自己的处理规则,例如Array
的配置当中,默认分隔符是逗号:separator: ','
;在parse
的时候,会根据逗号,
来分割字符串,在stringify
的时候,会根据逗号,
来拼接字符串。如果想更改为别的分隔符,例如更改为连接符号-
,则可以在init
的时候,传入第二个参数,对需要更改的规则进行处理:
1 2 3 4 5 6 7 8 9 10 |
init([
// ...
], {
Array: {
separator: '-'
},
IntArray: {
// ...
}
}) |
不同数据类型详细的配置,可以看这里
扩充类型
如果上述默认的类型都不能支持项目所用,可以使用extend
的全局方法来扩充所需要的类型,下面这个例子是扩充一个DateArray
的类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// dateformat 为一个日期格式类型处理的库
import dateformat from 'dateformat'
extend({
DateArray: {
// 从字符串转换为 Array 的方法
parse (str, value, option) {
if (!str) return value
return str.split(option.separator).map(item => {
return new Date(item)
})
},
// 从 Array 转换为字符串的方法
stringify (value, option) {
if (!value.length) return ''
return value.map(item => dateformat(item, option.format)).join(option.separator)
},
option: {
// 分隔符
separator: ',',
// 转换的时间格式
format: 'yyyy/mm/dd hh:MM'
}
}
}) |
需要注意的是,使用extend
扩充的类型,会全局影响,因此最好在项目入口定义新的类型
目前暂时只支持HTML5 history
的模式,暂还没支持hash
的路由模式
更详细的API可以查看这里