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


一、前后端架构


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


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 的方式跳转页面:
使用href跳转
上图中,最初 Network 里的信息是空的,当在 Console 中使用 window.location.href 修改地址后,虽然地址栏中的
URL 发生了变化,但是再看 Network 中已经有了向服务器请求的信息,所以这种方式并不适合单页面应用

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


1) 使用 hash 的方式

其语法非常简单,window.location.hash = 'URL 地址',先用一下看看效果
使用hash跳转
和前面一样的操作,但使用 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>

运行效果:
hash模拟前端路由

2) 使用 history 的方式

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

api参数描述
window.history.pushState({}, ‘’, ‘路径’)前两个参数不常用,第三个参数代表修改的路径路径会保存在历史记录中,支持前进后退按钮
window.history.replaceState({}, ‘’, ‘路径’)前两个参数不常用,第三个参数代表修改的路径路径不会保存在历史记录中,不支持前进后退按钮

用 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 = arguments
    window.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>

运行效果:
使用history方式改变路径

通过 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 = arguments
    window.dispatchEvent(e)
    return newEvent
  }
}
history.pushState = bindEventListener('pushState')

3. 结语

不论是使用 hash 的方式,还是使用 history 的方式,我们的页面都是由 JS 渲染出来的,而不是通过服务器响应回
来的,这样每个画面就没有缓存,这会导致在使用浏览器的前进和后退按钮时,有可能出现地址变化但是页面不变的
问题,所以要想实现一个可靠性高的前端路由功能,是有很多细节要考虑的

和以前一样,这篇文章浅显的介绍前端路由相关知识,就是为了给后面要学习的路由插件 vue-router 做铺
垫,如果想自定义一个产品级的前端路由组件,那还需要很多知识要去自己摸索和掌握

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值