rubylouvre / mmrouter Goto Github PK
View Code? Open in Web Editor NEWavalon的三柱臣之一( 路由,动画,AJAX)
avalon的三柱臣之一( 路由,动画,AJAX)
avalon.router.get("/aaa", callback)
avalon.router.navigate(“/aaa”);
上面代码中 navigate 会触发两次调用 callback
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
})
之前
主要表现如下:
在状态配置里使用 /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
}
},
不知是我自己没学好还是真的只是写的那样,只是scan完毕就回调,可dom貌似并不一定建立完毕,给需要二次渲染的一些dom造成麻烦,现在都是用enough time的延时来做。(毕竟一些mvvm组件有时候还是不如过去的dom组件库好用,一些触屏轮播,滑动导航条组件目前项目里还是用的别的组件库,需要预期一些特定dom结构再渲染,以前是后端模板来的时候就是预期的dom,现在得先前端渲染,再二次渲染)。
希望能变成真正的页面渲染完成的回调,如其名。嘿嘿。不知道实现起来怎么样,感觉现在是不是框架下个命令去渲染一些东西,但是真正最后一个dom渲染完毕了并不好捕捉啊。总不能轮询来看看dom是否符合预期了啊。
可能有很多不对的地方,求指正,求赐教,谢谢大家!
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
使用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也会出现内存一直增涨的情况。
不知道是不是我使用方法的问题~~ 求指教 。
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)()
}
文档是这样说的:
{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函数的后面,因为这两个是必须的。
现在返回后,页面的滚动条位置都会重新回到顶部哦!
其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)
需求:路由改变时需要保证JS
每次都要执行。
配置了 viewCache、cacheController 为 false 还是生成 dom cache。
问题复现代码如下:
<!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 指定的地址。
在IE10下反应非常慢,之前跟正美大神汇报过了,这次在github马克下,希望能早日修复 xD
<!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>
mmRouter示例页面(https://github.com/RubyLouvre/mmRouter/blob/master/index2.html )默认使用的是avalon.js也就是全兼容版本的avalon,路由功能都正常,但是只要换上avalon.mobile.js就会报错。
我用的chrome40,avalon.mobile版本是1.391,控制台报错:“warning: home状态对象的【】视图对象 必须存在template, templateUrl, templateProvider中的一个”。
就用这个例子 https://rawgit.com/RubyLouvre/mmRouter/master/index2.html#!/
node-webkit直接黑屏,
我测试了一下,发现是 template, templateUrl, templateProvider,加载模版的时候,必挂
点击链接时,下面的两段代码都会执行,导致同时调用了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吧
mmHistory.js 第258行
if (path.indexOf("#/") === 0) 只对#/的情况进行判断
改成
if ( ~path.indexOf("#/") || ~path.indexOf("#!/"))
就正常了
<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">
成功执行,用 #!/
不能成功返回值。
#37 修复后,absolute:true的父级可以获取到this.params了,但是只有第一次是准确的,以后刷新都会这个参数值都没变。子集获得的this.params是准确的。
使用mmState,遇到标题写的错误警告,页面没有加载出来,下面是详细描述,麻烦帮我看下。
目录结构如下:
根目录下的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状态对象的【】视图对象对应的元素节点不存在
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)
}
}
}
有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异常
/([-.\w]+)./ 修改之后的正则表达式
看了mmRouter-demo-list里在列表页用了,还有源码文档里写了几句。还是有些疑问。请正妹老师说几句或者有使用心得的朋友总结一下。感觉这个属性应该对性能优化有帮助但是不应该滥用吧。
源码里写道: 更新视图时调用该函数,为true时不会去重写视图和scan,请确保该视图内用到的数据没有放到avalon vmodel $skipArray内。
列子里写道: url通过{}配置的参数变量发生变化的时候是否通过innerHTML重刷ms-view,默认会,如果你做的是翻页应用,建议使用例子内的配置,把数据更新到vmodel上即可。
如果做类似做题的场景,题目经常切换且很多,而且题目类型不同会使用不同的模版,该使用这个属性吗?
现在路由器遇到一个问题, 就是切换后某些VM的数据丢失
因为有些VM是定义在子页面的.
mmState.state("xxxx",{ })
子页面是通过路由器切换
这些VM应该只初始化一次
并且应该与用户交互逻辑分离
换言之,这些VM与普通JS分别在不同的函数内
普通JS所在的函数应该能拿到它们想要的VM,或者说通过依赖注入
当页面再切换到其他页面时, 应该做一些后继处理
比如, 页面上的ms-widget生成的VM,它们的数据应该保存起来
当页面再切换回来, VM不用再重新创建,它们只需要再扫描(mmState不会保存这些子页面的DOM节点,
这是在移动端出于性能考虑, 只保存字符器)
然后子页面的ms-widget 再重新赋予之前的数据
这样一来, 就能还原所有之前的状态
升级新版后发现,第一次点击内部链接跳转没问题,地址栏中会自动将#test替换成#!test,第二次点击又变回#test了(firefox 29.0.1)。
<a href="#test">test</a>
这块去掉了对#前缀的判定,只对#!做了拦截,是故意这么做的吗 @RubyLouvre
https://github.com/RubyLouvre/mmRouter/blob/master/mmHistory.js#L250
做spa时,如果用户跳转到了/User/Index 后按了f5 将不能正确的捕捉到该路由的变化并执行相关回调,我目前的解决方案时在后端记录该访问路径并跳转到主页面后,使用js获取该路径并且执行相关函数。
希望框架可以提供一个 avalon.router.fire(path) 函数调用,实现即使用户使用了f5还是可以正确的在/User/Index 进行刷新
<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)
<!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>
在chrome29下, 不过这个bug与浏览器应该无关
以http://wvovo.com/avalon/#!/start 为例
第一次进入该页面后, 如果手动将地址栏上的start改为route(或者使用location.href改动地址), 可以看到响应是不正确的.
但如果手动点击一次"路由操作"之后, 再返回"安装方式", 这时再像上面那样改动地址栏, 这时可以获得正确的响应.
粗略看了下源代码, 提出一点个人的理解:
mmHistory中, 给document绑定的click的匿名函数里调用了setLocation, 把hash和location的关系写入到location2hash这个关联数组. 而在地址栏里输入时, 上面的函数不会被调用.
我暂时没有想到如何检测地址栏中的键盘事件, 也没有完全弄明白这个关联数组具体的作用, 所以没有好的修改建议. 希望您指教.
ie下报错
类似于angular的
http://docs.angularjs.org/tutorial/step_07
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)
}
}
}
我写的是一个浏览文件夹的应用,例如 http://localhost:8000/sharedir/test#!/sharedirtest/apps 这样目录会越来越深,那我没办法写一个规则来匹配到所有的path了?
例如两层/的path就要写两个规则,100层就要写100个了
所以能不能加个接口或者匹配方式能匹配到所有的path
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
现在路由只有请求开始时的数据初始化 callback,但是页面跳转增多后,我返回上一页面后,就会出现刚才的页面状态没有清理的问题!
而且我认为这应该在离开刚才页面的时候进行处理,即路由除了有请求开始时的 callback,时候有离开页此页面时的 callbakc,以清理一些状态!
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('有被执行')
}
});
我现在遇到一个问题 希望您能解答一下(用的是mmState)
我定义了一种状态,对应的模板里有一个UI组件库 loading,在地址处于该状态的时候,我让该模板的loading组件显示。第一次是成功的。
但是第二次又跳到该种状态的时候提示loading视图为undefined( avalon.vmodels['loadingName'] )。报错。开始我以为是模板未载入完的原因,所以将调用loading的代码放至了onAfterLoad里面,第一次加载模板后是没问题的 ,但第二次跳到该种状 依然有这个错误。
avalon.state("test", {
controller: "model",
url: "/test",
});
建议将url参数改完可传入字符串或数组,若都能匹配,则优先匹配先写的,例如:
avalon.state("test", {
controller: "model",
url: ["/test","/test/{type}"],
});
之所以有传入多个数组的需求,是为了同时匹配
/
以及/xxx
<!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>
我看这个问题被问过好几次了,但是好像也没有解决方案。
直接用回官方代码说明问题:
我再 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>
用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没有销毁~
当使用requireJs加载器的时候,禁用avalon内部的loader,而mmState默认使用的是 avalon的加载器,造成
mmState中的 controllerUrl后面的参数加载出现bug,这个怎么解决呀!
如题,
我以前用jquery的时候是这样的。
$(window).bind('hashchange', function(e) {
var url = $.param.fragment();
if(url ==''){
url = "overview";
}
$("#main-content").load(url);
});
会绑定每次的hashchange
用的是 jQuery BBQ
刚开始接触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>
导致收到的数据虽然是新的,但view显示的却是上一次绑定的内容,没有跟着更新。
文件见 https://github.com/VaJoy/issueStample/tree/master/mmState
比如从home.html(该视图从data.json获取的数据)上点击进入temp.html,修改data.json内容后再点击返回home.html,会发现每次这么返回到home,其view显示的都是上一次收到的数据,而不是当次收到的新数据
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.