Vue Router

完整的导航解析流程
  • 官方文档

  • 导航被触发

  • 在失活组件(from)中调用组件内守卫beforeRouteLeave

    • 离开守卫通常用来禁止用户在还未保存修改前突然离开。本次导航可以通过 next(false) 来取消
  • 调用全局的beforeEach守卫

    • 一般被用于登录验证
  • 若是导航至重用组件,也就是相同组件,则此时调用组件内守卫beforeRouteUpdate(2.2+)

  • 调用路由配置中的beforeEnter

  • 开始解析异步路由组件

    • 也就是这种形式导入的组件component: () => import("url")
  • 在被激活的组件中调用beforeRouteEnter

    • 该守卫中无法使用this,因为此时组件实例还未被创建,所以为undefined

    • 但可在next函数的参数中传入回调函数,该回调函数参数vm,即为实例,于导航被确认时执行

    • 若是重用组件,则只会在第一次进入重用组件时调用该守卫,重用组件自身切换不会再次调用

  • 调用全局的 beforeResolve 守卫 (2.5+)

    • router.beforeEach类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,该守卫才被调用
  • 导航被确认

  • 调用全局的 afterEach 守卫

  • 触发 DOM 更新

  • 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数

原理

hash版

  • <a href="#/dasha">dasha</a>

  • 若不为a标签,则监控点击事件即可

    • <span onClick="routerChange('/dasha')">dasha</span>

    • 直接传入路径作为参数

  • function routerChange(pathName) { location.hash = pathName; } 赋值后自动添加#

  • 监听hashchange事件

  • window.addEventListener("hashchange", () => {})

  • 通过location.hash可获取页面hash

  • 第一次进入该页面时,不会有hashchange,所以可以使用DOMContentLoaded事件做初始化

    • window.addEventListener("DOMContentLoaded", () => {})

history版

  • html
<span onclick="routerChange('/')">首页</span>
<span onclick="routerChange('/chloe')">chloe</span>
  • js

    • 通过H5中的history.pushState实现不刷新页面,改变url中的路径

    • 通过popstate事件监听浏览器的前进回退事件

    • 通过location.pathname可获取当前页面路径

function routerChange(pathName) {
  history.pushState(null, null, pathName);
}

window.addEventListener("popstate", () => {})

hash与history区别

  • hash

    • 带有#,不会和服务器产生交流
  • history

    • 没有#,正常的url改变,会和服务端产生交流

实现基础Vue Router

  • 便于查看 所以未模块化
当前路由的信息
class History {
  constructor() {
    this.current = {
      path: ""
    };
  }
}

router实例构造函数
class VueRouter {
  constructor(options) {
    this.routesMap = this.createRouteMap(options.routes || []);
    history即为$route
    this.history = new History();
    拿到模式
    this.mode = options.mode || "hash";
    初始化route信息
    this.init();
  }

  处理路由组件信息 以路径为key component为值
  createRouteMap(routes) {
    const routesMap = {};
    for (let i = 0; i < routes.length; i++) {
      routesMap[routes[i].path] = routes[i].component;
    }
    return routesMap;
  }

  初始化route信息
  init() {
    if (this.mode === "hash") {
      location.hash || (location.hash = "/");
      document.addEventListener("DOMContentLoaded", () => {
        this.history.current.path = location.hash.slice(1);
      });
      window.addEventListener("hashchange", () => {
        this.history.current.path = location.hash.slice(1);
      });
    } else {
      document.addEventListener("DOMContentLoaded", () => {
        this.history.current.path = location.pathname;
      });
      window.addEventListener("popstate", () => {
        this.history.current.path = location.pathname;
      });
    }
  }
}

Vue.use()会默认调用install方法
VueRouter.install = (Vue) => {
  this.$options.router即为new VueRouter创建的实例
  给所有组件都混入beforeCreate钩子 该组件的$options上若有router
  则说明该组件为根实例 因为router只在new Vue()上设置了
  Vue.mixin({
    给根实例添加_router和_route属性
    beforeCreate() {
      if (this.$options.router) {
        给_router赋值为该实例
        this._router = this.$options.router;
        给_route赋值 使用Vue的响应式方法 在_route改变时 重新渲染页面
        Vue.util.defineReactive(this, "_route", this._router.history.current);
      }
    }
  });

  通过Object.defineProperty而不是Vue.prototype.$router给$router赋值
  可实现在get方法中拿到调用$router的组件实例,this.$router
  $router即为new VueRouter所创建的实例
  Object.defineProperty(Vue.prototype, "$router", {
    get() {
      返回根实例上的router实例
      return this.$root._router;
    }
  });

  与$router同理
  Object.defineProperty(Vue.prototype, "$route", {
    get() {
      返回根实例上的router上的history
      return this.$root._route;
    }
  });

  router-link组件
  Vue.component("router-link", {
    props: {
      to: {
        type: String,
        require: true
      },
      tag: {
        type: String,
        default: "a"
      }
    },
    methods: {
      手动改变hash值
      handleClick() {
        const mode = this.$router.mode;
        if (mode === "hash") {
          location.hash = this.to;
        } else {
          history.pushState(null, null, this.to);
          this.$router.history.current.path = this.to;
        }
      }
    },
    render(h) {
      const mode = this.$router.mode;
      const data = {};

      if (this.tag === "a" && mode === "hash") {
        data.attrs = { href: "#" + this.to };
      } else {
        data.on = {
          若不是a标签 则触发点击事件
          click: this.handleClick
        };
      }

      通过h而不是JSX来创建 可渲染不同的tag
      return h(this.tag, data, this.$slots.default);
    }
  });

  router-view组件
  Vue.component("router-view", {
    functional: true,
    render(h, { parent }) {
      拿到routesMap
      const routesMap = parent.$router.routesMap;
      const path = parent.$route.path;
      return h(routesMap[path]);
    }
  });
};

export default VueRouter;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容