GithubHelp home page GithubHelp logo

mmrouter's Introduction

mmRouter

avalon的三柱臣之一( 路由,动画,AJAX)

avalon1.* 与mmRouter的文档见这里

这里的介绍只针对 avalon2与新的mmRouter, mmHistory

avalon2使用 webpack进行打包

var webpack = require('webpack')
var path = require('path')


module.exports = {
    entry: {
        mmRouter: './src/mmRouter',
        example1: './src/example1', //这里你要打包的入口文件,里面会引入mmRouter,avalon2
        example2: './src/example2'
    },
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name].js',
    }, //页面引用的文件

    plugins: [
    ],
    resolve: {
        extensions: ['.js', '', '.css']
    }
}

example1.js

//加载node_modules中的avalon2/dist/avalon.js
var avalon = require('avalon2')
//加载编译好的mmRouter
require('../dist/mmRouter')
//定义VM
var vm = avalon.define({
    $id: 'test',
    currPath: ''
})
//添加路由规则
avalon.router.add("/aaa", function (a) {
    vm.currPath = this.path
    // this里面能拿到如下东西:
    // path: 路径
    // query: 一个对象,就是?后面的东西转换成的对象
    // params: 一个对象, 我们在定义路由规则时,那些以冒号开始的参数组成的对象
})
avalon.router.add("/bbb", function (a) {
    vm.currPath = this.path
})
avalon.router.add("/ccc", function (a) {
    vm.currPath = this.path
    
})
avalon.router.add("/ddd/:ddd/:eee", function (a) {//:ddd为参数
    vm.currPath = this.path
    console.log(this.query)
    console.log(this.params)
})
//启动路由监听
avalon.history.start({
    root: "/mmRouter"
})
//启动扫描机制,让avalon接管页面
avalon.scan(document.body)

avalon.router.add的第二参数回调,可以返回一个字符串,作为新的hash来改写地址栏,详见example6, example7

mmHistory.start方法的配置项

root: "/", //根路径
html5: false, //是否使用HTML5 history 
hashPrefix: "!",//
autoScroll: false //滚动

mmRouter的方法

###avalon.router.add(rule, cb) 添加 一个路由规则与对象的回调, cb为rule规则中捕捉的参数

###avalon.router.error(cb)

当目标页面不匹配我们所有路由规则时, 就会执行此回调.有点像404

###avalon.router.navigate(hash, mode)

mode 0或undefined, 不改变URL, 不产生历史实体, 执行回调 1, 改变URL, 不产生历史实体, 执行回调 2, 改变URL, 产生历史实体, 执行回调

手动触发对应的回调 (见example5)

mmrouter's People

Contributors

csbun avatar dolymood avatar dsonet avatar fanliao avatar gogoyqj avatar ilife5 avatar linkfly6 avatar litor avatar maogm12 avatar michaeljayt avatar rubylouvre avatar shuxiang avatar urrrich avatar xuzicn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mmrouter's Issues

内存泄漏问题

用IE调试的时候发现如果在页面上只调用history方法,一直刷新整个页面,内存会持续增长,看信息应该是这个方法的问题:

        Router.prototype.setLastPath = function (path) {
            if (cookieID) {
                clearTimeout(cookieID)
                cookieID = null
            }
            localStorage.setItem("msLastPath", path)
            cookieID = setTimeout(function () {
                localStorage.removItem("msLastPath")
            }, 1000 * 60 * 60 * 24)
        }

应该是最后的那个timer没有销毁~

avalon.router.navigate 失效

问题复现代码如下:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>demo test</title>
    <script src="../avalon.js"></script>
</head>
<style>
    * {word-wrap: break-word;margin:0;padding:0}
    body {background: #f9f9f9;font-size: 14px;}
    .tb {margin-top: 10px;padding-left: 5px;line-height: 30px;border-bottom: 1px solid #CDCDCD;}
    .cl {zoom: 1;}
    .cl::after {content: ".";display: block;height: 0px;clear: both;visibility: hidden;}
    .contents {width: 862px;margin:30px 0 0 20px;background: #FFF;}
    ul li{list-style: none;}
    .tb li {float: left;margin: 0 3px -1px 0;}
    .tb li {_width: 120px;*width:120px: ;}
    a {color: #333;text-decoration: none;}
    .tb a {display: block;padding: 0 10px;border: 1px solid #CDCDCD;background: #E5EDF2;}
    .tb .a a{border-bottom-color: #fff;background: #FFF;font-weight: 700;}
    .tb li {_width:120px;*width:120px;}
    a.tbtxt1 {float:left;border-right-width:0;}
    a.tbtxt2 {float:left;}
    a.tbclose {float:left;border-left-width:0;width:12px;}
</style>
<body>
<div class="contents" ms-controller="box">
    <ul class="tb cl">
        <li ms-repeat="tab.page" ms-class="a:tab.currpage===el.name"><a ms-href="el.url" ms-class-1="tbtxt1:el.name!=='tab1'" ms-class-2="tbtxt2:el.name==='tab1'">{{ el.title }}</a><a ms-if="el.name!='tab1'" ms-click="tab_close(el.name)" href="javascript:;" class="tbclose">X</a></li>
    </ul>
    <div id="main" class="cl" style="margin:10px 5px 5px 10px;width:500px;height:300px">
        <p>{{tab.content}}</p>
    </div>
    <div>
        <a href="#/tab1">显示tab1</a> <a href="#/tab2">显示tab2</a> <a href="#/tab3">显示tab3</a>
    </div>
</div>
<script>
require("mmRouter", function () {
    var model = avalon.define('box', function(vm) {
        vm.tab = {currpage:'tab1',content:'tab1', page:[{name:'tab1', title:'选项卡一',url:'#/tab1'}]};
        vm.tab_close = function (tname) {
            while(true) {
                var l = vm.tab.page.length;
                if (l===1) break;
                loaded[vm.tab.page[l-1].name] = 0;
                if (vm.tab.page[l-1].name===tname) {
                    vm.tab.page.pop();
                    break;
                } else {
                    vm.tab.page.pop();
                }
            }
            avalon.router.navigate("/tab1");
            vm.tab.currpage = 'tab1';
        }
    });
    var loaded = {};
    avalon.router.get("/tab1", function(a) {
        var page = 'tab1';
        model.tab.content = "页面:"+page;
        model.tab.currpage = page;
    });

    avalon.router.get("/tab2", function(a) {
        var page = 'tab2';
        var a = {name:page, title:'选项卡二', url:'#/tab2'};
        if (!loaded[page]) {
            loaded[page] = 1;
            model.tab.page.push(a);
        }
        model.tab.content = "页面:"+page;
        model.tab.currpage = page;
    });

    avalon.router.get("/tab3", function(a) {
        var page = 'tab3';
        var a = {name:page, title:'选项卡三', url:'#/tab3'};
        if(!loaded[page]) {
            loaded[page] = 1;
            model.tab.page.push(a);
        }
        model.tab.content = "页面:"+page;
        model.tab.currpage = page;
    });

    avalon.router.error(function(a) {
        avalon.router.navigate("/tab1");
    })
    avalon.history.start({html5Mode: false});

    avalon.scan();
});
</script>
</body>
</html>

问题描述:设置可以显示多个选项卡,不同路由显示指定的选项卡。可以通过点击显示页面下方的“显示tabxx”来显示出多个选项卡。当点击“X”来关闭选项卡时会执行 tab_close 方法,会将除第一个选项卡外的所有其他选项卡关闭,并使用 avalon.router.navigate("/tab1") 将路由地址改成第一个选项卡的。但实际测试发现,在点点击“X”后,浏览器中显示的路由地址仍保持原先的地址并没有变成 avalon.router.navigate 指定的地址。

常见问题&以及解决方案 - 看过来

TypeError: avalon.require is not a function --mmState.js 有什么办法,用的是avalon.shim.js

重写avalon.controller.loader【或者avalon.require = requrejs,如果你使用的是requirejs】
这是默认的loader:

avalon.controller.loader = function (url, callback) {
        // 没有错误回调...
        avalon.require(url, function ($ctrl) {
            callback && callback($ctrl);
        });
    };

重写为:

avalon.controller.loader = function (url, callback) {
        // 没有错误回调...
        requrejs(url, function ($ctrl) {
            callback && callback($ctrl);
        });
    };

请确保以上的操作发生在:

            avalon.history.start({
                // basepath: "/mmRouter",
                fireAnchor: false
            })

之前

mmRouter 的 html5model 问题

<li><a href="#/"></a></li> //1
<li><a href="#/a"></a></li> //2
<li><a href="#/b"></a></li> //3

<script>
  var callback = function () {
     console.log(this);
  }
  avalon.router.get("/:status", callback);
  avalon.history.start({
     basepath: "/",
     html5Mode: true
  });
</script>

你好,我在使用 mmRouter(最新代码) 的时候,发现开启 html5Mode 后,点击 2 或者 3 链接都没有问题,但是在 2 或 3 链接下,即地址栏为:localhost/index.html/a 或者 localhost/index.html/b 时,点击 1 链接,地址没有按照预期改变为:localhost/index.html ,而仍然是 localhost/index.html/a 或者 localhost/index.html/b ,并且触发了一次对应的 callback。关闭 html5Mode 后一切正常。用的是 avalon.modern.shim.js(最新代码), chrome 41(mac)

请问目前的mmState里面$ctrl的$onRendered回调是不是只是scan完成,但是不保证全部页面dom结构建立完毕,符合预期?

不知是我自己没学好还是真的只是写的那样,只是scan完毕就回调,可dom貌似并不一定建立完毕,给需要二次渲染的一些dom造成麻烦,现在都是用enough time的延时来做。(毕竟一些mvvm组件有时候还是不如过去的dom组件库好用,一些触屏轮播,滑动导航条组件目前项目里还是用的别的组件库,需要预期一些特定dom结构再渲染,以前是后端模板来的时候就是预期的dom,现在得先前端渲染,再二次渲染)。

希望能变成真正的页面渲染完成的回调,如其名。嘿嘿。不知道实现起来怎么样,感觉现在是不是框架下个命令去渲染一些东西,但是真正最后一个dom渲染完毕了并不好捕捉啊。总不能轮询来看看dom是否符合预期了啊。

可能有很多不对的地方,求指正,求赐教,谢谢大家!

mmState改进计划

现在路由器遇到一个问题, 就是切换后某些VM的数据丢失

因为有些VM是定义在子页面的.

mmState.state("xxxx",{ })

子页面是通过路由器切换

这些VM应该只初始化一次

并且应该与用户交互逻辑分离

换言之,这些VM与普通JS分别在不同的函数内

普通JS所在的函数应该能拿到它们想要的VM,或者说通过依赖注入

当页面再切换到其他页面时, 应该做一些后继处理

比如, 页面上的ms-widget生成的VM,它们的数据应该保存起来

当页面再切换回来, VM不用再重新创建,它们只需要再扫描(mmState不会保存这些子页面的DOM节点,

这是在移动端出于性能考虑, 只保存字符器)

然后子页面的ms-widget 再重新赋予之前的数据

这样一来, 就能还原所有之前的状态

参数不固定的子状态bug

abstract:true,表示不参与匹配,这样他的子集url:'',等于覆盖了其默认值,这样确实可以让任何子集来填充父级的默认状态
但是,不参与匹配,似乎无法获取this.params这种参数
本来父级应该做一些全局操作嘛,获取不到params的话,如果path是带参数的,就不好初始化状态啦。例子:

avalon.state("form.table", {
    controller: "form",
    url: "/{source_type}/{db_name}/{table_name}",
    views: {
      "form_container": {
        templateUrl: '/templates/form/table.html',
      }
    },
    abstract: true,
    onChange: function() {
      console.log(this.params);
    },
});

//sql
  avalon.state("form.table.sql", {
    controller: "table",
    url: "/sql",
    views: {
      "form_table_container": {
        templateUrl: '/templates/form/sql.html',
      }
    },
    onChange: function() {
      console.log('sql');
    },
  });

路由是
/aaa/bbb/ccc
时,this.params能正确输出。
路由是
/aaa/bbb/ccc/sql
这个子状态时,this.params输出为undefined

mmRouter例子

<!DOCTYPE html>

<html>
    <head>
        <title>路由器的相关测试</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width">
        <script src="avalon.js">

        </script>
        <script>
            require(["ready!", "mmRouter"], function() {
                if (typeof console === "undefine") {
                    avalon.log = function(str) {
                        var div = document.createElement("div")
                        div.innerHTML = str
                        document.body.appendChild(div)
                    }
                }
                avalon.router.get("/", {
                    template: "<div>首页</div>",
                    callback: function() {
                        avalon.log("首页加载完毕")
                    }
                })
                avalon.router.get("/aaa", {
                    template: "<h1>这是第1个模板</h1>",
                    callback: function(a) {
                        avalon.log("aaa加载完毕")
                    }
                })
                avalon.router.get("/bbb/:bbb", {
                    templateUrl: function(a) {
                        return "template" + a + ".html"
                    }
                })
                avalon.router.get("/ccc", {
                    templateUrl: "template1.html",
                })


                avalon.history.start({basepath: "/avalon/"})
                avalon.define("test", function(vm) {
                    vm.hash = function(a, e) {
                        location.hash = a
                        e.preventDefault()
                    }
                })
                avalon.scan()
            })
        </script>

    </head>
    <body>
        <div ms-controller="test">
            <div ms-view></div>
            <ul>
                <li><a href="#!/aaa">1111</a></li>
                <li><a href="#!/bbb/2">2222</a></li>
                <li><a href="#!/bbb/3">3333</a></li>
                <li><a href="#!/ccc">4444</a></li>
            </ul>


            <table class="table-doc">
                <tr>
                    <th>名字</th><th width="80">默认值</th><th>说明</th>
                </tr>
                <tr>
                    <td colspan="3" align="center">配置参数</td>
                </tr>
                <tr>
                    <td>view</td><td>""</td> <td>容器的名字, ms-view="name"中的name</td>
                </tr>
                <tr>  
                    <td>element</td><td> null </td> <td>容器元素,即ms-view所定义的元素节点</td> 
                </tr>
                <tr>   
                    <td>template</td> <td> null </td><td>字符串或函数,如果是函数会将入路由的参数与将路由对象作为this,
                        最后返回容器要添加的HTML模板</td> 
                </tr>
                <tr>   
                    <td>templateUrl</td>  <td>null</td><td>字符串或函数,如果是函数会将入路由的参数与将路由对象作为this,
                        最后将返回一个URL地址,框架通过它得到template。template会缓存到路由对象上</td> 
                </tr>
                <tr>   
                    <td>templates</td>  <td>{}</td><td>如果存在templateUrl,它会通过ajax请求加载对应的模块,缓存在这里</td> 
                </tr>
                <tr>   
                    <td>regexp</td> <td></td> <td>路由规则对应的正则</td> 
                </tr>
                <tr>   
                    <td>path</td> <td></td> <td>地址中被配匹的部分</td>  
                </tr>
                <tr>   
                    <td>args</td> <td>[]</td><td>抽取路由规则中的参数名对应的实际值放在这里</td> 
                </tr>
                <tr>   
                    <td>params</td><td>{}</td> <td>将路由规则中的参数名与对应的实际值 以键值对形式 构成一个对象</td> 
                </tr>
                <tr>   
                    <td>callback(params)</td> <td></td><td> 当模板插入后,执行的回调函数,参数为args,值为路由对象</td> 
                </tr>
                <tr>   
                    <td>query</td> <td>{}</td><td> 地址栏上的query组成的对象</td> 
                </tr>
            </table>
        </div>

    </body>
</html>

template1.html

<div>这是模板</div><div>这是模板</div><div>这是模板</div>

template2.html

<div style="color:green;font-size: 26px">这是第2个模板</div>

template3.html

<div style="color:pink;font-size: 26px">这是第3个模板</div>

点击 `#!/` 链接回调两次

我看这个问题被问过好几次了,但是好像也没有解决方案。

直接用回官方代码说明问题:

我再 callback 里面加了一句 console.log(this.path),可以很清晰看到,每点击一次链接控制台输出两次。

不过,如果我在控制台里面输入:location.hash = #!/aaa/ 这样子,就只会回调一次。这是为什么呢?如何解决?求帮助!

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width">
        <title>路由系统</title>
        <script src="avalon.js"></script>
        <script>
            require(["mmRouter"], function() {
                var model = avalon.define('test', function(vm) {
                    vm.currPath = ""
                    vm.params = {}
                    vm.query = {}
                    vm.args = "[]"
                })
                function callback() {
                    console.log(this.path);
                    model.currPath = this.path
                    var params = this.params
                    if ("time" in params) {
                        params.time = avalon.filters.date(params.time, "yyyy年M月dd日")
                    }
                    model.params = params
                    model.query = this.query
                    model.args = "[" + [].slice.call(arguments, 0) + "]"

                }
                avalon.router.get("/aaa/", callback)
                avalon.router.get("/bbb", callback)
                avalon.router.get("/ccc/:ccc", callback)
                avalon.router.get("/ddd/{time:date}/", callback)
                avalon.router.get("/eee/{count:\\d{4}}/", callback)
                avalon.router.get("/fff", callback)
                avalon.history.start({
                    basepath: "/avalon/",
                    html5Mode: false
                });
                avalon.scan()
            })
        </script>
    </head>
    <body>
        <div ms-controller="test">
            <table width="100%" height="300">
                <tr>
                    <td width="250">
                        <ul>
                            <li><a href="#!/aaa">aaa</a></li>
                            <li><a href="#!/bbb?uu=3445345&were=4324">bbb</a></li>
                            <li><a href="#!/ccc/etretr">ccc</a></li>
                            <li><a href="#!/ddd/2014-09-19">ddd</a></li>
                            <li><a href="#!/eee/2222">eee</a></li>
                            <li><a href="#!/fff?a=1&nn=4&dfg=676">fff</a></li>
                        </ul>
                    </td>
                    <td>
                        <div style="color:red">this.path: {{currPath}}</div>
                        <div style="color:blue">arguments: {{args}}</div>
                        <fieldset>
                            <legend>this.params</legend>
                            <ol>
                                <li ms-repeat="params"> {{$key}}: {{$val}}</li>
                            </ol>
                        </fieldset>
                        <fieldset>
                            <legend>this.query</legend>
                            <ol>
                                <li ms-repeat="query"> {{$key}}: {{$val}}</li>
                            </ol>
                        </fieldset>
                    </td>
                </tr>
            </table>
            <div style="height: 600px;width:1px;">

            </div>
            <p id="fff">会定位到这里</p>
        </div>

    </body>
</html>

avalon.router.get 回调两次

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width">
        <title>路由系统</title>
        <script src="avalon.js"></script>
        <script>
            require(["mmRouter"], function() {
                var model = avalon.define('test', function(vm) {
                    vm.currPath = ""
                    vm.params = {}
                    vm.query = {}
                    vm.args = "[]"
                    vm.currIndex = 0
                })
                function callback() {
                    model.currIndex++;
                    console.log(avalon.router.getLastPath());
                    model.currPath = this.path
                    var params = this.params
                    if ("time" in params) {
                        params.time = avalon.filters.date(params.time, "yyyy年M月dd日")
                    }
                    model.params = params
                    model.query = this.query
                    model.args = "[" + [].slice.call(arguments, 0) + "]"

                }
                avalon.router.get("/aaa/", callback)
                avalon.router.get("/bbb", callback)
                avalon.router.get("/ccc/:ccc", callback)
                avalon.router.get("/ddd/{time:date}/", callback)
                avalon.router.get("/eee/{count:\\d{4}}/", callback)
                avalon.router.get("/fff", callback)
                avalon.history.start({
                    basepath: "/avalon"
                })
                avalon.scan()
            })
        </script>
    </head>
    <body >
        <div ms-controller="test">
            <table width="100%" height="300">
                <tr>
                    <td width="250">
                        <ul>
                            <li><a href="#!/aaa">aaa</a></li>
                            <li><a href="#!/bbb?uu=3445345&were=4324">bbb</a></li>
                            <li><a href="#!/ccc/etretr">ccc</a></li>
                            <li><a href="#!/ddd/2014-09-19">ddd</a></li>
                            <li><a href="#!/eee/2222">eee</a></li>
                            <li><a href="#!/fff?a=1&nn=4&dfg=676">fff</a></li>
                        </ul>
                    </td>
                    <td>
                        <div style="color:red">this.path: {{currPath}}</div>
                        <div style="color:red">callBack次数: {{currIndex}}</div>
                        <div style="color:blue">arguments: {{args}}</div>
                        <fieldset>
                            <legend>this.params</legend>
                            <ol>
                                <li ms-repeat="params"> {{$key}}: {{$val}}</li>
                            </ol>
                        </fieldset>
                        <fieldset>
                            <legend>this.query</legend>
                            <ol>
                                <li ms-repeat="query"> {{$key}}: {{$val}}</li>
                            </ol>
                        </fieldset>
                    </td>
                </tr>
            </table>
            <div style="height: 600px;width:1px;">

            </div>
            <p id="fff">会定位到这里</p>
        </div>

    </body>
</html>

视图切换时的内存问题

使用mmstate控制视图跳转的时候发现内存会一直增涨,不会释放。
比如只使用一个最简单的首页,然后放一个链接到另一个最简单的页面。
然后就在这两个状态之间切换,内存会持续增涨~ 页面上只显示了文字和链接:

avalon.state("home", {
        url: "/",
        views: {
            "": {
                template:"this is home page"
            }
        }
}).state("demo", {
        url: "/demo",
        views: {
            "": {
                templateUrl: "/demo/demo.html",
                controllerUrl: "/demo/demo"
                // viewCache: true
            }
        }
})


// vm里只在页面上放一行文字
var vm = avalon.define({
       $id: "demo"
 });

使用官方提供的例子mmRouter-demo-list也会出现内存一直增涨的情况。
不知道是不是我使用方法的问题~~ 求指教 。

state view加载后,vm为上一次绑定的,没有被新vm覆盖

导致收到的数据虽然是新的,但view显示的却是上一次绑定的内容,没有跟着更新。
文件见 https://github.com/VaJoy/issueStample/tree/master/mmState

比如从home.html(该视图从data.json获取的数据)上点击进入temp.html,修改data.json内容后再点击返回home.html,会发现每次这么返回到home,其view显示的都是上一次收到的数据,而不是当次收到的新数据

1.5.1版本路由问题

有3个路由状态aaa、bbb、bbb.ccc

当页面第一次加载时,进入aaa,直接转到bbb.ccc时无法正常显示页面
chrome中log显示navigating to [bbb.ccc] will be stopped, redirect to [bbb.ccc] now

但是依次点击aaa、bbb、bbb.ccc时,是正常的,仅仅页面第一次加载后会出现aaa到bbb.ccc异常

测试代码见:https://github.com/Realgomu/AvalonTest

路径参数解析的问题

主要表现如下:
在状态配置里使用 /book/{id} 这种格式定义一个view,
然后用" /book/1" 访问是正常的,但是使用 "/book/0" 访问的话就会解析成"/book".

经过debug发现是mmRouter.js里的参数判断有问题:

        urlFormate: function(url, params, query) {
            var query = query ? queryToString(query) : "",
                hash = url.replace(placeholder, function(mat) {
                    var key = mat.replace(/[\{\}]/g, '').split(":")
                    console.log(key);
                    key = key[0] ? key[0] : key[1]
                    //return params[key] || ''   // 这个地方判断的时候param的值如果为 0或者false什么的,就会被过滤掉,改成下面一行就可以了
                    return params[key] !== undefined ? params[key] : ''
                }).replace(/^\//g, '')

            console.log("after url formate");
            console.log("hash:" + hash);
            console.log("query:" + query);
            return {
                path: hash,
                query: query
            }
        },

麻烦升级一下mmState,请求异常情况添加xhr到回调函数中

xhr.onreadystatechange = function() {
                if (xhr.readyState === 4) {
                    var status = xhr.status;
                    if (status > 399 && status < 600) {
                        reason.message = "templateUrl对应资源不存在或没有开启 CORS"
                        reason.xhr = xhr
                        reject(reason)
                    } else {
                        resolve(avalon.templateCache[url] = xhr.responseText)
                    }
                }
            }

warning: home状态对象的【】视图对象对应的元素节点不存在

使用mmState,遇到标题写的错误警告,页面没有加载出来,下面是详细描述,麻烦帮我看下。
目录结构如下:

default

根目录下的index.html代码:

<!DOCTYPE html>
<html>
<head lang="zh">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <link rel="stylesheet" type="text/css" href="vendor/bootstrap/css/bootstrap.min.css">
    <script src="vendor/avalon/avalon.mobile.js" data-main="vendor/main"></script>
    <title>index</title>
</head>
<body>
<div ms-cotroller="index">
    <div ms-view></div>
</div>
</body>
</html>

main.js代码:

/**
 * 主程序入口,配置加载器和路由
 * Created by Peak on 2015/3/9.
 */
require.config({
    paths: {
        avalon: "avalon/avalon.mobile.js",
        mmAnimate: "avalon/mmAnimate.modern.js",
        mmHistory: "avalon/mmHistory.js",
        mmPromise: "avalon/mmPromise.js",
        mmRequest: "avalon/mmRequest.js",
        mmRouter: "avalon/mmRouter.js",
        mmState: "avalon/mmState.js",
        jquery: "jquery/jquery.min.js",
        bootstrap: "bootstrap/js/bootstrap.min.js"
    }, shim: {
        bootstrap: {
            deps: ["jquery"],
            export: "bootstrap"
        }
    }
});
require(["domReady!", "mmState"], function () {
    avalon.define({
        $id: "index"
    });
    //TODO 配置路由
    avalon.state("home", {
        controller: "index",
        url: "/",
        views: {
            "": {
                templateUrl: "../views/index.html"
            }
        }
    });
    avalon.history.start({
        basepath: "/"
    });
    avalon.scan();
});

页面未能正常加载(空白的,没有出现views/index.html的内容),浏览器控制台信息:

debug: 正准备加载 http://localhost:8090/vendor/main.js 
debug: 正准备加载 http://localhost:8090/vendor/avalon/mmState.js
debug: 已成功加载 http://localhost:8090/vendor/main.js
debug: 正准备加载 http://localhost:8090/vendor/avalon/mmPromise.js
debug: 正准备加载 http://localhost:8090/vendor/avalon/mmRouter.js
debug: 已成功加载 http://localhost:8090/vendor/avalon/mmState.js 
debug: 已成功加载 http://localhost:8090/vendor/avalon/mmPromise.js
debug: 正准备加载 http://localhost:8090/vendor/avalon/mmHistory.js
debug: 已成功加载 http://localhost:8090/vendor/avalon/mmRouter.js
debug: 已成功加载 http://localhost:8090/vendor/avalon/mmHistory.js
warning: home状态对象的【】视图对象对应的元素节点不存在 

每个路由都需要定义吗?支不支持通用匹配?

如题,
我以前用jquery的时候是这样的。

$(window).bind('hashchange', function(e) {
var url = $.param.fragment();
if(url ==''){
url = "overview";
}
$("#main-content").load(url);
});

会绑定每次的hashchange
用的是 jQuery BBQ

关于 mmRouter

刚开始接触mmRouter,用的时候有下面这个问题,该怎么解决 @RubyLouvre

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <script type="text/javascript" src="common/avalon.1.391.20150131.js"></script>
    <script type="text/javascript">
        require(["mmRouter", "ready!"], function(){

            var url = window.location.href;

            var vm = avalon.define({
                $id: "test",
                num: 1,
                click: function(){
                    vm.num++;
                    // 能不能通过某个方法给url中的num赋值?
                    // 因为这样 在第一次点击后,刷新页面,上一次的url就被记住了,这时候再往后加就重复了
                    console.log(url);
                    window.location.href = url + "?num=" + vm.num;
                }
            })

            avalon.router.get("/", function(){
                console.log(this.query)
            });

            avalon.history.start();

            avalon.scan();
        })
    </script>
</head>
<body>
<div ms-controller="test">
    <button ms-click="click">click</button>
</div>
</body>
</html>

在IE10下路由会失效

在IE10下反应非常慢,之前跟正美大神汇报过了,这次在github马克下,希望能早日修复 xD

浏览器返回上一页面时,当前页面状态&数据清理问题

现在路由只有请求开始时的数据初始化 callback,但是页面跳转增多后,我返回上一页面后,就会出现刚才的页面状态没有清理的问题!
而且我认为这应该在离开刚才页面的时候进行处理,即路由除了有请求开始时的 callback,时候有离开页此页面时的 callbakc,以清理一些状态!

mmState路由改进建议

avalon.state("test", {
    controller: "model",
    url: "/test",
});

建议将url参数改完可传入字符串或数组,若都能匹配,则优先匹配先写的,例如:

avalon.state("test", {
    controller: "model",
    url: ["/test","/test/{type}"],
});

之所以有传入多个数组的需求,是为了同时匹配/以及/xxx

old _parse function

 var runicode = /[\x00-\x1f"\\\u007f-\uffff]/g
    var quote = function(str) {//String.quote || JSON.stringify ||
        return '"' + str.replace(runicode, function(a) {
            switch (a) {
                case '"':
                    return '\\"';
                case '\\':
                    return '\\\\';
                case '\b':
                    return '\\b';
                case '\f':
                    return '\\f';
                case '\n':
                    return '\\n';
                case '\r':
                    return '\\r';
                case '\t':
                    return '\\t';
            }
            a = a.charCodeAt(0).toString(16);
            while (a.length < 4)
                a = "0" + a;
            return "\\u" + a;
        }) + '"';
    }
    //将(  ) 转换为数组的两端,最后构成一个多维数组返回
    function _parse(tokens) {
        var array = "["
        // console.log(tokens)
        for (var i = 0, n = tokens.length; i < n; i++) {
            var token = tokens[i];
            if (token === "(") {
                array += "["
            } else if (token == ")") {
                array += "]"
            } else {

                array += quote(token) + ","
            }
        }
        array += "]"
        array = array.replace(/,\]/g, "]")
        return Function("return " + array)()
    }

点击链接,回调触发两次

点击链接时,下面的两段代码都会执行,导致同时调用了avalon.router.navigate方法

//155行
if (hash) {
                event.preventDefault()
                avalon.router && avalon.router.navigate(hash)
            }
//266行
if (hash !== void 0) {
        that.fragment = hash
        that.fireRouteChange(hash, {fromHistory: true})
   }

这个应该是bug吧

avalon.router.getLastPath()的问题

<a href="#/a">没有感叹号</a> //可以成功获取最后访问的地址
<a href="#!/a">有感叹号</a> //不成功!!!!!

<script>
    var callback = function () {
      console.log(this.params.status);
    }
    avalon.router.get("/:status", callback);

    var lastPath = avalon.router.getLastPath();

    avalon.history.start({
      basepath: "/"
    });

    avalon.router.navigate('/' + lastPath);
</script>

你好,我在使用 mmRouter 的时候发现以上问题,avalon.router.getLastPath() 只能对 <a href="#/a"> 成功执行,用 #!/ 不能成功返回值。

mmRouter2

define(["mmHistory"], function(avalon) {

    function Router() {
        this.routingTable = {};
    }
    function parseQuery(path) {
        var array = path.split("#"), query = {}, tail = array[1];
        if (tail) {
            var index = tail.indexOf("?");
            if (index > 0) {
                var seg = tail.slice(index + 1).split('&'),
                        len = seg.length, i = 0, s;
                for (; i < len; i++) {
                    if (!seg[i]) {
                        continue;
                    }
                    s = seg[i].split('=');
                    query[decodeURIComponent(s[0])] = decodeURIComponent(s[1]);
                }
            }
        }
        return {
            pathname: array[0],
            query: query
        };
    }

    var optionalParam = /\((.*?)\)/g
    var namedParam = /(\(\?)?:\w+/g
    var splatParam = /\*\w+/g
    var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g
    Router.prototype = {
        error: function(callback) {
            this.errorback = callback
        },
        _pathToRegExp: function(path, params) {
            path = path.replace(escapeRegExp, '\\$&')
                    .replace(optionalParam, '(?:$1)?')
                    .replace(namedParam, function(match, optional) {
                        params.push(match.slice(1))
                        return optional ? match : '([^/?]+)'
                    })
                    .replace(splatParam, '([^?]*?)');
            return new RegExp('^' + path + '(?:\\?([\\s\\S]*))?$')
        },
        //添加一个路由规则
        add: function(method, path, callback) {
            var array = this.routingTable[method]
            if (!array) {
                array = this.routingTable[method] = []
            }
            var regexp = path, params = []
            if (avalon.type(path) !== "regexp") {
                regexp = this._pathToRegExp(regexp, params)
            }
            array.push({
                value: callback,
                regexp: regexp,
                params: params
            })
        },
        routeWithQuery: function(method, path) {
            var parsedUrl = parseQuery(path),
                    ret = this.route(method, parsedUrl.pathname);
            if (ret) {
                ret.query = parsedUrl.query;
                return ret;
            }
        },
        _extractParameters: function(route, path) {
            var array = route.regexp.exec(path) || []
            array = array.slice(1)
            var args = [], params = {}
            var n = route.params.length
            for (var i = 0; i < n; i++) {
                args[i] = decodeURIComponent(array[i])
                args[ route.params[i] || i  ] = args[i]
            }
            return {
                query: {},
                value: route.value,
                args: args,
                params: params,
                path: path
            }
        },
        route: function(method, path) {//判定当前URL与预定义的路由规则是否符合
            path = path.trim()
            var array = this.routingTable[method]
            if (array) {
                for (var i = 0, el; el = array[i++]; ) {
                    if (el.regexp.test(path)) {
                        return this._extractParameters(el, path)
                    }
                }
            }
        },
        getLastPath: function() {
            return getCookie("msLastPath")
        },
        setLastPath: function(path) {
            setCookie("msLastPath", path)
        },
        navigate: function(url) {//传入一个URL,触发预定义的回调
            var match = this.routeWithQuery("GET", url);
            if (match) {
                var fn = match.value;
                if (typeof fn === "function") {
                    return  fn.apply(match, match.args);
                }
            } else if (typeof this.errorback === "function") {
                this.errorback(url)
            }
        }
    };
    Router.prototype.getLatelyPath = Router.prototype.getLastPath
    Router.prototype.setLatelyPath = Router.prototype.setLastPath

    "get,put,delete,post".replace(avalon.rword, function(method) {
        return  Router.prototype[method] = function(path, fn) {
            return this.add(method.toUpperCase(), path, fn)
        }
    })
    function supportLocalStorage() {
        try {
            return 'localStorage' in window && window['localStorage'] !== null;
        } catch (e) {
            return false;
        }
    }
    if (supportLocalStorage()) {
        Router.prototype.getLatelyPath = function() {
            return localStorage.getItem("msLastPath")
        }
        Router.prototype.setLatelyPath = function(path) {
            localStorage.setItem("msLastPath", path)
        }
    }

    function escapeCookie(value) {
        return String(value).replace(/[,;"\\=\s%]/g, function(character) {
            return encodeURIComponent(character);
        });
    }
    function setCookie(key, value) {
        var date = new Date();//将date设置为10天以后的时间 
        date.setTime(date.getTime() + 60 * 60 * 24);
        document.cookie = escapeCookie(key) + '=' + escapeCookie(value) + ";expires=" + date.toGMTString()
    }
    function getCookie(name) {
        var result = {};
        if (document.cookie !== '') {
            var cookies = document.cookie.split('; ')
            for (var i = 0, l = cookies.length; i < l; i++) {
                var item = cookies[i].split('=');
                result[decodeURIComponent(item[0])] = decodeURIComponent(item[1]);
            }
        }
        return name ? result[name] : result
    }

    avalon.router = new Router
    // 先添加路由规则与对应的处理函数
    // router.add("GET","/aaa", function(){}) //{GET:{1:{aaa: function(){}}}}
    // router.add("GET","/aaa/bbb", function(){}) //{GET:{1:{aaa:{bbb: function(){}} }}}
    // router.add("GET","/aaa/:bbb", function(){}) //{GET:{1:{aaa: {"^n": "bbb", "^v": function(){}}}}}
    // router.add("GET","/aaa(/:bbb)", function(){}) //{GET:{1:{aaa: {"^n": "bbb", "^v": function(){}}}}}
    // 再启动历史管理器
    // require("ready!", function(avalon){
    //     avalon.history.start();
    // })


    return avalon
})
// http://kieran.github.io/barista/
// https://github.com/millermedeiros/crossroads.js/wiki/Examples

angular路由系统研究

其when方法的第一个参数要求为字符串,并且第一个字符必须是"/", 它会创建两个适配规则

            function pathRegExp(path, opts) {
                var insensitive = opts.caseInsensitiveMatch,
                        ret = {
                            originalPath: path,
                            regexp: path
                        },
                keys = ret.keys = [];

                path = path
                        .replace(/([().])/g, '\\$1')
                        .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) {
                            var optional = option === '?' ? option : null;
                            var star = option === '*' ? option : null;
                            keys.push({name: key, optional: !!optional});
                            slash = slash || '';
                            return ''
                                    + (optional ? '' : slash)
                                    + '(?:'
                                    + (optional ? slash : '')
                                    + (star && '(.+?)' || '([^/]+)')
                                    + (optional || '')
                                    + ')'
                                    + (optional || '');
                        })
                        .replace(/([\/$\*])/g, '\\$1');

                ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
                return ret;
            }
            var routes = {}
            avalon.when = function(path, route) {
                routes[path] = avalon.mix(
                        {reloadOnSearch: true}, route,
                        path && pathRegExp(path, route));

                // create redirection for trailing slashes
                if (path) {
                    var redirectPath = (path[path.length - 1] == '/')
                            ? path.substr(0, path.length - 1)
                            : path + '/';
                    routes[redirectPath] = avalon.mix(
                            {redirectTo: path},
                    pathRegExp(redirectPath, route)
                            );
                }
                return this;
            };

            avalon.when("/aaa", {})
            console.log(routes)

image

BUG: 无法正确响应在浏览器地址栏里第一次输入的地址

在chrome29下, 不过这个bug与浏览器应该无关

http://wvovo.com/avalon/#!/start 为例

第一次进入该页面后, 如果手动将地址栏上的start改为route(或者使用location.href改动地址), 可以看到响应是不正确的.
但如果手动点击一次"路由操作"之后, 再返回"安装方式", 这时再像上面那样改动地址栏, 这时可以获得正确的响应.

粗略看了下源代码, 提出一点个人的理解:
mmHistory中, 给document绑定的click的匿名函数里调用了setLocation, 把hash和location的关系写入到location2hash这个关联数组. 而在地址栏里输入时, 上面的函数不会被调用.
我暂时没有想到如何检测地址栏中的键盘事件, 也没有完全弄明白这个关联数组具体的作用, 所以没有好的修改建议. 希望您指教.

mmState 的 avalon.router.go() 方法 Bug

avalon.state("ordersuccess", {
        controller: "page",
        url: "/order/success/{orderId}",
        views: {
            "": {
                templateUrl: "./modules/ordersuccess/tpl.html"
            },
            'title@': {
                template: "订单"
            }
        },
        onChange: function (orderId) {
            orderId || avalon.router.go("home");
            console.log('有被执行')
        }
    });

如上demo,avalon.router.go("home"); 这里的 go() 方法存在如下问题:

  • 不能阻止下面的语句继续执行,而是下面的语句可以继续执行;
  • 视图跳转到相应的 'home',但是 路由还处于当前状态,并不是 home 的路由状态

请问ignoreChange有哪些使用场景或者推荐的用法抑或最佳实践!

看了mmRouter-demo-list里在列表页用了,还有源码文档里写了几句。还是有些疑问。请正妹老师说几句或者有使用心得的朋友总结一下。感觉这个属性应该对性能优化有帮助但是不应该滥用吧。

源码里写道: 更新视图时调用该函数,为true时不会去重写视图和scan,请确保该视图内用到的数据没有放到avalon vmodel $skipArray内。
列子里写道: url通过{}配置的参数变量发生变化的时候是否通过innerHTML重刷ms-view,默认会,如果你做的是翻页应用,建议使用例子内的配置,把数据更新到vmodel上即可。

如果做类似做题的场景,题目经常切换且很多,而且题目类型不同会使用不同的模版,该使用这个属性吗?

希望可以增加一个api触发相应的路径回调

做spa时,如果用户跳转到了/User/Index 后按了f5 将不能正确的捕捉到该路由的变化并执行相关回调,我目前的解决方案时在后端记录该访问路径并跳转到主页面后,使用js获取该路径并且执行相关函数。
希望框架可以提供一个 avalon.router.fire(path) 函数调用,实现即使用户使用了f5还是可以正确的在/User/Index 进行刷新

opts.onEnter 回调函数的参数顺序问题

文档是这样说的:

{Function} opts.onEnter 进入状态触发,可以返回false,或任意不为true的错误信息或一个promise对象,用法跟视图的$onEnter一致
{Function} onEnter.params 视图所属的state的参数
{Function} onEnter.resolve $onEnter return false的时候,进入同步等待,直到手动调用resolve
{Function} onEnter.reject 数据加载失败,调用

使用时,

onEnter: function(rs ,rj){
        console.log(arguments)
        setTimeout(function(){
                 rs(true)
         },300)
         return false
 }

遇到的问题是:
当路由上有参数时, arguments[0]为参数对象;
当没有参数时,arguments[0]为resolve函数;

假如我的onEnter是一个公用的,我就不得不判断一下当前的arguments,因为它可能会变!

建议:将onEnter的路由参数放到 resolve, reject函数的后面,因为这两个是必须的。

状态的迁移

<!DOCTYPE html>
<html>
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8">
        <script src="avalon.js"></script>
        <meta name="viewport" content="width=device-width">
        <script>
            var rootState = {}
            var Router = function() {
                this.states = {
                    "": rootState
                }
                this.currentState = ""
                this.stateArray = []
            }
            Router.prototype = {
                add: function(obj) {
                    this.registerState(obj, true)
                },
                _getLevel: function(state) {
                    if (state == "") {
                        return 0
                    }
                    return state.split(".").length
                },
                _transitionTo: function(from, to) {
                    //情况1 "" T aaa
                    var curr = from
                    var fromList = []
                    do {
                        fromList.push(from)
                    } while ((from = this.states[from].parentNode) != null);

                    var toList = []
                    do {
                        toList.push(to)
                    } while ((to = this.states[to].parentNode) != null);
                    do {
                        if (fromList[fromList.length - 1] === toList[toList.length - 1]) {
                            fromList.pop()
                            toList.pop()
                        } else {
                            break
                        }
                    } while (true)
                    var array = fromList.concat(toList.reverse())
                    if (array[0] == curr) {
                        array.shift()
                    }
                    return array

                }
                ,
                transitionTo: function(to) {
                    var from = this.currentState
                    this.currentState = to
                    return this._transitionTo(from, to)
                },
                registerState: function(obj, recursive) {
                    var state = obj.state //这是一个字符串
                    var match = state.match(/([\.\w]+)\./) || ["", ""]
                    var parentNode = match[1]
                    obj.parentNode = parentNode
                    var parent = this.states[parentNode]
                    if (parent) {
                        parent.children = parent.children || []
                        avalon.Array.ensure(parent.children, obj)
                        this.states[state] = obj
                        avalon.Array.ensure(this.stateArray, obj)
                    }
                    if (recursive) {
                        for (var i = 0, el; el = this.stateArray[i++]; ) {
                            if (el !== obj)
                                this.registerState(el)
                        }
                    }
                }
            }
            var router = new Router
            router.add({state: "contacts"})
            router.add({state: "contacts.list"})
            router.add({state: "contacts.detail"})
            router.add({state: "contacts.detail.item"})
            router.add({state: "home"})
            router.add({state: "home.eee"})
            router.add({state: "abouts"})
            console.log(router)
            router.currentState = "home.eee"
            var array = router.transitionTo("contacts.detail.item")
            console.log(array)
        </script>
    </head>

    <body>
        <div>TODO write content</div>
    </body>
</html>

请教关于mmState的一个问题

我现在遇到一个问题 希望您能解答一下(用的是mmState)

我定义了一种状态,对应的模板里有一个UI组件库 loading,在地址处于该状态的时候,我让该模板的loading组件显示。第一次是成功的。

但是第二次又跳到该种状态的时候提示loading视图为undefined( avalon.vmodels['loadingName'] )。报错。开始我以为是模板未载入完的原因,所以将调用loading的代码放至了onAfterLoad里面,第一次加载模板后是没问题的 ,但第二次跳到该种状 依然有这个错误。

麻烦升级一下mmState,请求异常情况添加xhr到回调函数中

xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var status = xhr.status;
if (status > 399 && status < 600) {
reason.message = "templateUrl对应资源不存在或没有开启 CORS"
reason.xhr = xhr
reject(reason)
} else {
resolve(avalon.templateCache[url] = xhr.responseText)
}
}
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.