一种技术登场,总有着它的使命。
前言
今天我们要聊的是路由。
路由是什么?路由是浏览器的地址栏的 URL
,往往不同路由对应不同业务模块。
比如:www.example.com/photo 指的是photo页面; www.example.com/profile 指的是个人资料页面;
而SPA ( single page application )
: 单页面应用。作为前端路由能力的解决方案。
回溯
1. 前后端的协作发展
1 前后端没分离:
特点:
(1)项目与页面结构:前端项目依附在后端指定目录里,如 wwwroot。并且页面和结构相对耦合度高,比如JSP、.net。
前端除了编写自己业务,还得了解后台语法,无形提高很多维护成本。
(2)路由能力:前端缺乏路由能力。请求新路由,会跳转到新的网页,并从服务器端下载 HTML,加载样式,JS等等;
重复请求公共页面,会造成资源浪费;
(3)数据获取:后台会在业务结构里传递全局变量,前端需拿到处理逻辑。另一种则是 Ajax 请求;
优点:SEO友好,首屏渲染快;
缺点:维护成本高,资源浪费;
技术栈:JAVA、PHP、.net、Ajax
2 前后端部分分离:
特点:
(1)项目结构:除了前后端不分离,还存在 把部分脱离后端业务的前端页面(定制或纯展示),放在云存储里。
如:https://www.cdnjs.com/custom-h5.html
(2)路由能力:window.History 模块推出了API pushState,让前端可以实现路由改变。
结合 aJax技术,JQuery推出了pJax库; 具体表现为,后台识别为pjax请求,返回页面某个区域的ajax请求html字符串;
前端替换这个区域的dom渲染,同时利用 pushState 改变路由;
技术栈:Ajax、Pjax、CDN
3 前后端分离:
繁杂的前端业务结构和日益庞大的后台数据逻辑,已经很难再合并一起开发;
特点:
(1)前端的工程化:从工具链(Gulp、Grunt)到大型构建(Webpack),项目脚手架。并独自部署维护项目;
(2)路由:前端可以构建大型应用,并以 SPA 模式去维护项目的跳转路由; 提升用户体验,节省下载资源;
三大框架(Vue React Angular)对 SPA 实践。React-router、Vue-router等;
本文的例子也是基于vue-router 展开;
优点:便于维护; 缺点:首屏渲染白屏时间过长; SEO不友好;
技术栈:Vue React Angular 、Nginx、WebPack
4 前端 SSR:解决SEO问题,将web代码放在服务端渲染。涉及篇幅较多,会另开介绍;
2. SPA
那么我们已经了解到 SPA 是一种路由解决方案。
对于 SPA 理解:
单页 single page 的理解就是从开始加载到离开浏览器,前端都是同一个 HTML。
只是在这个页面做路由监听,以及对应处理(跳转路由,切换DOM结构)。
现在的前端框架对虚拟DOM,数据绑定已是驾轻就熟,在监听路由改变时,无刷新切换路由和更新视图。
application 的理解:当页面和业务规模到一定程度的时候,就是一个应用。
对于 SPA 原理:
路由有两种形式:
(1)带 hash: URL中有#,后面跟着的是hash值
路由例子:https://www.google.com/#hash
设置和获取:`location.hash`
监听改变:
1 | window.addEventListener('hashchange', () => { // code... }) |
(2)不带 hash (也称 history模式)
路由例子:https://www.example.com/user
设置和获取:
1 | // 获取 |
监听改变:
1 | window.addEventListener('popstate', () => { //code... }) |
3. Vue-router 构建 SPA 的一些思考
1 install 钩子方法,注册 vue 组件 router-link router-view 。扩展当前 vue 根实例。也巧妙的给 vue 原型赋值: $route 和 $router,并做了防止原型链污染。
1 | // vue.prototype.$router 是 router实例上一些方法 |
Vue.util.defineReactive 方法,给指定对象定义响应式。这个方法,实现了在vue实例上收集当前路由的依赖。
定义 router-view 组件时,用了函数式组件的写法:funtional: true。特点是:没有this 没有data 没有生命周期,减少性能消耗。
router-view 中的做法:路由匹配得到的数组 类似 [{ path: ‘/home’, component: aa}, {path: ‘/home/a’}, component: bb] ,
递归改变数组匹配深度,调用render 函数,传入对应组件渲染。保证从子到父都渲染到。有点类似冒泡。
2 history 为核心模块。专门处理底层的路由跳转,路由监听,以及路由匹配。(注:这里的底层,是不会直接暴露给用户使用的)
它考虑的很周全,公用的方法采取继承
的方式。公用的方法有 存储和更新当前路由、路由匹配、创建路由方法、队列处理周期钩子(如 beforeEach)
两种路由模式(hash history) 的不同之处:
(1)获取当前路径
(2)监听路由改变:监听的事件不一样,触发的回调也不一样
我们对 $routerd的API
的操作,都会直接映射到这两个基类去操作。比如 push
其实调用的是 history.push
方法,而 push 方法在两模式下实现的方式也各有不同。
队列处理钩子这部分,用到了循环运行迭代器的思想。递归执行到最后,执行回调函数。(点此有介绍)
3 Matcher 模块。负责处理用户 router 配置,递归映射成如: url => route的 map 结构,增加路由等;
4. 总结
结合现在前端框架的组件能力以及浏览器相关 API(location、history、event),也是可以实现 SPA 的。
感谢现在的前端框架,能让我们学的这么多技巧和知识!
- 本文标题:前后端协作历史 && SPA
- 本文作者:Jonnzer
- 创建时间:2022-04-13 19:06:24
- 本文链接:https://jonnzer.github.io/2022/04/13/技术/前后端协作&&SPA/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!