前端面试复习日计划day3

路由相关总结

1.SPA

现代前端项目多为单页Web应用(SPA),在单页Web应用中路由是其中的重要环节。
SPA 是 single page web application 的简称,译为单页Web应用。
简单的说 SPA 就是一个WEB项目只有一个 HTML 页面,一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转。 取而代之的是利用 JS 动态的变换 HTML 的内容,从而来模拟多个视图间跳转。

2.SEO

JavaScript SEO 是技术 SEO(搜索引擎优化)的一部分,旨在使包含较多 JavaScript 网站容易被抓取和索引,以及更加搜索引擎友好。目标是为了使这些网站可以被搜索引擎发现,并获得更高的排名

前端路由

简单的说,就是在保证只有一个 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

history基本api

  • history.go(n):路由跳转几步,n为2往前跳转2个页面,-2往后跳转两个页面
  • history.back():路由后退,相当于 history.go(-1),用户可点击浏览器左上角的后退按钮模拟此方法
  • history.forward():路由前进,相当于 history.go(1),用户可点击浏览器左上角的前进按钮模拟此方法

pushState()

history.pushState():添加一条路由历史记录,如果设置跨域网址则报错

history.pushState用于在浏览历史中添加历史记录,但是并不触发跳转,此方法接受三个参数,依次为:
state:一个与指定网址相关的状态对象, popstate事件触发时,该对象会传入回调函数。如果不需要这个对象,此处可以填 null
title:新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填 null
url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。

window的popstate事件

当活动历史记录条目更改时,将触发popstate事件。如果被激活的历史记录条目是通过对history.pushState()的调用创建的,或者受到对history.replaceState()的调用的影响,popstate事件的state属性包含历史条目的状态对象的副本。
需要注意的是调用history.pushState()或history.replaceState()不会触发popstate事件。只有在做出浏览器动作时,才会触发该事件,如用户点击浏览器的回退按钮(或者在Javascript代码中调用history.back())

<!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(path) {
        this.routes = {}; // 记录路径标识符对应的cb
        history.replaceState({ path }, null, path); // 进入状态
        this.routes[path] && this.routes[path]();
        window.addEventListener("popstate", e => {// 当用户点击浏览器的前进或者后退触发
            console.log(e.state)
          const path = e.state && e.state.path;
          this.routes[path] && this.routes[path]();
        });
      }
      
      /* 初始化 */
      static init() {
        window.Router = new RouterClass(location.pathname);
      }

      /* 注册路由和回调 */
      route(path, cb) {
        this.routes[path] = cb || function() {};
      }

      /* 跳转路由,并触发路由对应回调 */
      go(path) {
        
        history.pushState({ path }, null, path);
        console.log(history);
        this.routes[path] && this.routes[path]();
      }
    }

    RouterClass.init();
    const ul = document.querySelector("ul");
    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页面"));

    ul.addEventListener("click", e => {
      console.log(e.target.tagName);
      if (e.target.tagName === "A") {
        e.preventDefault();
        Router.go(e.target.getAttribute("href"));
      }
    });
  </script>
</html>

Hash 模式和 History 模式对比

Hash 模式是使用 URL 的 Hash 来模拟一个完整的 URL,因此当 URL 改变的时候页面并不会重载。History 模式则会直接改变 URL,所以在路由跳转的时候会丢失一些地址信息,在刷新或直接访问路由地址的时候会匹配不到静态资源。因此需要在服务器上配置一些信息,让服务器增加一个覆盖所有情况的候选资源,比如跳转 index.html 什么的

hash路由 优缺点

  • 优点

    • 实现简单,兼容性好(兼容到ie8
    • 绝大多数前端框架均提供了给予hash的路由实现
    • 不需要服务器端进行任何设置和开发
    • 除了资源加载和ajax请求以外,不会发起其他请求
  • 缺点

    • 对于部分需要重定向的操作,后端无法获取hash部分内容,导致后台无法取得url中的数据,典型的例子就是微信公众号的oauth验证
    • 服务器端无法准确跟踪前端路由信息
    • 对于需要锚点功能的需求会与目前路由机制冲突

History(browser)路由 优缺点

  • 优点

    • 对于重定向过程中不会丢失url中的参数。后端可以拿到这部分数据
    • 绝大多数前段框架均提供了browser的路由实现
    • 后端可以准确跟踪路由信息
    • 可以使用history.state来获取当前url对应的状态信息
  • 缺点

    • 兼容性不如hash路由(只兼容到IE10)
    • 需要后端支持,每次返回html文档

vue-router中默认使用的是hash模式,也就是会出现如下的URL:
vue-router中hash模式


vue项目携带一个#会对开发造成一定的影响:

1、微信三方登录回调地址,有#号时,接收不到code参数

2、微信H5支付的回调地址,不允许有#

3、App分享,处理特殊字符时,可能会对#进行编译

4、有点影响美观

现在网站大多有三方登录,支付等功能,存在#,项目开发就存在很大的障碍。话不多说,开始去掉#吧。

去#的原理

vue把#当做自己的根目录,静态资源,比如图片。

去掉#后,要采用相对路径去引用,如果图片引用是在js内,则要采用require()方法进行引用。

去#后台渲染,请参考vue官方文档

如何去掉

1、首先判断产线项目是否放在域名根目录下

在router/index.js文件中,添加history模式

如果项目不在根目录下,比如产线项目根目录为 http://www.baidu.com/project-name/

export default new Router({
  mode: 'history',
  base: '/project-name/', //如果项目根目录不为域名,则添加该行
  routes: [{}]

  • 1
  • 2
  • 3
  • 4
  • 5

2、修改config/index.js文件

build: {
    // Paths
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',
    assetsPublicPath: '/project-name/', //添加根目录,如果域名为根目录,则为 '/'
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这样子URL中的#号就被去除了。

去掉#,对项目的根目录造成影响,这是不可避免的,再次强调,确保静态资源文件路径正确。

修改为history需要后端配合

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值