前端路由Hash与History模式

了解SPA现代前端项目多为单页Web应用(SPA),在单页Web应用中路由是其中的重要环节。SPA 是 single page web application 的简称,译为单页Web应用。 简单的说 SPA 就是一个WEB项目只有一个 HTML 页面,一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转。 取而代之的是利用 JS 动态的变换 HTML 的内容,从而来模拟多个视图间跳转。 前度路由简单的说,就是在保证只有一个 HTML 页面,且与用户交互时不刷新和跳转页面的同时,为 SPA 中的每个视图展示形式匹配一个特殊的 url。在刷新、前进、后退和SEO时均通过这个特殊的 url 来实现。我们需要实现下满两点: 改变 url 且不让浏览器像服务器发送请求。可以监听到 url 的变化可以在不刷新页面的前提下动态改变浏览器地址栏中的URL地址hash 模式和 history 模式,就是用来实现上面功能的 Hash模式在url后面加上#,如http://127.0.0.1:5500/前端路由/hash.html#/page1这个url后面的#/page1就是hash值 hash 值的变化不会导致浏览器像服务器发送请求location.hash可以获取hash值hashchange是hash值发生改变的调用的函数基于以上三点我们可以写一个路由实例 <!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Document</title> </head> <body> <ul> <li><a href="#/">/</a></li> <li><a href="#/page1">page1</a></li> <li><a href="#/page2">page2</a></li> </ul> <div class="content-div"></div> </body> <script> class RouterClass { constructor() { this.routes = {}; // 记录路径标识符对应的cb this.currentUrl = ""; // 记录hash只为方便执行cb window.addEventListener("load", () => this.render()); window.addEventListener("hashchange", () => this.render()); } /* 初始化 */ static init() { window.Router = new RouterClass(); } /* 注册路由和回调 */ route(path, cb) { this.routes[path] = cb || function() {}; } /* 记录当前hash,执行cb */ render() { this.currentUrl = window.location.hash.slice(1) || "/"; this.routes[this.currentUrl](); } } RouterClass.init(); const ContentDom = document.querySelector(".content-div"); const changeContent = content => (ContentDom.innerHTML = content); Router.route("/", () => changeContent("默认页面")); Router.route("/page1", () => changeContent("page1页面")); Router.route("/page2", () => changeContent("page2页面")); </script></html>History模式History 接口允许操作浏览器的曾经在标签页或者框架里访问的会话历史记录。可以参考下两篇文章对history的说明https://css-tricks.com/using-the-html5-history-api/https://developer.mozilla.org/zh-CN/docs/Web/API/History下面介绍在这个模式下需要用到的api ...

November 4, 2019 · 2 min · jiezi

HG220GU光猫超级用户密码zz

原博_20150321 反正被上海电信坑了要换光猫结果所有设置权全部上交了,唉,不过有朋友还在用姑且也留个档吧。 原教程地址也已经没了 http://77ya.com/?article/wwjc... 光纤猫超级密码获取(网上那些个教程基本不适合新版2013年出的光纤猫了) 在浏览器上输入 http://192.168.1.1/logoffaccount.html,设置隐藏用户改为启用,这样就可以用工程账号登陆了。登录工程帐号(用户名:fiberhomehg2x0密码:hg2x0),登录网址http://192.168.1.1/(如果这个你走不了,请联系电信处理吧,别乱弄的网都上不了。)工程模式中,开启FTP功能(自己设置一个用户名和密码,默认都是admin);出厂设置-升级image-【更新 -- 预配置生成】,点击[生成预配置]登录FTP,地址ftp://192.168.1.1/fhconf/,用户名和密码是3中你设置的,复制其中的backpresettings.conf到本地使用记事本打开,后中字符串极为密码,需使用base64解密,在这里解密, Base64加密解密,即为密码,用户名是telecomadmin使用telecomadmin和密码登录http://192.168.1.1/,即可管理猫大部分功能。 其实是一集美剧引发的血案。。。乐视的字幕没出于是去搜下载,结果只有迅雷于是就下载了,呵护了好久的ssd一朝破功-_-|||磁盘再起不能只好用time machine恢复,应用程序没有备份我连浏览器都没了,幸好系统应用里仅存safari和itunes。。。硬盘里还是山狮的安装盘,我的系统早已是优胜美地了,于是踏上了漫漫下载路。。。 被百度云折磨得死去活来,拖了两个版本,一个装不到u盘上,一个索性打不开,死活md5也不对,继续在mega上下载,20k的速度俨然回到解放前。。。 伴随着对电信的恨恨之心,疑似速度限制和内网ip有关,可我这连拨号都被光猫代劳了,于是又拾起了破解大计。早先直接提供的超级用户密码早已不对,这回终于找到了可用的攻略,不过这家的攻略版本还漏了一个解密网址,差点功亏一篑。。。

June 26, 2019 · 1 min · jiezi

Vue的路由对象

1. 一个路由对象(route object)表示当前激活的路由的状态信息,包含了当前URL解析得到的信息,还有URL匹配到的路由记录(route records)。 2. 路由对象是不可变的,每次成功的导航都会产生一个新的对象。 3. 路由出现的地方有以下几个(慢慢熟悉): 在组件内,即this.$route;在$route观察者回调内;router.match(location)的返回值;导航守卫的参数: router.beforeEach((to, from, next) => { // to 和 from 都是路由对象 })scrollBehavior方法的参数: const router = new VueRouter({ scrollBehavior (to, from, savedPosition){ // to 和 from 都是路由对象 } })4.路由对象属性: $router.path string类型,对应当前路由的路径,总是解析为绝对路径,如"/man/tom"$router.params object类型,一个key/value对象,包含了动态片段和全匹配片段,如果没有路由参数,就是空对象$router.query object类型,一个key/value对象,表示URL查询参数。例如,对于路径 /列表项目列表项目列表项目列表项目

June 17, 2019 · 1 min · jiezi

VueRouter

当 <router-link> 对应的路由匹配成功,将自动设置 class 属性值 .router-link-active 。默认 hash 模式:使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。history 模式:充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面,此模式如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面。动态路由匹配{path: '/user/:id', component: User}user/abc user/123 都将映射到相同的路由this.$route.params.id(template 中 $route.params.id){path: '/user-*'} 匹配以 `/user-` 开头的任意路径嵌套路由User 需要 <router-view></router-view>children 路径不能以 "/" 开头{ path: '/user', component: User, children: [ { // 当 /user/profile 匹配成功, // UserProfile 会被渲染在 User 的 <router-view> 中 path: 'profile', component: UserProfile }, { // 当 /user/posts 匹配成功 // UserPosts 会被渲染在 User 的 <router-view> 中 path: 'posts', component: UserPosts } ]}编程式的导航<router-link :to="..."> 等同于调用 router.push(...)this.$router.push(location, onComplete?, onAbort?)this.$router.push('home')this.$router.push({ path: 'register', query: { plan: 'private' }})this.$router.push({ name: 'user', params: { userId: '123' }})location 如果提供了 path,params 会被忽略,解决办法:{path: `register/${id}`}onComplete 和 onAbort 两个回调用于导航成功完成(在所有的异步钩子被解析之后)或终止(导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由)的时候进行相应的调用<router-link :to="..." replace> 等同于调用 router.replace(...),和 router.push() 唯一的不同就是,它不会向 history 添加新记录,而是替换掉当前的 history 记录router.go(n) 这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)。命名视图<router-view></router-view><router-view name="a"></router-view><router-view name="b"></router-view>{ path: '/', components: { default: Foo, a: Bar, b: Baz }}重定向和别名{ path: '/a', redirect: '/b' }{ path: '/a', redirect: { name: 'foo' }}{ path: '/a', redirect: to => { // 方法接收 目标路由 作为参数 // return 重定向的 字符串路径/路径对象}}{ path: '/a', component: A, alias: '/b' }导航守卫全局前置守卫router.beforeEach((to, from, next) => { // to: Route: 即将要进入的目标 路由对象 // from: Route: 当前导航正要离开的路由 // next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。 // some code next()})全局后置钩子router.afterEach((to, from) => { // ...})路由独享的守卫{ path: '/foo', component: Foo, beforeEnter: (to, from, next) => {/* */}}组件内的守卫const Foo = { template: `...`, beforeRouteEnter (to, from, next) { // 在渲染该组件的对应路由被 confirm 前调用 // 【不能】获取组件实例 `this`,不过,你可以通过传一个回调给 next来访问组件实例 next(vm => { // 通过 `vm` 访问组件实例 }) // 因为当守卫执行前,组件实例还没被创建 }, beforeRouteUpdate (to, from, next) { // 在当前路由改变,但是该组件被复用时调用 // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候, // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 // 可以访问组件实例 `this` }, beforeRouteLeave (to, from, next) { // 导航离开该组件的对应路由时调用 // 可以访问组件实例 `this` }}路由元信息{ path: 'bar', component: Bar, meta: { requiresAuth: true, title: 'BAR' }}遍历 $route.matched 来检查路由记录中的 meta 字段。滚动行为scrollBehavior 只在支持 history.pushState 的浏览器中可用。new VueRouter({ routes: [...], scrollBehavior (to, from, savedPosition) { // savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时才可用。 // return 期望滚动到哪个的位置 }})路由懒加载const Foo = () => import('./Foo.vue')https://router.vuejs.org/zh/ ...

June 15, 2019 · 2 min · jiezi

实现一个前端路由如何实现浏览器的前进与后退

1. 需求如果要你实现一个前端路由,应该如何实现浏览器的前进与后退 ? 2. 问题首先浏览器中主要有这几个限制,让前端不能随意的操作浏览器的浏览纪录: 没有提供监听前进后退的事件。不允许开发者读取浏览纪录,也就是 js 读取不了浏览纪录。用户可以手动输入地址,或使用浏览器提供的前进后退来改变 url。所以要实现一个自定义路由,解决方案是自己维护一份路由历史的记录,从而区分 前进、刷新、回退。 下面介绍具体的方法。 3. 方法目前笔者知道的方法有两种,一种是 在数组后面进行增加与删除,另外一种是 利用栈的后进先出原理。 3.1 在数组最后进行 增加与删除通过监听路由的变化事件 hashchange,与路由的第一次加载事件 load ,判断如下情况: url 存在于浏览记录中即为后退,后退时,把当前路由后面的浏览记录删除。url 不存在于浏览记录中即为前进,前进时,往数组里面 push 当前的路由。url 在浏览记录的末端即为刷新,刷新时,不对路由数组做任何操作。另外,应用的路由路径中可能允许相同的路由出现多次(例如 A -> B -> A),所以给每个路由添加一个 key 值来区分相同路由的不同实例。 注意:这个浏览记录需要存储在 sessionStorage 中,这样用户刷新后浏览记录也可以恢复。笔者之前实现的 用原生 js 实现的轻量级路由 ,就是用这种方法实现的,具体代码如下: // 路由构造函数function Router() { this.routes = {}; //保存注册的所有路由 this.routerViewId = "#routerView"; // 路由挂载点 this.stackPages = true; // 多级页面缓存 this.history = []; // 路由历史}Router.prototype = { init: function(config) { var self = this; //页面首次加载 匹配路由 window.addEventListener('load', function(event) { // console.log('load', event); self.historyChange(event) }, false) //路由切换 window.addEventListener('hashchange', function(event) { // console.log('hashchange', event); self.historyChange(event) }, false) }, // 路由历史纪录变化 historyChange: function(event) { var currentHash = util.getParamsUrl(); var nameStr = "router-history" this.history = window.sessionStorage[nameStr] ? JSON.parse(window.sessionStorage[nameStr]) : [] var back = false, // 后退 refresh = false, // 刷新 forward = false, // 前进 index = 0, len = this.history.length; // 比较当前路由的状态,得出是后退、前进、刷新的状态。 for (var i = 0; i < len; i++) { var h = this.history[i]; if (h.hash === currentHash.path && h.key === currentHash.query.key) { index = i if (i === len - 1) { refresh = true } else { back = true } break; } else { forward = true } } if (back) { // 后退,把历史纪录的最后一项删除 this.historyFlag = 'back' this.history.length = index + 1 } else if (refresh) { // 刷新,不做其他操作 this.historyFlag = 'refresh' } else { // 前进,添加一条历史纪录 this.historyFlag = 'forward' var item = { key: currentHash.query.key, hash: currentHash.path, query: currentHash.query } this.history.push(item) } // 如果不需要页面缓存功能,每次都是刷新操作 if (!this.stackPages) { this.historyFlag = 'forward' } window.sessionStorage[nameStr] = JSON.stringify(this.history) }, }以上代码只列出本次文章相关的内容,完整的内容请看 原生 js 实现的轻量级路由,且页面跳转间有缓存功能。 ...

June 9, 2019 · 3 min · jiezi

积梦前端的路由方案-ruledrouter

积梦(https://jimeng.io) 是一个为制造业制作的一个平台.积梦的前端基于 React 做开发的. 早期使用 React Router.后来出现了一些 TypeScript 集成还有定制化的需求, 自己探索了一套方案. 使用 React Router 遇到的问题React Router 本身是一个较为稳妥而且全面的方案, 早期我们使用了它.后面随着积梦数据平台的页面的重构, 遇到了一些问题.积梦的管理界面从顶层往内存在多个层级, 复杂的情况会出现五六层嵌套, 导航栏 -> 子导航 -> 标签页 -> 功能 -> 子页面虽然一般的情况只是三四个层级, 但是页面的嵌套大量存在,早期的我们办法是定义一个 basePath 变量用来表示外层路由, <Route path={`${this.props.basePath}/:page`} render={(props) => { return this.renderSubPage(props.match.params.page); }}/>然后在内部跳转时, 也会使用 basePath 变量快速生成路径, <Redirect to={`${this.props.basePath}/${EWorkflowPage.Step}`} />这样手动传递偶尔会出错, 特别是当页面结构发生一些修改的时候.经过一两次导航的重构, 我们在局部出现了一些代码, 无法正确跳转.虽然靠着测试逐步修复了问题, 但是随着页面增多, 这个问题不能轻视. 我觉得这个问题是两部分, 一方面是 TypeScript 的类型检查没有帮助到的路由部分,React Router 当中基本上通过字符串定义的路径, 这些不容易被类型检查.特别是拼接的路由, 发生改变以后就难以准确追踪了. 另一方面, 我认为 React Router 的规则也限制了 JavaScript 代码的使用.相对于 React Router 通过 Context 传递路由状态的方案, 更倾向于代码.基于 switch/case 还有函数组成的控制流, 有更为灵活的应对的办法. ...

May 11, 2019 · 3 min · jiezi

GPSR:贪婪转发与周边转发

博客原文地址:https://godbmw.com/passages/2019-03-02-gpsr/博客主题推荐:Theme Art Design,“笔记记录+搭建知识体系”的利器。这是之前学习《无线传感网络》这门课做的期末大作业,GPSR是"greedy perimeter stateless routing"的缩写。这是一种无状态的路由转发协议,巧妙地借助“贪婪转发”和“周边转发”有效地降低了每个物理节点的存储信息量,非常具有实用意义。除此之外,它还能快速地应对现实中外界条件、节点能耗等多种因素造成的频繁变化的节点分布。因此,特别重温一下,作为一次分享。0. 摘要随着路由节点的增加以及拓扑结构变化率的增大,传统的路由转发协议算法效率低、鲁棒性差。贪婪周界无状态路由协议(GPSR)只使用拓扑结构中的临近信息节点进行“贪婪转发”决策。当数据包进入“路由空洞”的时候,算法会先构造GG平面图或者RNG平面图,然后采用“周边转发”绕过此区域。此过程中,算法会自动切换“贪婪转发”和“周边转发”这两种模式。在频繁变化或者节点数量多的拓扑结构中,并且每个节点存储的信息量少,GPSR可以较低的成本快速地响应变化,查询正确的路由路径。关键词:GPSR, 贪婪转发, 周边转发, 路由空洞, 平面图1. 研究背景及意义1.1 研究背景与动机当下的一些路由具有节点多、拓扑结构变化快的特点,例如:Ad-hoc网络(无基础设施,支持军事用户、灾后救援人员以及临时协作)、传感器网络(由小型传感器组成,节点资源匮乏)、“屋顶”网络(非移动,但是密集遍布大都市区域,节点数量数十万)。传统的路由算法的节点成本和消息成本过高,造成在高移动性和密集节点拓扑结构中的的低适应性。因此,需要一种新的节点成本低、鲁棒性高的路由算法。1.2 研究意义论文提出的GPSR算法合理利用地理信息来实现高稳健性。在网络节点数量不断增加的情况下提高稳健性和迁移率,降低路由协议消息发送成本,各个路由节点消息传递成功率以及使得每个节点存储最少的信息量。2. 国内外研究现状论文中提出的DV和LS算法,要求将整个网络拓扑结构的映射到所有的路由节点。在DV算法的描述中,每个路由节点都记录了最新周期中到所有网络目的地的距离;在LS算法的描述中,每个路由节点都会接受到链路改变的相关信息状态。当拓扑结构变化率增大,或者路由区域中的路由节点的数量增多,DV算法和LS算法的复杂度就会增加,同时增加的还有每个节点的信息量储备和节点之间沟通成本。虽然“缓存”技术可以减少节点负载,但是当节点数目过多或者拓扑结构变化率过大的时候,现有算法仍然不能保证较高的鲁棒性以及较低的节点开销。3. 研究内容3.1 主要挑战及创新点为了让节点存储最少的信息量,并且能够快速响应拓扑结构的变化。需要使用贪心法的思想,让每一步都是最优解,这个转发过程就是“贪心转发”。但是有些时候无法满足“贪心转发”的条件,此时的情况就是“路由空洞”。解决“路由空洞”的重要技术是“右手法则”,这个转发过程就是“周边转发”。而在周边转发之前需要将图处理成平面图,有GG和RNG两种平面图供选择。在转发过程中,根据节点条件,切换“贪心转发”模式和“周边转发”模式,直到到达最终的目的节点。3.2 相关技术介绍GPSR算法的实现过程中,需要配合“信标算法”来确定邻居节点的位置信息。“信标算法”中,每个节点周期性的以广播方式传送一个信标,信标包括节点自身的位置信息,位置信息被编码成两个4字节的浮点数值,用于标记节点的x坐标和y坐标。数据格式是(IP, (x, y))。为了避免邻居节点发送的信标产生冲突,用B表示信标间的时间间隔,节点发送信标的时间统一分布在[0.5B, 1.5B]之间。设节点保留位置信息的最长时间为T,在超过T时间间隔后仍然没有收到邻居节点发送的信标,就认为邻居节点失效或超出覆盖范围,删除对应的位置信息。借助这些地理信息,GPSR算法的可以避免探测包的盲目洪泛,从而进行有效的路由转发,并且针对节点变动进行有效的路由维护。甚至实现基于无状态的分布式的非端到端的数据转发。3.3 贪婪转发贪婪转发的过程是指:数据包由源节点标记要发送数据包的目标节点或目标区域位置;每一个中间的转发节点都知道它的邻居节点的位置,转发节点在选择数据包的下一跳节点时使用贪婪转发策略,即选择地理位置最接近目标节点的节点作为下一跳节点;以此类推,每一次转发都会更加接近目标节点,直至到达目标节点。贪婪转发的原理就是利用“贪心”思想,让每个节点选择当前的最优选择(在满足条件的情况下),直到算法结束。如下图所示,根据贪婪转发的原则,节点x的下一跳节点就是节点y。毫无疑问,贪婪转发只需要保证节点的一条邻居信息即可。3.3 贪婪转发困境·路由空洞路由空洞是指当前节点比所有其他一跳邻居节点更接近目的节点,此时,根据贪婪转发的规则,当前节点不会转发数据给一跳的邻居节点。如果存在这种网络拓扑结构,那么就称之为“路由空洞”。如下图所示,void区域就是没有满足“贪婪转发”条件的区域。因为节点x的覆盖范围与以直线xD为半径的圆的交叉区域没有邻居节点。3.4 周边转发针对上述的“路由空洞”问题,算法会将模式(Mode)从贪婪转发切换到周边转发,进而绕过“路由空洞”。“周边转发”是根据“右手法则”来判断下一跳转节点:连接当前节点和目的结点形成直线,右手握住此线逆时针旋转,到达的第一条边(边代表其上的两个点可以互达)就是下一跳的方向。3.5 周边转发困境虽然周边转发可以绕过“路由空洞”,但在一些情况下,单纯地进行周边转发可能会陷入死循环,最终只能回到当前节点,无法抵达目的节点。这里举一个例子进行介绍。下图是一个由X、W、U、Z以及目的节点D构成的图,节点之间的连线代表着两端节点是相邻的(可以互相达到)。假设现在从节点X开始出发。从X节点开始,根据“右手法则”依次抵达U节点、Z节点和W节点。此时,对W节点再次使用“右手法则”,算法又重新跳回了U节点。最后,对U节点使用“右手法则”,跳回了开始节点X。显而易见,此时的周边转发陷入困境。这主要是由于这张图不是一个“平面图”的原因。需要删除一些边,从而使其变成GG或者RNG平面图,才能走出此困境。3.6 RNG平面图和GG平面图RNG平面图的定义是:若顶点U,V和任意其它顶点W之间的距离,全都大于或等于顶点u和v之间的距离d(u,v),则在顶点U和V之间存在RNG边(u,v)。用方程式表示如下:如下图所示,若(u,v)是RNG中的边,则在节点U和V之间的阴影半月形区域内,不能包含有任何证明节点w。此时,由于d(u, v) > max(d(u,w), d(w,v)),为了构建RNG平面图,必须把边(u, v) 舍去。关于RNG平面实现的伪代码如下。其中,N是对于任意节点u来说的邻接节点列表,v是集合N中的任一节点。GG平面图的定义是:如果节点u和节点v之间,直径为uv的圆内,不存在其它顶点W,则节点u和节点v存在GG边(u,v)。用方程式表示如下:如下图所示,若(u,v)是GG中的边,则在节点U和V之间的圆形阴影区域,不能包含有任何证明节点w。关于GG平面实现的伪代码如下。其中,N是对于任意节点u来说的邻接节点列表,v是集合N中的任一节点。对于GG和RNG两种平面图,RNG平面图是GG平面图的子集。它们之间的直接关系可以用下图表示出来:3.7 周边转发困境·RNG平面图解决构造上述的RNG平面图或者GG平面图就可以解决“周边转发”无法到达目的节点的困境。这里以构造RNG平面图为例,还是使用之前的图形。为了方便讲述,规定边xu长度为12,边xw长度为11,边uw长度为10。根据RNG的定义,D(U, X) > MAX(D(W, U), D(X, W)),所以移除UX边。此时,“周边转发”不再会陷入困境。4. 性能评估论文为了测试算法的性能,使用了Carnegie Mellon(卡梅隆)大学的测试数据。在畅通平面上,无线仿真模型节点进行运动。节点会在指定区域内随机选择一个目标,然后在指定范围内随机选择一个速度,以此速度到达目标并且停留一段时间。这个过程模拟了拓扑结构的高迁移率以及其中的节点。下图显示了不同的B(时间间隔)的情况下,GPSR传递成功的数据包。将从B=3s降低到B=1.5S并没有带来很大成功率提高,成功率也保持在97%以上。下图比较了DSR算法和GPSR算法的节点消耗,GPSR的节点消耗远远低于DSR的节点消耗,并且随着时间推移,GPSR节点的消耗也更加稳定。因此,GPSR算法达到了设计的初衷:在迁移率高的拓扑结构中,能够保持较高的鲁棒性,并且每个节点的资源消耗都得到了改善。论文中还分节点状态、路径长度等维度进行了比较,也只是说明GPSR算法的优势。主要的性能测试还是上述的传包成功率和节点消耗,其它测试这里不再冗赘。5. 阅读心得在本次论文的学习过程中,掌握了“贪婪转发”、“周边转发”、“RNG和GG平面图”,最重要的是理解了在GPSR算法中是如何调度进行状态转化(贪婪=>周边 / 周边=>贪婪),以及如何解决“路由空洞”和“周边转发困境”。除了算法的核心部分,也触类旁空地了解了“信标算法”的实现机制以及冲突解决方法。就我个人来看,GPSR算法和论文中提及的传统算法相比,已经实现了最小化节点的保存数据(节省节点资源),并且能够利用状态切换合理应对可变性高的拓扑结构。美中不足的是“信标机制”会带来额外的开销,但是相比于每个节点保存所有节点的信息,信标机制的这点开销完全可以忽略。6. 参考在阅读论文的过程中,我查找了大量的中文和英文资料,非常有助于理解这篇论文所讲述的GPSR算法。特此系统记录一下相关资料。论文:GPSR: Greedy Perimeter Stateless Routing for Wireless NetworksYoutube Videos(印度英语)https://www.youtube.com/watch…https://www.youtube.com/watch...https://www.youtube.com/watch…百度文库讲义(从43页开始): https://wenku.baidu.com/view/…博客原文地址:https://godbmw.com/passages/2019-03-02-gpsr/博客主题推荐:Theme Art Design,“笔记记录+搭建知识体系”的利器。

March 5, 2019 · 1 min · jiezi

前端路由跳转基本原理

目前前端三杰 Angular、React、Vue 都推介单页面应用 SPA 开发模式,在路由切换时替换 DOM Tree 中最小修改的部分 DOM,来减少原先因为多页应用的页面跳转带来的巨量性能损耗。它们都有自己的典型路由解决方案,@angular/router、react-router、vue-router。一般来说,这些路由插件总是提供两种不同方式的路由方式: Hash 和 History,有时也会提供非浏览器环境下的路由方式 Abstract,在 vue-router 中是使用了外观模式将几种不同的路由方式提供了一个一致的高层接口,让我们可以更解耦的在不同路由方式中切换。值得一提的是,Hash 和 History 除了外观上的不同之外,还一个区别是:Hash 方式的状态保存需要另行传递,而 HTML5 History 原生提供了自定义状态传递的能力,我们可以直接利用其来传递信息。下面我们具体看看这两种方式都有哪些特点,并提供简单的实现,比如基本的功能,更复杂的功能比如懒加载、动态路径匹配、嵌套路由、路由别名等等,可以关注一下后面的 vue-router 源码解读方面的博客。1. Hash1.1 相关 ApiHash 方法是在路由中带有一个 #,主要原理是通过监听 # 后的 URL 路径标识符的更改而触发的浏览器 hashchange 事件,然后通过获取 location.hash 得到当前的路径标识符,再进行一些路由跳转的操作,参见 MDNlocation.href:返回完整的 URLlocation.hash:返回 URL 的锚部分location.pathname:返回 URL 路径名hashchange 事件:当 location.hash 发生改变时,将触发这个事件比如访问一个路径 http://sherlocked93.club/base/#/page1,那么上面几个值分别为:# http://sherlocked93.club/base/#/page1{ “href”: “http://sherlocked93.club/base/#/page1", “pathname”: “/base/”, “hash”: “#/page1”}注意:因为 Hash 方法是利用了相当于页面锚点的功能,所以与原来的通过锚点定位来进行页面滚动定位的方式冲突,导致定位到错误的路由路径,所以需要采用别的办法,之前在写 progress-catalog 这个插件碰到了这个情况。1.2 实例这里简单做一个实现,原理是把目标路由和对应的回调记录下来,点击跳转触发 hashchange 的时候获取当前路径并执行对应回调,效果:class RouterClass { constructor() { this.routes = {} // 记录路径标识符对应的cb this.currentUrl = ’’ // 记录hash只为方便执行cb window.addEventListener(’load’, () => this.render()) window.addEventListener(‘hashchange’, () => this.render()) } /* 初始化 / static init() { window.Router = new RouterClass() } / 注册路由和回调 / route(path, cb) { this.routes[path] = cb || function() {} } / 记录当前hash,执行cb / render() { this.currentUrl = location.hash.slice(1) || ‘/’ this.routesthis.currentUrl }}具体实现参照 CodePen如果希望使用脚本来控制 Hash 路由的后退,可以将经历的路由记录下来,路由后退跳转的实现是对 location.hash 进行赋值。但是这样会引发重新引发 hashchange 事件,第二次进入 render 。所以我们需要增加一个标志位,来标明进入 render 方法是因为回退进入的还是用户跳转class RouterClass { constructor() { this.isBack = false this.routes = {} // 记录路径标识符对应的cb this.currentUrl = ’’ // 记录hash只为方便执行cb this.historyStack = [] // hash栈 window.addEventListener(’load’, () => this.render()) window.addEventListener(‘hashchange’, () => this.render()) } / 初始化 / static init() { window.Router = new RouterClass() } / 记录path对应cb / route(path, cb) { this.routes[path] = cb || function() {} } / 入栈当前hash,执行cb / render() { if (this.isBack) { // 如果是由backoff进入,则置false之后return this.isBack = false // 其他操作在backoff方法中已经做了 return } this.currentUrl = location.hash.slice(1) || ‘/’ this.historyStack.push(this.currentUrl) this.routesthis.currentUrl } / 路由后退 / back() { this.isBack = true this.historyStack.pop() // 移除当前hash,回退到上一个 const { length } = this.historyStack if (!length) return let prev = this.historyStack[length - 1] // 拿到要回退到的目标hash location.hash = #${ prev } this.currentUrl = prev this.routesprev // 执行对应cb }}代码实现参考 CodePen2. HTML5 History Api2.1 相关 ApiHTML5 提供了一些路由操作的 Api,关于使用可以参看 <Manipulating the browser history> 这篇 MDN 上的文章,这里就列举一下常用 Api 和他们的作用,具体参数什么的就不介绍了,MDN 上都有history.go(n):路由跳转,比如n为 2 是往前移动2个页面,n为 -2 是向后移动2个页面,n为0是刷新页面history.back():路由后退,相当于 history.go(-1)history.forward():路由前进,相当于 history.go(1)history.pushState():添加一条路由历史记录,如果设置跨域网址则报错history.replaceState():替换当前页在路由历史记录的信息popstate 事件:当活动的历史记录发生变化,就会触发 popstate 事件,在点击浏览器的前进后退按钮或者调用上面前三个方法的时候也会触发,参见 MDN2.2 实例将之前的例子改造一下,在需要路由跳转的地方使用 history.pushState 来入栈并记录 cb,前进后退的时候监听 popstate 事件拿到之前传给 pushState 的参数并执行对应 cb,因为借用了浏览器自己的 Api,因此代码看起来整洁不少class RouterClass { constructor(path) { this.routes = {} // 记录路径标识符对应的cb history.replaceState({ path }, null, path) // 进入状态 this.routes[path] && this.routespath window.addEventListener(‘popstate’, e => { const path = e.state && e.state.path this.routes[path] && this.routespath }) } / 初始化 / static init() { window.Router = new RouterClass(location.pathname) } / 注册路由和回调 / route(path, cb) { this.routes[path] = cb || function() {} } / 跳转路由,并触发路由对应回调 */ go(path) { history.pushState({ path }, null, path) this.routes[path] && this.routespath }}Hash 模式是使用 URL 的 Hash 来模拟一个完整的 URL,因此当 URL 改变的时候页面并不会重载。History 模式则会直接改变 URL,所以在路由跳转的时候会丢失一些地址信息,在刷新或直接访问路由地址的时候会匹配不到静态资源。因此需要在服务器上配置一些信息,让服务器增加一个覆盖所有情况的候选资源,比如跳转 index.html 什么的,一般来说是你的 app 依赖的页面,事实上 vue-router 等库也是这么推介的,还提供了常见的服务器配置。代码实现参考 CodePen网上的帖子大多深浅不一,甚至有些前后矛盾,在下的文章都是学习过程中的总结,如果发现错误,欢迎留言指出~参考:history | MDNhashchange | MDNManipulating the browser history | MDN前端路由的基本原理 - 大史不说话History 对象 – JavaScript 标准参考教程 ...

January 31, 2019 · 2 min · jiezi

vue之vue router

走在前端的大道上本篇将自己读过的相关 vue router 文章中,对自己有启发的章节片段总结在这(会对原文进行删改),会不断丰富提炼总结更新。文章内容 基于vue 2.01.vue router如何传参?1.1 params、query是什么?params:/router1/:id ,/router1/123,/router1/789 //这里的id叫做paramsquery:/router1?id=123 ,/router1?id=456 //这里的id叫做query。比如:跳转/router1/:id<router-link :to="{ name:‘router1’,params: { id: status}}" >正确</router-link><router-link :to="{ name:‘router1’,params: { id2: status}}">错误</router-link>1.2 html标签传参params、query不设置也可以传参,params不设置的时候,刷新页面或者返回参数会丢失路由界面:当你使用params方法传参的时候,要在url后面加参数名,并在传参的时候,参数名要跟url后面设置的参数名对应。params是路由的一部分,必须要有,否则会导致跳转失败或者页面会没有内容如图片 :id,// 上面的router-link传参,也可以使用编程式导航跳转this.$router.push({ name:‘router1’,params: { id: status ,id2: status3},query: { queryId: status2 }});//编程跳转写在一个函数里面,通过click等方法来触发query,是拼接在url后面的参数,就没有这种限制,直接在跳转里面用就可以,没有也没关系。使用路由上面的参数<template> <div class=“router1”> <h1>接收参数的路由</h1> <h1> params.id:{{ $route.params }}</h1> <h1>query.status:{{ $route.query.queryId }}</h1> <keep-alive> <router-view></router-view> </keep-alive> </div></template>注意:获取路由上面的参数,用的是$route,后面没有r本节参考文章:vue router 使用params query传参,以及有什么区别1.3 vue this.$router.push()传参params 传参注意⚠️:patams传参 ,路径不能使用path 只能使用name,不然获取不到传的数据this.$router.push({name: ‘dispatch’, params: {paicheNo: obj.paicheNo}})取数据:this.$route.params.paicheNoquery 传参this.$router.push({path: ‘/transport/dispatch’, query: {paicheNo: obj.paicheNo}})取数据:this.$route.query.paicheNo2.this.$router 与 this.$route 在登录页完成登录请求后进行下面的操作 获取路径中存放前一个路径的参数 ,然后跳转到该页面 loginSuccess() { const { params: { back } } = this.$route; const route = back || { name: ‘home’ }; const { name, params, query } = route; this.$router.replace({ name, params, query }); },在上面这段代码中出现了两个我们经常混淆的概念:我们知道this.$router是router实例,可以用来直接访问路由。我们称router配置中每一个对象为一个路由记录,this.$route是暴露出来用来访问每个路由记录的。因此我们获取参数时使用的是this.$route 跳转路由时使用的是道this.$router。this.$router.push 与 this.$router.replace上端代码中我们使用了replace而不是push来跳转路由,这两者的区别是会不会在history中产生记录。replace不会新增记录,而是直接替换掉了这条路由记录。本节参考文章:vue router+ vuex+ 首页登录判断逻辑3.路由懒加载路由懒加载应该是写大一点的项目都会用的一个功能,只有在使用这个component的时候才会加载这个相应的组件,这样写大大减少了初始页面 js 的大小并且能更好的利用游览器的缓存。首先,可以将异步组件定义为返回一个 Promise 的工厂函数 (该函数返回的 Promise 应该 resolve 组件本身):const Foo = () => Promise.resolve({ /* 组件定义对象 / })const Foo = resolve => require([’./Foo.vue’], resolve)//或者const Foo = () => import(’./Foo’);官网:详细4.单页及多页应用全局配置404页面4.1 SPA的404路由配置单页应用配置404页面,也区分两种情况:4.1.1 路由表固定的情况如果SPA的路由表是固定的,那么配置404页面就变得非常的简单。只需要在路由表中添加一个路径为404的路由,同时在路由表的最底部配置一个路径为的路由,重定向至404路由即可。(由于路由表是由上至下匹配的,一定要将任意匹配规则至于最底部,否则至于此路由规则下的路由将全部跳转至404,无法正确匹配。)// router.jsexport default new Router({ mode: ‘history’, routes: [ // … { name: ‘404’, path: ‘/404’, component: () => import(’@/views/notFound.vue’) }, { path: ‘’, // 此处需特别注意至于最底部 redirect: ‘/404’ } ],})4.1.2 路由表动态生成的情况路由表是动态生成的情况下,也就是说路由表分为两部分,一部分为基础路由表,另一部分是需要根据用户的权限信息动态生成的路由表。本项目中动态生成路由采用vue-router自带的addRoutes方法,该方法是会将新的路由规则在原路由表数组的尾部注入的。由于任意匹配重定向至404页面的规则必须至于路由表的最底部,所以此处我将重定向至404页面的规则抽出,在动态路由注入后,再注入重定向规则,以确保该规则至于路由表最底部。// router.jsexport default new Router({ mode: ‘history’, routes: [ // … { name: ‘404’, path: ‘/404’, component: () => import(’@/views/notFound.vue’) }, // …other codes ],})// notFoundRouterMap.jsexport default [ { name: ‘404’, path: ‘/404’, component: () => import(’@/views/notFound.vue’) }, }, { path: ‘’, redirect: ‘/404’ }]// main.js//…other codesrouter.beforeEach((to, from, next) => { new Promise((resolve, reject) => { if (getCookie(tokenName)) { if (!getInfo()) { Promise.all([store.dispatch(‘getBasicInfo’), store.dispatch(‘getUserDetail’)]).then(res => { store.dispatch(‘GenerateRoutes’, { roles }).then(() => { // 根据用户权限生成可访问的路由表 router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表 router.addRoutes(NotFoundRouterMap) // 添加404及重定向路由规则 resolve({ …to, replace: true }) // 重新加载一次路由,让路由表更新成功后走下面else的判断 }) }) } else { // …other codes } } else { window.location.href = ‘/login.html’ } }).then(res => { if (res) { next(res) } else { next() } }).catch(err => { new Error(err) next(false) })4.2 多页应用的404路由配置多页应用区别于SPA的不同点是每个页面有自己的一套路由,并且每个页面可能有自己的一套404页面风格,当然也可能没有。这时候,就不能再采用动态添加路由规则的方法了。我采用的方案是在全局导航守卫beforeEach中对路由匹配的情况进行判断,这时候就需要用到vue导航守卫中的matched数组了。如果没有一个匹配上的,那么就重定向至404页面。当然,这个404页面也单独设置为一个页面。// permission.js//…other codesrouter.beforeEach((to, from, next) => { new Promise((resolve, reject) => { // …other codes }).then(res => { if (!to.matched.length) { window.location = ‘/error.html#/404’ return } if (res) { next(res) } else { next() } }).catch(err => { new Error(err) next(false) })本节参考文章:Vue单页及多页应用全局配置404页面实践5.后台系统权限控制具体实现思路创建vue实例的时候将vue-router挂载,但这个时候vue-router挂载一些登录或者不用权限的公用的页面。当用户登录后,获取用role,将role和路由表每个页面的需要的权限作比较,生成最终用户可访问的路由表。调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由。使用vuex管理路由表,根据vuex中可访问的路由渲染侧边栏组件。6.基于路由的动态过渡<!– 使用动态的 transition name –><transition :name=“transitionName”> <router-view></router-view></transition>// 接着在父组件内// watch $route 决定使用哪种过渡watch: { ‘$route’ (to, from) { const toDepth = to.path.split(’/’).length const fromDepth = from.path.split(’/’).length this.transitionName = toDepth < fromDepth ? ‘slide-right’ : ‘slide-left’ }}动手理解导航守卫(Vue)Vue动态路由的实现(后台传递路由,前端拿到并生成侧边栏)手摸手,带你用vue撸后台 系列二(登录权限篇)vue router原理前端的路由模式包括了 Hash 模式和 History 模式。vue-router 在初始化的时候,会根据 mode 来判断使用不同的路由模式,从而 new 出了不同的对象实例。例如 history 模式就用 HTML5History,hash 模式就用 HashHistory。vue-router 源码:前端路由vue-router 源码:路由模式 ...

December 27, 2018 · 2 min · jiezi

解决vue多个路由共用一个页面的问题

本次为大家分享一篇解决vue多个路由共用一个页面的问题,写的十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参考学习下。如有不足之处,欢迎批评指正。在日常的vue开发中我们可能会遇见多个路由需要共用一个页面的需求,特别是当路由是通过动态添加的,不同的路由展示的东西只是数据不同其他没有变化。例如:let routes = [ {path:"/zhanshan", components:Person, }, {path:"/lisi", components:Person, }, {path:"/wangwu", components:Person, }]//欢迎加入前端全栈开发交流圈一起学习交流:864305860这种情况的时候,我们发现,其实我们的页面在第一次加载成功后就不会再加载了。所以页面一直显示第一次加载的数据,给人的赶脚好像路由没有生效,而我们通过观察浏览器地址栏中的变化可以确定的是这和路由没关系,这对刚刚开始使用的vue的同学可能会产生一点点困扰,其实这和页面的声明周期是相关的,这种情况出现的原因是因为页面在加载后他的大多数钩子函数(mounted,computed…)就不会再次出发了,所以导致页面感觉没有跳转。一道这种业务需求其实也比较好处理,其实我们不需要页面切换,我们只需要页面中的数据发生改变就好了,我们可以在页面中监听路由地址的变化,当地址变化的时候,我们就重新加载数据。watch:{ “$route”:function(to,from){ //from 对象中包含当前地址 //to 对象中包含目标地址 //其实还有一个next参数的//这个参数是控制路由是否跳转的//如果没写,可以不用写next()来代表允许路由跳转,如果写了就必须写next(),否则路由是不会生效的。 }//欢迎加入前端全栈开发交流圈一起学习交流:864305860}每当路由发生变化的时候上面的函数都会被触发,我们可以在这个函数中对页面的数据进行重新加载的操作。如果页面结构变化很大,还是建议单独新建一个不同的页面。上面的问题可能对新入门vue的朋友有用,对vue比较了解了的朋友请绕过。结语感谢您的观看,如有不足之处,欢迎批评指正。

December 18, 2018 · 1 min · jiezi

Flutter路由管理代码这么长长长长长,阿里工程师怎么高效解决?(实用)

背景:在flutter的业务开发过程中,flutter侧会逐渐丰富自己的路由管理。一个轻量的路由管理本质上是页面标识(或页面路径)与页面实例的映射。本文基于dart注解提供了一个轻量路由管理方案。 不论是在native与flutter的混合工程,还是纯flutter开发的工程,当我们实现一个轻量路由的时候一般会有以下几种方法:较差的实现,if-else的逻辑堆叠: 做映射时较差的实现是通过if-else的逻辑判断把url映射到对应的widget实例上,class Router { Widget route(String url, Map params) { if(url == ‘myapp://apage’) { return PageA(url); } else if(url == ‘myapp://bpage’) { return PageB(url, params); } }}这样做的弊端比较明显: 1)每个映射的维护影响全局映射配置的稳定性,每次维护映射管理时需要脑补所有的逻辑分支. 2)无法做到页面的统一抽象,页面的构造器和构造逻辑被开发者自定义. 3)映射配置无法与页面联动,把页面级的配置进行中心化的维护,导致维护责任人缺失.一般的实现,手动维护的映射表: 稍微好一点的是将映射关系通过一个配置信息和一个工厂方法来表现class Router { Map<String, dynamic> mypages = <String, dynamic> { ‘myapp://apage’: ‘pagea’, ‘myapp://bpage’: ‘pageb’ } Widget route(String url, Map params) { String pageId = mypages[url]; return getPageFromPageId(pageId); } Widget getPageFromPageId(String pageId) { switch(pageId) { case ‘pagea’: return PageA(); case ‘pageb’: return PageB(); } return null; }在flutter侧这种做法仍然比较麻烦,首先是问题3仍然存在,其次是由于flutter目前不支持反射,必须有一个类似工厂方法的方式来创建页面实例。 为了解决以上的问题,我们需要一套能在页面级使用、自动维护映射的方案,注解就是一个值得尝试的方向。我们的路由注解方案annotation_route(github地址:https://github.com/alibaba-flutter/annotation_route)) 应运而生,整个注解方案的运行系统如图所示: 让我们从dart注解开始,了解这套系统的运作。dart注解注解,实际上是代码级的一段配置,它可以作用于编译时或是运行时,由于目前flutter不支持运行时的反射功能,我们需要在编译期就能获取到注解的相关信息,通过这些信息来生成一个自动维护的映射表。那我们要做的,就是在编译时通过分析dart文件的语法结构,找到文件内的注解块和注解的相关内容,对注解内容进行收集,最后生成我们想要的映射表,这套方案的构想如图示: 在调研中发现,dart的部分内置库加速了这套方案的落地。source_gendart提供了build、analyser、source_gen这三个库,其中source_gen利用build库和analyser库,给到了一层比较好的注解拦截的封装。从注解功能的角度来看,这三个库分别给到了如下的功能:build库:整套资源文件的处理analyser库:对dart文件生成完备的语法结构source_gen库:提供注解元素的拦截 这里简要介绍下source_gen和它的上下游,先看看我们捋出来的它注解相关的类图:source_gen的源头是build库提供的Builder基类,该类的作用是让使用者自定义正在处理的资源文件,它负责提供资源文件信息,同时提供生成新资源文件的方法。source_gen从build库提供的Builder类中派生出了一个自己的builder,同时自定义了一套生成器Generator的抽象,派生出来的builder接受Generator类的集合,然后收集Generator的产出,最后生成一份文件,不同的派生builder对generator的处理各异。这样source_gen就把一个文件的构造过程交给了自己定义的多个Generator,同时提供了相对build库而言比较友好的封装。 在抽象的生成器Generator基础上,source_gen提供了注解相关的生成器GeneratorForAnnotation,一个注解生成器实例会接受一个指定的注解类型,由于analyser提供了语法节点的抽象元素Element和其metadata字段,即注解的语法抽象元素ElementAnnotation,注解生成器即可通过检查每个元素的metadata类型是否匹配声明的注解类型,从而筛选出被注解的元素及元素所在上下文的信息,然后将这些信息包装给使用者,我们就可以利用这些信息来完成路由注解。annotation_route在了解了source_gen之后,我们开始着手自己的注解解析方案annotation_route 刚开始介入时,我们遇到了几个问题:只需要生成一个文件:由于一个输入文件对应了一个生成文件后缀,我们需要避免多余的文件生成需要知道在什么时候生成文件:我们需要在所有的备选文件扫描收集完成后再能进行映射表的生成source_gen对一个类只支持了一个注解,但存在多个url映射到一个页面 在一番思索后我们有了如下产出首先将注解分成两类,一类用于注解页面@ARoute,另一类用于注解使用者自己的router@ARouteRoot。routeBuilder拥有RouteGenerator实例,RouteGenerator实例,负责@ARoute注解;routeWriteBuilder拥有RouteWriterGenerator实例,负责@ARouteRoot注解。通过build库支持的配置文件build.yaml,控制两类builder的构造顺序,在routeBuilder执行完成后去执行routeWriteBuilder,这样我们就能准确的在所有页面注解扫描完成后开始生成自己的配置文件。 在注解解析工程中,对于@ARoute注解的页面,通过RouteGenerator将其配置信息交给拥有静态存储空间的Collector处理,同时将其输出内容设为null,即不会生成对应的文件。在@ARoute注解的所有页面扫描完成后,RouteWriteGenerator则会调用Writer,它从Collector中提取信息,并生成最后的配置文件。对于使用者,我们提供了一层友好的封装,在使用annotation_route配置到工程后,我们的路由代码发生了这样的变化: 使用前: class Router { Widget pageFromUrlAndQuery(String urlString, Map<String, dynamic> query) { if(urlString == ‘myapp://testa’) { return TestA(urlString, query); } else if(urlString == ‘myapp://testb’) { String absoluteUrl = Util.join(urlString, query); return TestB(url: absoluteUrl); } else if(urlString == ‘myapp://testc’) { String absoluteUrl = Util.join(urlString, query); return TestC(config: absoluteUrl); } else if(urlString == ‘myapp://testd’) { return TestD(PageDOption(urlString, query)); } else if(urlString == ‘myapp://teste’) { return TestE(PageDOption(urlString, query)); } else if(urlString == ‘myapp://testf’) { return TestF(PageDOption(urlString, query)); } else if(urlString == ‘myapp://testg’) { return TestG(PageDOption(urlString, query)); } else if(urlString == ‘myapp://testh’) { return TestH(PageDOption(urlString, query)); } else if(urlString == ‘myapp://testi’) { return TestI(PageDOption(urlString, query)); } return DefaultWidget; } }使用后:import ‘package:annotation_route/route.dart’; class MyPageOption { String url; Map<String, dynamic> query; MyPageOption(this.url, this.query); } class Router { ARouteInternal internal = ARouteInternalImpl(); Widget pageFromUrlAndQuery(String urlString, Map<String, dynamic> query) { ARouteResult routeResult = internal.findPage(ARouteOption(url: urlString, params: query), MyPageOption(urlString, query)); if(routeResult.state == ARouteResultState.FOUND) { return routeResult.widget; } return DefaultWidget; } }目前该方案已在闲鱼app内稳定运行,我们提供了基础的路由参数,随着flutter业务场景越来越复杂,我们也会在注解的自由度上进行更深的探索。关于annotation_route更加详细的安装和使用说明参见github地址:https://github.com/alibaba-flutter/annotation_route ,在使用中遇到任何问题,欢迎向我们反馈。本文作者:闲鱼技术-兴往阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

December 18, 2018 · 2 min · jiezi

示例Express中路由规则及获取请求参数

本次给大家分享一篇基于express中路由规则及获取请求参数的方法,写的十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参考学习下。如有不足之处,欢迎批评指正。express中常见的路由规则主要使用的路由规则是get和post两种,即var express = require(’express’);var app = express();app.get(); // get和post两种请求方式app.post();//欢迎加入前端全栈开发交流圈一起学习交流:864305860app.get()和app.post()的第一个参数为请求路径,第二个参数为处理请求的回调函数;回调函数有两个参数,分别为req和res,代表请求信息和响应信息。获取请求路径和请求体中的各种参数路径请求及对应获取请求路径的形式有以下几种:(1)req.query (查询get请求中的参数)GET /shoes?order=desc&shoe[type]=converse&shoe[color]=bluereq.query.order// =>‘desc’req,query.shoe.type// =>‘converse’(2)req.body (查询请求体)// POST user[name]=dby&user[email]=bing@163.comreq.body.user.name// =>‘dby’(3)req.params// GET /file/javascript/jquery.jsreq.params[0]// => ‘javascript/jquery.js’(4)req.params(name)// ?name=tobireq.params(name)// => ’tobi’// POST name=tobireq.param(’name’)// => ’tobi’//欢迎加入前端全栈开发交流圈一起学习交流:864305860由上述代码可以很明显的看出各种获取路径的含义:req.query: 处理get请求,获取get请求的请求参数req.params: 处理/:xxx形式的get或者post请求,获取请求参数req.body: 处理post请求,获取post了请求的请求体req.param(): 处理get和post请求,但查找优先级由高到低为req.params->req.body->req.query注:路径规则支持正则表达式。结语感谢您的观看,如有不足之处,欢迎批评指正。

December 17, 2018 · 1 min · jiezi