zoukankan      html  css  js  c++  java
  • 我的前端工具集(三)页面路由工具

    我的前端工具集(三)页面路由工具

     

    liuyuhang原创,未经允许禁止转载

    目录

    我的前端工具集

    1、什么是路由

      狭义上的路由就是我们常用的路由器了,不管是何种方式连接,都是转发的功能,

      实现了信道的扩展和加密功能。

      广义上的路由,实际上就是一个指挥员,根据目标的性质,进行映射的工具。

      可以是交通信号灯,可以是路牌,可以是网络路由器,可以是人生导师!

      本文中所说的路由,是指根据配置文件的设置,在点击指定内容的时候,对指定的div加载指定html的程序。

      我叫他页面路由,这种路由在很多前端框架中已经有实现了,功能也不少,学习的人很少去纠其作用机理。

      本文以自己的视角去实现一个路由,并尝试满足其各种功能。

    2、为什么要自己写前端路由

      为什么要自己写前端路由?我也不知道,总是觉得学一套语法,还不如自己实现一套语法,虽然是自己造轮子吧。

      使用别人的轮子总是有各种限制的,也无法根据需要进行功能的加减。

      何况,最主要的目的,还是学习。理论更重要!

    3、路由在架构中的重要性

      路由是及其重要的,可能这个词并不是很多见,但是类似的功能的东西比较多了,举几个例子:

      Struts是一种优秀的前端路由映射工具。

      SpringMVC是一种优秀的前端路由映射工具。

      常用的了,这两种对于java程序员来说太常用了。

      基本的原理,都是根据url请求,解析映射,然后根据配置文件的配置,访问到固定的java类,同时将封装

      的servlet的request和response作为参数传递进去。

      同时,一个好的路由架构,能够成功拆分文件,将任务分解(项目经理头疼的问题)

      虽然当前使用很多框架或者es6已经实现了模块化编程和聚合,会这些的人要的薪水也较高!

      发挥每个人的优势才是重要的。

      有一说叫让外部极简(客户方便),让内部复杂(编码结构复杂)。

      在这个思想的指导下,让大部分的开发者极简,让少部分的开发者复杂,也是一种架构思路了!

    4、我想认为路由应该有的功能与作用

      4.1.无刷加载页面功能:

        前端路由的核心功能了,要加载的页面直接加载,进行局部刷新,中间没有白屏。

      4.2.前置事件功能:

        执行某种效果之前执行的函数,这里的加载事件,是指一种AOP思想。

        主要是在路由跳转之前执行一定的函数,该函数应该会返回一个boolean,如果为true,则执行跳转。

      4.3.后置事件功能:

        好像大家都叫它钩子函数吧,即执行了某个功能以后,立即执行的函数。

      4.4.加载页面需要的js

      4.5.加载页面需要的css

    5.路由以何种形态出现?

      根据4中描述的功能,前端路由应该设置的内容如下:

      ①state:跳转名称,应该是唯一的,作为一个map的key的存在,应该是一个字符串。

      ②target:要改变的div的target,应该不是唯一的,多个target名字相同,应该对多个div进行同样的改变,应该是一个字符串。

      ③template:html模板,该路由起作用的时候,应该在该div内加载该html模板。应该是一个字符串。

      ④url:加载div并不是跳转,也不是重定向,url是没有变化的,此参数只是为了改变地址(决定废弃,感觉没啥用,以前做过)

      ⑤jsArr:多个js文件的名称,执行该路由的时候,同时加载多个js文件。

      ⑥cssArr:多个css文件的名称,执行该路由的时候,同时加载多个css文件。

      ⑦before:前置事件函数名,字符串格式,返回boolean,若为true则执行跳转,若为false则执行某提示函数或donothing。

      ⑧after:后置事件函数名,字符串格式,即执行路由后,执行该函数(该函数不应该为加载的js的函数,因为可能未加载成功导致after无法执行)

      

    6、细节部分的实现

      6.1.state

      其中state应为被点击的html元素的一个属性,页面加载的时候扫描路由配置,给state属性赋值,为state的名称。如:

      <div state="myStateName"></div>

      该功能应该配合一个listener来实现,假定配置文件中有key=“state”,value=“myStateName”,则listener应为:

      if(key=="state"){
        var temp = $("[state="+value+"]");
        if(temp.length>0){
          temp..click(function(){
            //执行路由功能
          })
        }    
      }

      6.2.before

      执行路由功能首先要先执行before的字符串,假定配置文件中有key=“before”,value=“test001()”,

      并且有

      function test001(str){
        console.log("test001");
        console.log(str);
      }
    
      //则执行before的应为:
      var flag = false;
      if(key=="before"){
        flag = eval("test001('hello test')")
      }

      6.3.template

      执行template加载之前,要先使用flag进行判断,假定配置文件中有key=“template”,value=“firstTemplate.html”,

      //这里下文中的loacl是获得http头,host,port等内容,暂且贴在这,自己调整
      var local = window.location.protocol + "//" + window.location.host + "/" + window.location.pathname.split("/")[1] + "/"
    
      if(key=="template"&&flag==true){
        $("div [target="+'myStateName'+"]").load(loacl+"firstTemplate.html");
      }

      6.4.jsArr

      加载js,前提是jsArr有内容。

      if (key=="jsArr"&&jsArr.length>0) {
        for (var i = 0; i <jsList.length; i++) {
          var script = document.createElement("script");
          script.setAttribute("type", "text/javascript");
          script.src = local +jsArr[i];
          document.getElementsByTagName("head")[0].appendChild(script);
        }
      }

      6.5.cssArr

      加载css,前提是cssArr有内容

      if (key=="cssArr"&&cssArr.length > 0) {
        for (var i = 0; i <cssArr.length; i++) {
          var link = document.createElement("link");
          link.setAttribute("rel", "stylesheet");
          link.href = local+cssArr[i];
          document.getElementsByTagName("head")[0].appendChild(link);
        }
      }

      6.6.after和before是相同的,不写了。

      6.7.以上的代码并非是实际代码,可以理解为伪代码吧!!

    7.代码实现

     

      说明,上述举例是举例,实际代码会有更多的判断内容和不同的格式,以下文为准,自行修改!

      7.1.准备配置数据,自己注意格式

        //测试时用的是springboot,非生产环境,无pathName
        var local = window.location.protocol + "//" + window.location.host + "/";
        /**
         * 准备配置文件,可单写为js文件
         * 数组格式,每个元素是一个json,代表一个路由事件配置
         */
        var routeConfig = [
            {
                state : "firstState", //state属性值,被点击的标签,执行名为state的路由
                target : "firstTarget", //执行路由将页面注入的目标,是一个div的target属性
                template : local + "view/firstPage.html", //执行路由注入的页面,一个html文件路径,不带local
                before : "firstFunBefore('my First Fun Before Here')", //路由执行之前的函数,决定是否执行
                after : "firstFunAfter('my First Fun After Here')", //路由执行之后的函数
                jsArr : [ local + "js/first01.js", local + "js/first02.js" ], //注入路由时注入的js文件列表,不带local
                cssArr : [ local + "css/first01.css", local + "css/first02.css" ], //注入路由时注入的css文件列表,不带local
            },
            {
                state : "secondState",
                target : "secondTarget",
                template : local + "view/secondPage.html",
                before : "secondFunBefore('my Second Fun Before Here')",
                after : "secondFunAfter('my Second Fun After Here')",
                jsArr : [ local + "js/second01.js", local + "js/second02.js" ],
                cssArr : [ local + "css/second01.css", local + "css/second02.css" ],
            }
        ]

      7.2.路由框架代码,只是路由跳转用的代码而已

    /**
         * 路由监听器初始化的函数,初始加载执行,每次有路由跳转后也要执行
         */
        function stateListener(routeConfig) {
            console.log(routeConfig);
            var setting = routeConfig;
            $("*[state]").unbind(); //移除state的监听器
            $("*[state]").click(function() { //注入监听器
                var state = $(this).attr("state");
                StateTo(state); //一旦点击即执行路由跳转函数
            })
        }
    
        /**
         * 路由跳转执行的函数
         * 传入参数为路由中的state的value
         */
        function StateTo(stateName) {
            console.log(stateName);
            //初始化变量
            var index,
                state,
                target,
                template,
                before,
                after,
                jsArr,
                cssArr,
                flag = false;
            //从配置数组中获取该元素的index
            for (var i = 0; i < routeConfig.length; i++) {
                if (stateName == routeConfig[i].state) {
                    index = i;
                    break;
                }
            }
            if (null != i) {
                state = routeConfig[index].state;
                target = routeConfig[index].target;
                template = routeConfig[index].template;
                before = routeConfig[index].before;
                after = routeConfig[index].after;
                jsArr = routeConfig[index].jsArr;
                cssArr = routeConfig[index].cssArr;
            }
            //检测state,target,template正确性
            if (checkEmpty(state) && checkEmpty(target) && checkEmpty(template) && $("[target = " + target + "]").length > 0) { //不为空且页面存在target
                flag = true;
            }
            //执行before并获得返回值
            flag = eval(before);
            //注入template,jsArr,cssArr
            if (flag == true) {
                //template注入
                $("div[target=" + target + "]").load(template);
                console.log("inject template : " + template);
                //js注入
                var jsArrLength = jsArr.length;
                if (jsArrLength > 0) {
                    for (var i = 0; i < jsArrLength; i++) {
                        var script = document.createElement("script");
                        script.setAttribute("type", "text/javascript");
                        script.src = jsArr[i];
                        document.getElementsByTagName("head")[0].appendChild(script);
                        console.log("inject js : " + jsArr[i]);
                    }
                }
                //css注入
                var cssArrLength = cssArr.length
                if (cssArrLength > 0) {
                    for (var i = 0; i < cssArrLength; i++) {
                        var link = document.createElement("link");
                        link.setAttribute("rel", "stylesheet");
                        link.href = cssArr[i];
                        document.getElementsByTagName("head")[0].appendChild(link);
                        console.log("inject js : " + cssArr[i]);
                    }
                }
                //如果有遮罩,全部删除,防止跳转后页面家假死,bootstrap中的遮罩...
                $(".modal-backdrop").remove();
            }
            //执行after
            if (flag == true) {
                flag = eval(after);
                stateListener(routeConfig);//重置路由监听
            }
            //==========
            //检验字符串是否为空的内部工具
            function checkEmpty(str) {
                return ('' != str && null != str && 'undefinded' != typeof str);
            }
        }

      7.3.准备测试的before和after函数代码

        //准备测试用的四个before和after函数
        function firstFunBefore(str) {
            console.log(str);
            return true; //允许路由跳转
        }
        function firstFunAfter(str) {
            console.log(str);
        }
        function secondFunBefore(str) {
            console.log(str);
            console.log("返回值为false,不执行路由!");
            return false; //不允许路由跳转
        }
        function secondFunAfter(str) {
            console.log(str);
        }

      7.4.初始化与测试按钮函数代码

        //init
        $(function() {
            stateListener(routeConfig)
        })
        
        function test(){
            StateTo("firstState");
        }

      7.5.html代码,需要引入jquery和bootstrap,版本没啥要求

    <div class="row">
            <div class="col-lg-12">
                <!-- bar -->
                <div class="col-lg-2">
                    <div state="firstState">
                        <span><h3>First</h3></span>
                    </div>
                    <div state="secondState">
                        <span><h3>Second</h3></span>
                    </div>
                    <div>
                        <button class="btn btn-default" onclick="test()">测试StateTo函数的按钮</button>
                    </div>
                </div>
                <!-- something -->
                <div class="col-lg-3">
                    <div target="firstTarget">
                        <h2>First Page</h2>
                    </div>
                    <div target="secondTarget">
                        <h2>Second Page</h2>
                    </div>
                </div>
            </div>
        </div>

       7.6.测试用的html,作为template

      firstPage.html

    <div>
        <h2>The First Page Injected</h2>
    </div>

    8.运行测试

      8.1.页面截图

      8.2.点击First后的截图

      8.3.点击测试按钮后的截图

       8.4.两次操作的时候,控制台的打印结果,有些内容自己去写吧!

      9.总结

        效果已经达到,js和css文件没有写,注入是能够成功的,自行去写。

        对于统一性的功能(如遮罩,如进度条,如加载错误信息,可以统一编写,直接写在这个框架内,也可以另增加init模块,

        使得这个模块在before或者fater或者其他地方选择执行,类似于AOP思想,统一编写,减少些业务的程序员的工作量。)

    以上!

      

  • 相关阅读:
    LInq 与lambda表达式
    Java内存模型-jsr133规范介绍(转)
    Java多线程之Lock的使用(转)
    有一个很大的整数list,需要求这个list中所有整数的和,写一个可以充分利用多核CPU的代码,来计算结果(转)
    CountDownLatch
    CyclicBarrier
    java设计模式演示样例
    2012毕业找工作记录点滴
    Objective-C语法之代码块(block)的使用
    权限表设计之代码解析
  • 原文地址:https://www.cnblogs.com/liuyuhangCastle/p/9763576.html
Copyright © 2011-2022 走看看