900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > Vue 学习(九 SPA-单页面应用 与 前端路由)

Vue 学习(九 SPA-单页面应用 与 前端路由)

时间:2018-11-12 18:23:00

相关推荐

Vue 学习(九 SPA-单页面应用 与 前端路由)

文章目录

一、前后端架构1. 服务器生成页面2. 前后端分离3. 单页面应用 - SPA 二、路由1. 服务器端路由2. 前端路由3. 结语

一、前后端架构

常见的前后端架构有 服务器生成页面、前后端分离、单页面应用三种,它们有着各自的特点,可以根据实际需求选

择不同的前后端架构方式

1. 服务器生成页面

服务器生成页面架构的 请求-响应 流程图:

这是比较早期的架构方式,通过浏览器向服务器发送一个请求,服务器找到与请求匹配的 JSP 文件,然后执行文件

内部的业务逻辑,最终生成一个完整的、包含数据的 HTML 页面返回给浏览器显示

优点:

架构单一,部署简单

缺点:

1. 因为服务器返回的是一个完整的 HTML, 所以往往内容比较多,占用网络也更多,浏览器白屏时间更长

2. 因为返回的是 HTML 结构,所以原生安卓、原生IOS 或 其他前端语言不能通用该接口,不利于跨终端开发

2. 前后端分离

随着移动设备的普及,多终端开发也越来越普遍,显然 <服务器生成页面> 这种单纯返回 HTML 结构的架构方式已

经不能满足实际产品需求,就是在这种背景下产生了前后端分离的概念,其宗旨是,服务器不再返回完整的 HTML

结构,而是返回 UI 中要用的数据,然后由前端技术拿着数据自己渲染画面

前后端分离概念图:

这种架构可以提升接口的复用性,它不在乎前端使用的具体技术,它只需要提供该功能需要的数据

前后端分离架构的 请求-响应 流程图:

前后端分离的架构解决了跨终端时接口复用性的问题,同时也改变了我们 HTML 页面的 请求-响应 流程,浏览器

先向静态资源服务器发起请求,拉取未填充数据的 HTML 页面,在 HTML 中再利用 AJAX 向接口服务器发送请求,

获取需要的数据,最后再通过 HTML 中的 JS 将数据填充形成一个完整的 HTML 页面

缺点:

网络请求会多一些,每次画面跳转既要访问静态资源服务器,又要访问接口服务器

优点:

1. 可以增加后端接口的跨终端复用性,

2. 开发人员职责分明,后端开发人员只关注接口代码,UI 布局和渲染相关代码由前端开发人员负责

3. 单页面应用 - SPA

单页面应用 ( Single Page Application ) 算是前后端分离的改良版,整个应用程序只有一个 HTML 页面,每次打开应

用时,浏览器会向静态资源服务器请求该页面,后续的页面跳转都是由该 HTML 中引入的 JS 动态渲染,不需要再向

资源服务器发起请求

单页面应用 请求-响应 流程图:

优点:

1. 页面跳转不需要发送网络请求,而是由 JS 渲染,这样页面转场的过度效果更容易实现

2. 跳转不需要发送网络请求,所以 UI 响应更快速、用户体验更好

缺点:

1. 页面都是 JS 动态渲染的,非单独的静态 HTML ,所以不利于 SEO 优化

二、路由

1. 服务器端路由

路由是服务器端开发常提的概念,它包含一个路由关系表,其中保存着路径和处理程序的对应关系,当浏览器发起

一个请求到达服务器时,我们需要通过路由表找到具体的处理程序,如下图的 3、4、5 就是一个路由的大概流程:

2. 前端路由

和服务器端一样,前端路由也是根据 URL 地址来匹配画面,使用 JS 渲染出不同地址对应的画面,但是当页面跳转

事件发生时,我们如何修改 URL 又成了一个新问题, 如果直接使用window.location.href的方式来修改 URL 路径,

浏览器会发生默认跳转,向 URL 所指的服务器发送请求,这显然不符合单页面应用的思想

使用window.location.href的方式跳转页面:

上图中,最初Network里的信息是空的,当在Console中使用window.location.href修改地址后,虽然地址栏中的

URL 发生了变化,但是再看Network中已经有了向服务器请求的信息,所以这种方式并不适合单页面应用

既然window.location.href的方式不适合单页面应用,那么就得考虑其他方式,在这里介绍两个可以改变 URL 又不会

发生服务器请求的方法

1) 使用 hash 的方式

其语法非常简单,window.location.hash = 'URL 地址',先用一下看看效果

和前面一样的操作,但使用window.location.hash修改地址后,Network中却没有向服务器发起请求的信息

(favicon.ico 可以忽略) ,这正满足了 URL 改变而不发生请求的要求,细心的可以发现,地址栏中的 URL 里多

了个#,这是hash方式的特点,#就像一个分界线,当#后的内容改变时, 不会向服务器发送请求

现在用 hash 的方式,来简单仿造一下前端路由,加深一下感受

代码:

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><div id="container"><div>首页</div><button onclick="btnClick('product')">商品</button><button onclick="btnClick('detail')">详情</button></div><script>// 按钮点击事件function btnClick(path) {window.location.hash = path}// 监听 hash 变化window.onhashchange = (e) => {// 清除原内容const container = document.querySelector('#container')container.remove()// 显示新内容const path = window.location.hash.slice(1)// 商品页内容if ('product' === path) {const span = document.createElement('span')span.innerText = '商品页'document.body.appendChild(span)}// 详细页内容if ('detail' === path) {const span = document.createElement('span')span.innerText = '详情页'document.body.appendChild(span)}}</script></body></html>

运行效果:

2) 使用 history 的方式

HTML 5 推出的 history 相关 Api,也可以做到改变 URL 而不发送请求的功能,在此简单介绍两个 Api 的用法

用 history 的方式,来简单实现一个前端路由

前面用 hash 的方式写前端路由时,是将创建的 html 文件直接用浏览器打开,这种情况浏览器默认使用 file 协议,

history.pushStatehistory.replaceState使用 file 协议会涉及到跨域的问题,我们应该将页面放在服务器,

然后让浏览器用 http 协议去访问服务器中的页面,这样才能避免跨域问题

搭建服务器的方式有很多,此处我使用一种对我来说比较方便的方式,就是用 vue-cli 创建一个项目,然后使用

其配置的webpack-dev-server服务器,相关知识 和 vue-cli 的使用方式都已经介绍过,就不再记录过程

通过 Vue-CLI 创建好 vue 项目后,直接修改app.vue文件:

<template><div id="app"><div>首页</div><button @click="btnClick">商品</button></div></template><script>// 自定义事件 - 监听 pushState 事件const bindEventListener = function (type) {const historyEvent = history[type]return function () {const newEvent = historyEvent.apply(this, arguments)const e = new Event(type)e.arguments = argumentswindow.dispatchEvent(e)return newEvent}}history.pushState = bindEventListener('pushState')// 监听 pushState 事件window.addEventListener('pushState', function (e) {// 删除原页面内容const app = document.querySelector('#app')app.removeChild(document.querySelector('#app > div'))app.removeChild(document.querySelector('#app > button'))// 新建商品页内容if (e.arguments[2].slice(1) === 'product') {const span = document.createElement('span')span.innerText = '商品'document.body.appendChild(span)}})export default {name: 'App',methods: {btnClick() {window.history.pushState({}, '', '/product')}}}</script>

运行效果:

通过Network页可以看出,history 的方式也没有发生服务器请求,而且地址栏路径也更清爽,不再有#标志,不

过代码中有一点要注意,因为 DOM 默认没有对history.pushState事件的监听,所以我们需要自定义一个事件,既

这一部分代码:

const bindEventListener = function (type) {const historyEvent = history[type]return function () {const newEvent = historyEvent.apply(this, arguments)const e = new Event(type)e.arguments = argumentswindow.dispatchEvent(e)return newEvent}}history.pushState = bindEventListener('pushState')

3. 结语

不论是使用 hash 的方式,还是使用 history 的方式,我们的页面都是由 JS 渲染出来的,而不是通过服务器响应回

来的,这样每个画面就没有缓存,这会导致在使用浏览器的前进和后退按钮时,有可能出现地址变化但是页面不变的

问题,所以要想实现一个可靠性高的前端路由功能,是有很多细节要考虑的

和以前一样,这篇文章浅显的介绍前端路由相关知识,就是为了给后面要学习的路由插件 vue-router 做铺

垫,如果想自定义一个产品级的前端路由组件,那还需要很多知识要去自己摸索和掌握

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。