jingzhiMo / jingzhiMo.github.io

http://jingzhimo.github.io/
15 stars 1 forks source link

造了一个路由切换的轮子 #10

Open jingzhiMo opened 5 years ago

jingzhiMo commented 5 years ago

之前一段时间就大概把这个轮子弄好了,但是一直没有整理成文章。今天完善一下使用这个轮子的例子,顺便发现之前的几个bug,一同修复。看来后面还是需要把测试用例补充,否则很难避免使用起来遗漏的地方。目前这个工具托管在friendly-query,欢迎大家尝试使用并提issue。

背景

通常路由库可以处理从:/path1 => path2的跳转切换不同的页面;但是对/path1?foo=1切换到/path2?foo=2这种情况支持不是很好。如果页面对应的参数不增加到url中,用户在页面更改部分参数后,刷新页面,用户之前选择的这一部分参数就会被重置而不会选中后的页面处理。 在开发的过程中,想实现页面的部分表单参数同步到url的query参数;

  1. 当用户在浏览器点击前进或者后退按钮,则需要监听popstate的事件,然后根据回调事件来监听参数发生变化,根据更改后的url参数,再进行处理;参数发生改变可能只影响一部分,通常只需要触发该部分的回调就可以了;
  2. 而且url的参数都是字符串,而实际可能还需要把字符串转换为需要的复杂数据类型;异步请求数据的时候,可能也需要把复杂的数据类型转换为字符串类型。

总体来说,这个工具就是解决以上两个问题

应用过程

image.png

上面图主要描述了三种情况:

  1. 初始化进入页面 enter page
  2. 页面数据发生改变update data
  3. 用户点击浏览器前进/后退按钮popstate

初始化进入页面

页面数据发生改变

通常这一步是用户触发更改不同的参数,前端根据不同的参数请求相应的数据

用户点击浏览器前进/后退按钮

init([{
    type: {
        foo: {}
    },
    callback () {}
}, {
    type: {
        bar: {}
    },
    callback () {}
}])

若在popstate事件发生的时候,foo参数相比旧url发生了改变,则foo对应分组的callback会调用;bar对应的分组的callback不会被调用;这种情况适用于页面有多个不同请求,并且这些请求都需要更新到url。

处理过程

image.png

从图可以看出,核心方法是loadconvert的处理,是字符串数据与复杂数据之间桥梁。

instance.load方法的核心是parse的方法,parse不是只有一个,而是对于多种不同的数据类型,从字符串转换为需要类型的转换函数,例如:IntArrayparse过程是不同的,parse函数主要接收三个参数:

parse对应的处理函数stringify,在instance.convert()实现,则是把不同的数据类型转换为字符串,例如把数组:['foo', 'bar']转换为字符串:'foo,bar',该函数主要接收两个参数:

更改类型配置

默认支持的类型有:

  1. Int
  2. Float
  3. String
  4. Date
  5. Boolean
  6. Array
  7. IntArray
  8. FloatArray

每种类型都有自己的处理规则,例如Array的配置当中,默认分隔符是逗号:separator: ',';在parse的时候,会根据逗号,来分割字符串,在stringify的时候,会根据逗号,来拼接字符串。如果想更改为别的分隔符,例如更改为连接符号-,则可以在init的时候,传入第二个参数,对需要更改的规则进行处理:

init([
    // ...
], {
    Array: {
        separator: '-'
    },
    IntArray: {
        // ...
    }
})

不同数据类型详细的配置,可以看这里

扩充类型

如果上述默认的类型都不能支持项目所用,可以使用extend的全局方法来扩充所需要的类型,下面这个例子是扩充一个DateArray的类型:

// 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可以查看这里