1、内容

  • 手写路由两种实现hashRouter及HistoryApi
  • 手写VueRouter中嵌套路由
  • 手写VueRouter中addRouteres原理
  • 手写RouterLink及RouterView原理
  • 手写VueRouter中钩子实现原理
  • VueRouter3中VueRouter4对比

image.png

两种路由比较

spa 单页应用不刷新 但是要切换页面(组件)
hash路由: 好处 兼容性好, 缺点: 丑
history路由:兼容性不好 通过history.pushState 来进行跳转,跳转时去匹配组件 popState来监控
image.png

使用插件的时候 Vue内部会调用install方法将this(Vue)传入, 保证VueRouter内部不需要再传入Vue,保证内外Vue一致,

image.png

vue-router/index.js

  1. import install from "./install"
  2. import createMatcher from "./create-matcher"
  3. import HashHistory from "./history/hash.js"
  4. import HTML5History from "./history/html5.js"
  5. class VueRouter {
  6. constructor(options){
  7. this.mode = options.mode;
  8. console.log(options)
  9. // 需要创建一个匹配器 (核心 匹配 动态添加)
  10. this.matcher = createMatcher(options.routes||[]);
  11. switch (this.mode) {
  12. case "hash":
  13. this.history = new HashHistory();;
  14. break;
  15. case "history":
  16. this.history = new HTML5History();
  17. break;
  18. }
  19. console.log(this.history)
  20. }
  21. init(app){ // 路由的初始化(1次)
  22. console.log("app根实例")
  23. console.log(this._routerRoot)
  24. console.log(this._router)
  25. // 希望根据历经来进行跳转对应的组件
  26. const history = this.history;
  27. // 此方法应该数据 base中的, 大家都有的
  28. const setupListener =()=>{
  29. history.setupListener(); // 美中方法监控的方式不一样
  30. }
  31. this.history.transitionTo(
  32. history.getCurrentLocation(),
  33. setupListener
  34. )
  35. }
  36. }
  37. VueRouter.install = install
  38. export default VueRouter

vue-router/install.js

  1. // 为了保证vueRouter和Vue 版本相同
  2. const install = function (Vue){
  3. // install的作用就是 将我们的routers实力共享给每个组件
  4. Vue.mixin({
  5. beforeCreate(){
  6. // 区分父子关系,先找父亲,儿子找父亲的属性,孙子找父亲
  7. // 组件生命周期顺序,先父后子
  8. if(this.$options.router){
  9. console.log('父','this.$options.name')
  10. // 将根实例放到_routerRoot上
  11. this._routerRoot = this
  12. this._router = this.$options.router
  13. this._router.init(this)
  14. }else{
  15. // 将根属性全部增加到每个组件的_routerRoot
  16. // 所有组件上都可以获取_routerRoot._router获取路由的实例
  17. this._routerRoot = this.$parent && this.$parent._routerRoot
  18. console.log('子','this.$options.name')
  19. }
  20. }
  21. })
  22. }
  23. export default install;

vue-router/create-matcher.js

  1. // 操作路由表
  2. function createMatcher(routes){
  3. console.log(routes)
  4. // 扁平化处理 一条路径一条记录
  5. let { pathMap } = createRouteMap(routes, oldPathMap);
  6. // matcher 1、原有路由基础上添加路由 2、根据路径匹配路由
  7. function addRoutes(){
  8. }
  9. function match(path){
  10. }
  11. return {
  12. addRoutes,
  13. match
  14. }
  15. }
  16. export default createMatcher

测试 —— test 添加动态路由,后面追加路由
image.png

image.png
create-route-map.js

  1. function createRouteMap(routes,oldPathMap){
  2. let pathMap = oldPathMap || Object.create(null);
  3. // path => recoed
  4. routes.forEach(route=>{
  5. addRouteRecord(route,pathMap)
  6. })
  7. return {
  8. pathMap
  9. }
  10. }
  11. function addRouteRecord(route,pathMap,parent){
  12. let path = parent ? `${parent.path}\${route.path}`: route.path;
  13. let record = {
  14. path:route.path,
  15. name:route.name,
  16. component:route.component
  17. }
  18. pathMap[path] = record
  19. // 如果有儿子就递归
  20. if(route.children){
  21. route.children.forEach(r=>{
  22. addRouteRecord(r,pathMap,record)
  23. })
  24. }
  25. }
  26. export default createRouteMap;

image.png

history/html5.js

  1. import Base from "./base"
  2. class HTML5 extends Base {
  3. consturctor(router){
  4. super(router); // this.router = router
  5. getCurrentLocation(){
  6. return window.location.pathname
  7. }
  8. setupListener(){
  9. // hash 值就是监控hash的变化
  10. window.addEventListener('popstate',()=>{
  11. this.transitionTo(this.getCurrentLocation())
  12. })
  13. }
  14. }
  15. export default HTML5;

history/hash.js

  1. import Base from "./base"
  2. function ensureSlash(){
  3. if(window.location.hash){
  4. return
  5. }
  6. window.location.hash = '/'
  7. }
  8. class HASH extends Base {
  9. consturctor(router){
  10. super(router);
  11. ensureSlash() // 确保有HASH值
  12. getCurrentLocation(){
  13. return window.location.hash.slice(1)
  14. }
  15. setupListener(){
  16. // hash 值就是监控hash的变化
  17. window.addEventListener('hashchange',()=>{
  18. this.transitionTo(this.getCurrentLocation())
  19. })
  20. }
  21. }
  22. }
  23. export default HASH;

history/base.js

  1. function transitionTo(history){
  2. constructor(router){
  3. this.router = router;
  4. }
  5. transitionTo(location,handler){
  6. // 根据路径,match出对应的记录
  7. handler && handler();
  8. }
  9. }

image.png