A. 什么是路由路由分为哪两类
提到路由,大家一般会想到生活中常见的路由器,路由器主要用于连接多个逻辑上分开的网络,逻辑网络代表-个单独的网络或者一个子网,可以通过路由器功能来完成不同网络之间数据的传递。在Vue中也引人了路由的概念,因此,我们先来对程序开发中的路由进行简单地了解。
程序开发中的路由分为后端路由和前端路由,下面我们分别进行简要介绍。
1. 后端路由
后端路由通过用户请求的URL分发到具体的处理程序,浏览器每次跳转到不同的URL都会重新访问服务器。服务器收到请求后,将数据和模板组合,返回HTML页面,或者直接返回HTML模板,由前端JavaScript程序再去请求数据,使用前端模板和数据进行组合生成最终的HTML页面。下图演示了后端路由的工作原理。
2. 前端路由
前端路由就是把不同路由对应不同的内容或页面的任务交给前端来做。前端路由和后端路由的原理是类似的,但是实现的方式不一样。对于单页面应用(Single Page Application, SPA)来说,主要通过URL中的hash(#号)来实现不同页面之间的切换。hash有一个特点,就是HTTP请求中不会包含hash相关的内容,所以单页面程序中的页面跳转主要用hash来实现。
下图演示了前端路由的工作原理。
在上图中,index.html 后面的‘#home”是hash方式的路由,由前端路由来处理,将hash值与页面中的组件对应,当hash值为“#/home” 时,就显示“首页”组件。前端路由在访问一个新页面的时候仅仅是变换了一下hash值而已,没有和服务端交互,所以不存在网络延迟,提升了用户体验。
B. 前端路由和后台路由有什么区别
区别在于express是服务器端的路由,也就是说需要向后台服务器发送请求,然后服务器来决定来render那个.html,这也就是最早的mvc架构模式,而前端的路由是将这一过程放在浏览器端,也就是前台写js代码控制,不在请求服务器,前台一般利用histroy和hash来控制,达到不刷新页面可以使显示内容发生变化,这样好处是js代码不发生变化(浏览器端可以维护一个稳定的model);一般单页应用就是前台来控制路由,这样速度更快,用户体验更好。单页应用还将模板拿到了浏览器端,从而解放了服务端,服务端趋于服务化。
C. 前端路由(二)
前面我们做到了切换路由不发送请求,现在我们要 把路由和组件对应起来达到渲 染。
接下来介绍的是VueRouter的原理,它是怎么做到的这个功能的。其会在 根Vue 上注册2个全局 函数式组件 <router-link> <router-view>,在根Vue原型上 定义$route(当前路由Route对象)和$router(传入newVue的router对象列表)两个属性(所有子Vue实例会继承)。
<router-link>,作为一个子组件,初始化渲染时会去执行render函数,主要做了 其 内部属性 (tab标签,activeClass等)的 处理 ,在点击时会去 执行router.push做url变化 。它不涉及渲染,逻辑比较简单。
执行router.push(replace)做url变化和初始化VueRouter时都会去触发 transitionTo方法做路径切换 。这个方法里做了很多事情,接下来会介绍。它执行完毕的成功回调中会切换url。
最重要的问题是渲染组件 , <router-view>如何知道去渲染哪个组件 ?我们手头有一个按文档规定写的new VueRouter({ routers })列表,传给了根Vue,VueRouter通过它做了很多事情。
首先在new VueRouter()时,会去执行其构造函数,其中 createMatcher 方法,会递归遍历routers把每个router对象进行重新描述得到 RouteRecord 对象,并由它们得到3个列表( pathList (路由path列表), pathMap (路由path: record列表), nameMap (路由name:record列表)),这3个列表是为导航守卫服务的我们先不管它。createMatcher最后返回2个方法,1、 addRoutes ,动态对上面3个列表修改。2、 match ,根据传入的位置和当前的路径,计算出新的路径为 Route对象。
transitionTo路径切换时,会去执行 match 函数计算新路径Route对象,其有一个属性值 matched ,是从当前RouteRecord向上(parent)查找直到根RouteRecord的到的RouteRecord数组,这样得到一个 层级关系 。我们在<router-view>的render函数中会标志flag表示是router-view组件,我们上(父组件)查找有flag标志就 会把 depth++,最后得到 当前<router-view>的深度 。我们通过 $route.matched [depth]就可以 找到router-view需要去渲染的组件。
这里还有一个问题, 我们怎么知道当前$route是哪个? 在初始化routerVue,init()中规定了所有子组件的实例的$route属性指向根Vue的$route属性,根Vue的$route属性又等于this._router.history.current。而current这个值又是在路径切换时会变化的。也就是说我们路径变化会把当前$route指为当前组件路由的route路径对象,这个我们不用担心。
概括: 就是做路径切换时,我们会根据我们写的路由表,把当前路由往上到根路由的路由对象组成一个数组,描述一个 层级关系List 。<router-view>是函数式组件,它有标志flag,我们也会从它往上查找<router-view>直到根vue,有就depth++,得到当前<router-view> 层级位置depth 。 List[depth].component就是我们要渲染的组件。
我们知道<router-view>要去渲染哪个组件,它是怎么做到更新视图的? 更新视图肯定也要符合Vue渲染原理呀,要把1个数据响应式化,get时收集订阅者Watcher并初始化渲染,set时派发更新 把订阅该数据的Watcher重新渲染。这个响应式数据是谁呢?就是定义在根Vue的_route,子Vue的$route也都指向它。<router-view>函数render执行的时候,会去取$route相当于访问根Vue的_route,会触发订阅者Watcher收集并初始化渲染。做路径切换时,会修改_route(即记录当前路由路径的this._router.history.current)。
触发点 :router-link 提供了"下一个位置参数",准备去切换url时,触发transitionTo方法去做路径切换。
响应式化数据 :history.current。记录当前路径Route对象(由当前路由和下一个位置计算出来),收集订阅者,派发更新渲染,都围绕它进行。
路由配对组件 :当前路径Route对象(由当前路由和下一个位置 计算出来)和我们写的路由表,得到当前路径Route对象到根路径的 路由路线 数组,<router-view>肯定在这之内,不会比其更深层,所以在这数组之内可以用depth取到。
守卫导航是怎么做到的 ?守卫导航就是transitionTo方法做路径切换时执行的一系列钩子函数。
这些钩子函数 有些定义在全局 ,用 this.router.xxx可以取到。
有些定义在组件内 ,通过前面的Routed.matched,即将离开路由的matched列表和当前路由的matched列表,从头对比到第一个不同,得到 updated (目标RouteRecord和当前RouteRecord相同,前面重复的部分)、 activated( 目标RouteRecord和当前RouteRecord不同,后面不同的部分 ) 、 deactivated( 当前RouteRecord和目标RouteRecord不同,后面不同的部分 ) 三个RouteRecord数组。 通过这些RouteRecord去取定义在组件中的导航守卫 。
有些定义在路由中 ,也通过这些 RouteRecord去取。
到这里就完成了,接下来会分析前端路由在实际开发中的运用,比如页面权限设置!
D. 前端路由(1):基础知识
URI: Uniform Resource Identifier 统一资源标识符; 由5部分组成:
URI = scheme:[//authority]path[?query][#fragment]
scheme: 协议 常见的有 http , https , file等
authority: 可以由三部分组成 [userinfo]host:[port]
其中userinfo指用户信息,可以通过authority中的userinfo进行的登录(当然现在前端应用并不会这么做)
host指的是主机地址,可以是ipv4或者是用方括号括起来的ipv6地址,或者是可以通过DNS解析成ip地址的域名,如www..com
port指端口号, 如果不指定则使用默认端口号,http默认端口号80, https默认端口号443
path: 指文件路径,指定服务器上文件路径以访问特定资源
query: 查询字符串 又称search值 get接口传参会通过此部分进行传输
fragment: 片段标识符 又称hash值, 通常用于标记已获取资源的子资源,不会被传递到服务器端
关于编码:
URI是用的是百分号编码,对于需要编码的字符,将其表示为2个十六进制的字符,然后在其前面加入转义字符%
两个编码api:
encodeURI 与 encodeURIComponent: encodeURI编码的范围比encodeURIComponent要小 ,其中encodeURI是w3c的标准。
window.history对象存在很多属性, 比较重点关注的有:
readonly length: number;
readonly state: any;
方法:
pushState(state: any, title: string, url?: string | null) : void;
replaceState(state: any, title: string, url?: string | null) : void;
go(delta: number): void 与 back():void; forward():void;
第一个参数需要是可被结构化复制的数据类型, (结构化复制: 可以处理循环引用的JSON),会持久化存储在浏览器内部,在每次页面生成的时候会被重新提取出来(属于浏览器实现的深拷贝)
第二个参数title传字符串,用于标记当前方法
第三个参数url,可以传简单字符串,也可以传url对象,需要注意的是因为此方法被同源策略所限制,url必须与当前href同源,否则会报错。此参数可以不传,不传的时候就是单纯的操作浏览器历史栈。
方法作用: 生成一个新的历史栈并将指针指向它,操作并不会刷新浏览器,也就是说此方法会改变浏览器的历史栈length,和state对象;
replaceState与pushState参数完全相同,使用方法类似,但其作用是替换当前历史栈,也就是说历史栈的指针与长度不会发生变化,其作用仅仅是替换当前的url与state。
pushState与replaceState方法其url传参方式多种多样,可以是绝对路径,也可以是相对路径,也可以传递查询字符串search值"?xxx"与片段标识符"#xxx",其最大特点是操作浏览器url,history对象的state与length属性,但不会触发浏览器跳转。
pushState与replaceState方法以相对路径方式进行操作url的时候,会受到当前html的<base>元素的href的影响,此时base元素的href会替换浏览器的url的href作为基准值进行相对路径跳转。
浏览器的跳转方法主要围绕window.location与window.history这两个对象进行。
1) window.history
window.history.go(delta: number): void;
移动浏览器历史栈指针并且刷新页面
window.history.back() === window.history.go(-1)
window.history.forward() === window.history.go(1)
2) window.location
window.location.href
get :() =>string;
set : (url: string) => void; // 触发浏览器跳转 并增加历史栈 不收同源策略限制
window.location.hash
get: () => string;
set: (hash: string) => void;
与href类似,其区别是用于修改浏览器导航栏url的hash值,会产生新的历史栈,但不会触发浏览器页面刷新,并且在set hash值与当前url的hash值相同时,不会有任何事发生(等于没执行)。
window.location.replace
这是一个纯方法,其作用是替换当前浏览器的栈记录,设置为传入新的URL,并且刷新页面。
replace可以对hash进行操作并且触发对应的事件,不会受相同hash的规则影响
1) popstate事件
history.pushState与history.replaceState产生的历史栈记录中,如果栈指针发生了移动,或者点击了浏览器的前进或者回退按钮时,会触发popstate事件,可以通过window.addEventListener去添加事件监听。
popstate拿到的event对象最关键的属性是event.state,这个event.state是直接从浏览器底层存储器中取出(属于深拷贝),而非从历史栈中的state取出,因此直接修改历史栈state并不会对event.state造成影响(反之亦然)。
前后两次设置相同的location.hash时不会触发两次popstate事件,但是通过location.href设置两次相同的hash可以,虽然可以触发两次popstate事件,但是历史栈只会增加一层。
2)hashchange事件
hashchange时间监听的是浏览器url的片段标识符的变化,也就是hash值的变化,事件对象event可以拿到关键的oldUrl和newUrl,表示hash跳转前的url和跳转后的url。
pushState方法即使是只改动了浏览器的hash值,也不会触发hashchange事件。
这两个事件都可以通过window.dispatchEvent方法去触发,dispatchEvent方法需要传入一个事件对象:
window.dispatchEvent(new PopStateEvent());
对于hashchang事件同理:
window.dispatchEvent(new HashChangeEvent());
E. 前端路由 有没有必要
web开发的传统MVC路由是由后端动态服务器控制的,你一个请求需要请求服务器,会根据你在动态服务器上设定的路由规则拼数据渲染页面。前端路由是通过hash,也就是URL后面的#部分(可以通过一些框架称的html5 mode重写成常规的path),你访问的只是首页,例如index.html,请求静态资源服务器,你依赖的SPA框架,根据你配置的前端路由规则,部分替换首页结构实现前端路由,页面的拼装是在浏览器端做的,只是会向静态资源服务器拉取局部调整用的模板。
F. 前端有路由器如何连接hify
步骤1、连接线路。
由于HyFi智能无线路由器与HyFi智能无线扩展器之间的正常注册需要使用到智能无线路由器的DHCP服务,因此对于前端有路由器的环境中,建议使用LAN-WAN级联。
将前端的网线接入HyFi智能无线路由器的WAN口,电脑通过有线连接到HyFi智能无线路由器的LAN口或者无线连接HyFi智能无线路由器的无线信号。
步骤2、设置HyFi智能无线路由器的管理IP地址。
为避免由于和前端路由设备网段冲突导致HyFi智能无线路由器无法正常连接到前段网络,需要修改HyFi智能无线路由器的管理IP地址网段。
电脑登陆HyFi智能无线路由器的管理界面(默认地址为http://192.168.1.1),点击“网络参数--LAN口设置”,修改IP地址为其他网段(例如192.168.2.1)后保存,设备将会重启。
步骤3、注册HyFi产品。
设置完HyFi智能无线路由器后,还需要将HyFi智能无线扩展器进行注册才可以正常使用HyFi产品。
在设备都通电情况下,两分钟内分别按一下HyFi智能无线路由器TL-H18R和HyFi智能无线扩展器TL-H18E上的Config按钮,待HyFi智能无线扩展器的指示灯由闪烁变为常亮时,即完成设备的注册。
如果有多个HyFi智能无线扩展器,使用相同方法多次与HyFi智能无线路由器进行注册即可。
G. 前端路由”有哪些优点和缺点
前端路由的优点和缺点:
优点
用户体验好,不需要每次都从服务器全部获取,快速展现给用户
缺点
使用浏览器的前进,后退键的时候会重新发送请求,没有合理地利用缓存
单页面无法记住之前滚动的位置,无法在前进,后退的时候记住滚动的位置
H. :前端路由是什么东西
前端路由是不涉及服务器的,是前端利用hash或者HTML5的history API来实现的,一般用于不同内容的展示和切换。
其实前端路由要做的就是两点:
在页面不刷新的情况下实现url的变化
捕捉url的变化,根据url更改页面内容
I. Vue Router 前端路由实现思路
什么是路由:满足一对多的情况,主要是用来分发请求,经过一些中间节点后到达最终目的地。
路由通常根据路由表:一个存储到各个目的地的最佳路径的表来引导分组传送。
hash模式: 任何情况下都能做前端路由
缺点:SEO不友好(服务器收不到hash)
例如我们访问.com/#1 或 .com/#marshall 或 .com/#anything 都相当于直接访问.com
但是谷歌有对应的优化(虽然不能和传统SEO媲美),服务器需要做一定的配置,有兴趣自行搜索Google Hash SEO
history模式: 只有一种情况下可以使用——后端将前端路由都渲染到同一页面(同一页面不能是404,404的页面是固定的)
IE8不支持
memory模式:
不改URL后缀,存在localStorage
适合非浏览器,例如在app里做路由,因为app里没有路径,
三者对比: history模式和hash模式是可分享的,而memory模式的缺点是没有url,只对单机有效。
J. 什么是"前端路由"什么时候适合使用"前端路由""前端路由"有哪些优点和缺点
你的理解有错误,所谓的前端路由是为了和后端路由区分开。后端路由也叫做二级路由,三级路由!简单举个例子,你在光猫后面插的第一个路由器就叫做前端路由,它主管你连接在这台路由器上的其他所有路由器,也就是二级或者三级路由器,在家用领域这个概念基本不会使用!但在公司或者一些比较大的企业,前端路由器的功能决定你公司的其他电脑的功能,比如带宽,认证,收费等!~