原生 js前端路由系统实现2之代码可读性 和可扩展性

前一篇 尝试着实现了前端路由的部分功能 原生 js前端路由系统实现1


代码可读性

影响代码可读和易于理解的因素

1 代码规范 2缩进空格 3减少函数嵌套层次 4函数单一职责 5。。。。

以上这些顶多只能在外观上面看起来清晰(也不一定真的清晰),但随着代码量的增大  代码依然非常难读和易于理解

如果给你几十行代码,程序员通过琢磨也能短时间搞清楚,如果给你上万行代码 即便技术大牛也得花大量的时间去阅读和调试才能看懂

影响代码可读性的主要原因是 代码的多少

以下是上次实现路由的代码 去掉兼容AMD等库的代码 是百来行,初次看的时候依然有点吃力,需要花点时间才能看懂  之后会把它改成更容易理解和看懂的代码

!function(root,fn){
	if (typeof exports === ‘object‘) {
        // Node.
        module.exports = fn.call(root);
    } else if (typeof define === ‘function‘ && define.amd) {
        // AMD. Register as an anonymous module.
        define(function(){ return fn.call(root) });
    } else {
        // Browser globals (root is window)
        root.diqye = fn.call(root);
    }
}(this,function(){
    var TYPE=(function(){
            var r={},types=[‘Arguments‘,‘Function‘,‘String‘,‘Number‘,‘Date‘,‘RegExp‘,‘Error‘,‘Null‘];
            for(var i=0,t;t=types[i++];){
                !function(t){
                    r[‘is‘+t]=function(obj){
                        return Object.prototype.toString.call(obj) === ‘[object ‘+t+‘]‘;
                    }
                }(t)
            }
            return r;
        })();
    //前端路由 本来打算写成一个函数,但路由对象只需要全局存在一个即可 并没有发现需要多个对象存在的场景
    var route=(function(){
        var p={
            //全局拦截器,本来想用链表来实现,但基于Javascript的特性用数组实现更加简单 和易用
            headUseFns:[],
            gets:[],
            type:{
                ‘number‘:/^\d+$/,
                ‘string‘:/^[^\/]+$/,
                ‘date‘:/^[0-9]{6,6}$/
            }
        };
        function getPath(url){
            var path=url.split("#")[1];
            if(!path)return "/";
            if(path.charAt(0)!="/")path="/"+path;
            return path;
        }

        function use(fn){
            p.headUseFns.push(fn);
        }

        function hashchange(path){
            var req={path:path},hlen=p.headUseFns.length;
            if(hlen==0){
                doother(req);
                return;
            }
            //执行拦截器链
            !function intec(i){
                if(i==hlen){
                    doother(req);
                    return;
                }
                p.headUseFns[i](req,function(){
                    intec(i+1);
                });
            }(0);
        }

        function doother(req){
            var path=req.path,hlen=p.gets.length;
            var a=path.split(‘?‘);
            if(a[1])path=a[0];
            path=path.split(‘//‘).join(‘/‘);
            if(path.length!=1&&path.charAt(path.length-1)==‘/‘)path=path.substr(0,path.length-1);
            //执行拦截器链
            !function intec(i){
                if(i==hlen){
                    return;
                }
                if(p.gets[i].match(path,req)){
                    p.gets[i].fn(req,function(){
                        intec(i+1);
                    });
                }else{
                    intec(i+1);
                }

            }(0);
        }

        function get(context,fn){
            var match=null;
            if(TYPE.isFunction(context))match=context;
            else if(TYPE.isRegExp(context)){
                match=function(path,req){
                    var para=context.exec(path);
                    if(para){
                        req.para=para;
                    }
                    return para;
                }
            }else if(TYPE.isString(context)){
                match=stringmatch(context);
            }
            var getter={
                match:match,
                fn:fn
            };
            p.gets.push(getter);
        }
        function stringmatch(context){
            var a=context.split(‘:‘),b=context.split(‘*‘);
            if(a.length==1&&b.length==1){
                return function(path,req){
                    return path==context;
                }
            }else if(a.length!=1){
                var reg=p.type[a[1]];
                if(reg==null)reg=new RegExp(a[1]);
                return function(path,req){
                    var para=path.substr(a[0].length);
                    var r=path.indexOf(a[0])==0&&reg.test(para);
                    if(r)req.para=para;
                    return r;
                }
            }else if(b.length!=1){
                return function(path,req){
                    return path.indexOf(b[0])==0;
                }
            }
        }
        function start(){
            window.onhashchange=function(){
                var path=getPath(location.href);
                hashchange(path);
            }
            var path=getPath(location.href);
            hashchange(path);
        }
        function to(path){
            window.location.hash=path;
        }
        
        return {
            start:start,
            use:use,
            get:get,
            to:to,
            p:p
        }
    })();
    return {
        type:TYPE,
        route:route
    }
});

代码不管怎么写 最终都是操作数据的 没有数据代码啥都不是(即使语法级别的 也算是一个语法树的数据) 所以看代码的时候 一般脑子中会勾勒出一幅数据图

以上代码主要提供几个api  start,use,get,to 他们维护的数据是p

var p={
            headUseFns:[],
            gets:[],
            type:{
                ‘number‘:/^\d+$/,
                ‘string‘:/^[^\/]+$/,
                ‘date‘:/^[0-9]{6,6}$/
            }
        };

p.headUseFns 是一个数组 里面存放的全部是拦截器 由use函数 和 hashchange事件去维护

p.gets 类似上面 不过是由get函数维护  p.type是为p.gets服务的

当搞明白这些数据的意义时候 不需要看代码了

上面的代码之所以看起浪费时间的地方(相对于来说) 个人觉得就是维护的数据是多个 当一段代码操作数据的时候脑中肯定会想 这个数据的来龙去脉 操作两个数据的时候就要在脑中想两个数据的环境 可惜大脑是单线程的 当在脑中关注的东西超过1个的时候 大脑就会增加负担 同时关注的东西越多大脑就越容易混乱

我觉得影响代码可读性的第二个因素是代码所维护的数据个数 也就是大脑关注的数据个数

上述代码我要变的更易读,我需要把代码变少,代码变少我需要把功能变少。功能变少并不是说要去掉功能而是做一个通用的功能 后续的功能通过可扩展去添加 刚好此时的use函数 可以做为一个通用函数 get函数可以基于use去做 代码简化成

var route=(function(){
        var p={
            intes:[]
        };
        function getPath(url){
            var path=url.split("#")[1];
            if(!path)return "/";
            if(path.charAt(0)!="/")path="/"+path;
            return path;
        }
        function use(fn){
            p.intes.push(fn);
        }
        function hashchange(path){
            var req={path:path},hlen=p.intes.length;
            if(hlen==0){
                doother(req);
                return;
            }
            //执行拦截器链
            !function intec(i){
                if(i==hlen){
                    return;
                }
                p.intes[i](req,function(){
                    intec(i+1);
                });
            }(0);
        }
        function start(){
            window.onhashchange=function(){
                var path=getPath(location.href);
                hashchange(path);
            }
            var path=getPath(location.href);
            hashchange(path);
        }
        
        return {
            start:start,
            use:use
        }
    })();

可扩展性

当你的代码可读性自认为已经达标的情况下 此时如果增加代码很容易会对原先代码造成污染 久之可能自己都看不懂了(比较实际的情况是人离职留给新来的了  哈哈 …………)这个时候比较成熟的解决法方案就是通过扩展来做其它的东东了

我需要对库本身的API进行扩展  提供一个扩展API函数 这个函数的名字需要参考别的框架中的名字  使用别的流行的框架功能函数命名也可以提高阅读速度 js中扩展api基本上是两个函数 一个是以jquery为主的extends函数  一个是mix 个人比较有洁癖 喜欢用短名字 mix ~~~~~~

//前端路由 本来打算写成一个函数,但路由对象只需要全局存在一个即可 并没有发现需要多个对象存在的场景
    var route=(function(){
        var p={
            intes:[]
        };
        function getPath(url){
            var path=url.split("#")[1];
            if(!path)return "/";
            if(path.charAt(0)!="/")path="/"+path;
            return path;
        }
        function use(fn){
            p.intes.push(fn);
        }
        function hashchange(path){
            var req={path:path},hlen=p.intes.length;
            if(hlen==0){
                doother(req);
                return;
            }
            //执行拦截器链
            !function intec(i){
                if(i==hlen){
                    return;
                }
                p.intes[i](req,function(){
                    intec(i+1);
                });
            }(0);
        }
        function start(){
            window.onhashchange=function(){
                var path=getPath(location.href);
                hashchange(path);
            }
            var path=getPath(location.href);
            hashchange(path);
        }
        //扩展API
        function mix(obj){
            for(var key in obj){
                result[key]=obj[key];
            }
        }
        var result={
            start:start,
            use:use,
            mix:mix
        }
        return result;
    })();

通过mix实现get函数功能

 //get 路由实现
    !function(mix){
        //获取处理后的path  去掉问好 和多余的/
        function pathfn(path){
            var a=path.split(‘?‘);
            if(a[1])path=a[0];
            path=path.split(‘//‘).join(‘/‘);
            return path;
        }
        //function interceptor
        function get1(fn,cb){
            return function(req,next){
                if(fn(pathfn(req.path)))cb(req,next);
                else next();
            }
        }
        //regExp interceptor
        function get2(reg,cb){
            return function(req,next){
                var para=reg.exec(pathfn(req.path));
                if(para){
                    req.para=para;
                    cb(req,next);
                }else next();
            }
        }
        //:xxx  xxx是可以扩展的
        function get3(ps,cb){
            var reg=get.type[ps[1]];
            return function(req,next){
                var path=pathfn(req.path);
                var para=path.substr(ps[0].length);
                if(pathfn(req.path).indexOf(ps[0])==0&&reg.test(para)){
                    req.para=para;
                    cb(req,next);
                }else next();
            }
        }
        //通配符  /xxxx*  *后面不允许有字符
        function get4(os,cb){
            return function(req,next){
                if(pathfn(req.path).indexOf(os[0])==0)cb(req,next);
                else next();
            }
        }
        //string interceptor
        function get5(path,cb){
            return function(req,next){
                if(path==pathfn(req.path))cb(req,next);
                else next();
            }
        }
        function get(path,cb){
            //匹配路径支持的5中情况
            if(TYPE.isFunction(path))this.use(get1(path,cb));
            if(TYPE.isRegExp(path))this.use(get2(path,cb));
            if(!TYPE.isString(path))return;
            var a=path.split(‘:‘);
            if(a.length!=1){
                this.use(get3(a,cb));
                return;
            }
            var b=path.split(‘*‘);
            if(b.length!=1){
                this.use(get4(b,cb));
                return;
            }
            this.use(get5(path,cb));
        }
        get.type={
            ‘number‘:/^\d+$/,
            ‘string‘:/^[^\/]+$/,
            ‘date‘:/^[0-9]{6,6}$/
        };
        mix({
            get:get
        });
    }(route.mix);

以上主要是支持了5中路由的匹配方式, 而这五中方式 函数取名 get1 get2 get3 get4 get5 但看函数名字就能知道属于统一类的  只是做不同的分支

以上所有代码地址 https://git.oschina.net/diqye/route.js

后续会扩展querystring功能 即对 url中的参数 和json对象互相转换

时间: 2024-08-13 17:56:26

原生 js前端路由系统实现2之代码可读性 和可扩展性的相关文章

原生 js前端路由系统实现3之代码 构建工具 和 querystring功能

构建 目前前端构建工具流行的是 grunk.js 功能是大而全,但往往大而全的东西为了多样性 需要做额外的配置  我还是想要有一个专门为自己特性项目而生构建工具 我不想加载第三方的node模块,也不想写配置 ,想要的功能也很简单只是根据文件名字进行合并 build.js 名字规则: a-b-c.js 或者 a-b-c.txt a代表文件合并的顺序(这个顺序跟文件显示的顺序一致) 第一固定为0 最后固定为e  b代表合并后的文件名字  c代表自己的文件名字 以下代码会 寻找当前目录下src目录中的

原生 js前端路由系统实现1

在看了百度手机小说网站源码之后  突然兴起想自己实现一个基于网页hash技术的路由系统 源码 我放到oscgit上面了 地址: https://git.oschina.net/diqye/route.js 适用场景: 局部刷新页面    通过ajax局部刷新页面会有一个问题就是 分享url或者刷新页面时不能记录 当前网页的状态  前端路由可解决这个问题 期望的路由: 1  当访问网页 http://localhost/index.html 或者 http://localhost/index.ht

原生 js前端路由系统实现4之 低版本ie兼容性

经测试ie7 上面不支持onhashchange事件,导致本路由解析失效. 解决办法 使用定时器监听url的变化 然后触发hashchange src目录下新建文件 1-ieadaptor-all.js  此文件经过build.js会在js目录下生成ieadaptor.js文件(build.js在上一遍有介绍) !function(){ var appv=window.navigator.appVersion; if(appv.indexOf('MSIE 7')===-1&&appv.in

原生JS判断手机系统

点击图片,判断手机操作系统,根据手机系统跳转不同链接. function imgHref(){ var userAgent = navigator.userAgent; var isAndroid = userAgent.indexOf('Android') > -1 || userAgent.indexOf('Adr') > -1; //android终端 var isiOS = !!userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //io

原生js 获取路由参数

方法一//获取url参数function getQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); var r = window.location.search.substr(1).match(reg); if (r != null) return decodeURI(r[2]); return null; }va

原生js仿梦幻时间查看活动(代码临时存储)

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.4/??flexible_css.js,flexible.js"></script> <title>js基础</title>&l

原生 JS Ajax,GET和POST 请求实例代码

javascript/js的ajax的GET请求代码如下所示: <script type="text/javascript"> /* 创建 XMLHttpRequest 对象 */ var xmlHttp; function GetXmlHttpObject(){ if (window.XMLHttpRequest){ // code for IE7+, Firefox, Chrome, Opera, Safari xmlhttp=new XMLHttpRequest();

Web开发中 前端路由 实现的几种方式和适用场景

浅析Web开发中前端路由实现的几种方式 主题 Web开发 故事从名叫Oliver的绿箭虾`说起,这位大虾酷爱社交网站,一天他打开了 Twitter ,从发过的tweets的选项卡一路切到followers选项卡,Oliver发现页面的内容变化了,URL也变化了,但为什么页面没有闪烁刷新呢?于是Oliver打开的网络监控器(没错,Oliver是个程序员),他惊讶地发现在切换选项卡时,只有几个XHR请求发生,但页面的URL却在对应着变化,这让Oliver不得不去思考这一机制的原因- 叙事体故事讲完,

浅析Web开发中前端路由实现的几种方式

故事从名叫Oliver的绿箭虾`说起,这位大虾酷爱社交网站,一天他打开了 Twitter ,从发过的tweets的选项卡一路切到followers选项卡,Oliver发现页面的内容变化了,URL也变化了,但为什么页面没有闪烁刷新呢?于是Oliver打开的网络监控器(没错,Oliver是个程序员),他惊讶地发现在切换选项卡时,只有几个XHR请求发生,但页面的URL却在对应着变化,这让Oliver不得不去思考这一机制的原因… 叙事体故事讲完,进入正题.首先,我们知道传统而经典的Web开发中,服务器端