Используйте собственный JS для инкапсуляции Vue-Router

Краткое содержание

Некоторое время назад в Vue-router были реализованы некоторые методы и принципы, давайте разберемся здесь, по сравнению с аксиомами, реализованными в предыдущей статье, маршрутизация, реализованная на этот раз, более сложная.
Методы реализации в основном включают в себя функции push, go, replace и hook функций beforeEach и afterEach.
Принцип реализации в основном включает URL и ответ страницы, а также router-view и router-link.

1. хеш и история

Прежде чем писать, нам нужно понять разницу между хешем и историей, главное отличие в том, что

Режим хеширования изменяет маршрут без повторного запроса, а перед маршрутом в URL-адресе стоит #. В режиме
истории перед маршрутом в URL-адресе нет символа #, и изменение URL-адреса потребует повторного запроса.

Независимо от того, какой метод мы реализуем, мы должны быть совместимы с этими двумя режимами.
И Vue-router также может установить, будет ли это режим хеширования или режим истории при создании экземпляра.
Таким образом, наш класс нужно написать так, когда он будет создан:

function myrouter (obj) {
    
    
  this.mode = obj.mode || 'hash'
}

2.push, go, replace методы

Во-первых, мы еще не реализовали router-view, поэтому вместо этого используем на странице тег h1.

(1) толчок

Для хеш-режима мы можем напрямую изменить хэш-значение URL-адреса.
Но в режиме истории нам нужно использовать метод pushState.

myrouter.prototype.push = function (data) {
    
    
  //判断路由模式
  if (this.mode == 'hash') {
    
    
    location.href = orgin + '/#' + ifObj(data);
    h1.innerHTML = ifObj(data)
  }
  else if (this.mode = 'history') {
    
    
    history.pushState({
    
    }, '', orgin + ifObj(data))
    h1.innerHTML = ifObj(data)
  }
}

ifObj — это метод определения того, является ли объект объектом:

//判断参数类型
function ifObj (data) {
    
    
  if (typeof data == 'object') {
    
    
    return data.path
  } else {
    
    
    return data;
  }
}

(2) идти

Для метода go это еще проще, просто используйте его напрямую:

myrouter.prototype.go = function (num) {
    
    
  history.go(num)
}

(3) заменить

Для метода замены нам
нужно вызвать метод location.replace() в хеш-режиме.
В режиме истории необходимо вызвать метод replaceState().

myrouter.prototype.replace = function (data) {
    
    
  let orgin = location.origin;
  //判断路由模式
  if (this.mode == 'hash') {
    
    
    //判断是否使用路由拦截
    location.replace(orgin + '/#' + ifObj(data));
    h1.innerHTML = ifObj(data)
  } else if (this.mode == 'history') {
    
    
    history.replaceState({
    
    }, '', orgin + ifObj(data))
    h1.innerHTML = ifObj(data)
  }
}

Хорошо, мы реализовали самые основные функции для этих трех методов, но нам нужно изменить их позже. Включение перехвата маршрута повлияет на эти три метода.

3. Метод мониторинга

Прежде всего, даже если мы реализуем эти три метода, если мы напрямую изменим маршрут в URL-адресе, страница все равно не будет отвечать, поэтому нам нужно отслеживать URL-адрес страницы.

И окно поставляется с некоторыми методами мониторинга URL:
для режима хеширования: onhashchange может отслеживать изменение значения хеш-функции.
Для режима истории: onpopstatea может отслеживать такие операции, как перенаправление вперед и назад.

myrouter.prototype.update = function () {
    
    
  let _this = this;
  //判断路由模式
  if (this.mode == 'hash') {
    
    
    //监听url哈希值的变化
    window.onhashchange = function () {
    
    
      h1.innerHTML = location.hash.replace('#/', '')
    }
  }
  else if (this.mode == 'history') {
    
    
    //监听history模式下前进后退的操作
    window.addEventListener('popstate', function () {
    
    
      h1.innerHTML = location.pathname
    });
    window.onload = function () {
    
    
      h1.innerHTML = location.pathname
    }
  }
}

Здесь основная функция onload заключается в том, что в режиме истории, если мы изменим URL-адрес страницы, страница будет обновлена, поэтому мы используем метод onload для мониторинга.

4. Функция хука перед каждой

Мы знаем, что хук-функция beforeEach принимает функцию обратного вызова в качестве параметра, поэтому нам нужно сохранить эту функцию обратного вызова в нашем исходном коде:

myrouter.prototype.beforeEach = function (config) {
    
    
  this.saveBefore = config;
}

И эта функция обратного вызова также принимает три параметра, поэтому наш конструктор нужно изменить:


function myrouter (obj) {
    
    
  this.mode = obj.mode || 'hash'
  this.saveBefore = null
  this.saveAfter = null
  this.from = ''
  this.to = ''
}

И независимо от того, в каком методе мы находимся, мы всегда должны обновлять значение to и from (метод push, replace, go, listen)

И третий параметр функции обратного вызова этой функции-ловушки — передать следующую функцию обратного вызова (немного запутанно)

Но основное внимание следует уделить следующей функции обратного вызова.Мы знаем, что есть три способа, которыми следующий метод может принимать параметры:

true / false : продолжить выполнение/остановить выполнение
string : перейти к
пустому маршруту : остановить выполнение (или не писать дальше)

Итак, в следующем методе
нам нужно оценить тип параметров, и для каждого типа нам также нужно оценить режимы хеширования и истории:

myrouter.prototype.next = function () {
    
    
  //当next中没有参数
  if (arguments[0] == undefined) {
    
    
    //判断路由模式
    if (this.mode == 'hash') {
    
    
      let str = location.origin + '/#' + this.to
      //判断是什么方法
      if (this.methodType == 'replace') {
    
    
        location.replace(str)
      } else if (this.methodType == 'push') {
    
    
        location.href = str
      }
      h1.innerHTML = str;
    } else if (this.mode = 'history') {
    
    
      let str = location.origin + this.to
      if (this.methodType == 'push') {
    
    
        history.pushState({
    
    }, '', str)
      } else if (this.methodType == 'replace') {
    
    
        history.replaceState({
    
    }, '', str)
      }
      h1.innerHTML = str;
    }
    this.from = this.to
  }
  //当next中参数是某个路径
  else if (typeof arguments[0] == 'string') {
    
    
    //判断路由模式
    if (this.mode == 'hash') {
    
    
      location.hash = arguments[0]
    } else if (this.mode = 'history') {
    
    
      let str = location.origin + arguments[0]
      //判断是什么方法
      if (this.methodType == 'push') {
    
    
        history.pushState({
    
    }, '', str)
      } else if (this.methodType == 'replace') {
    
    
        history.replaceState({
    
    }, '', str)
      }

      h1.innerHTML = str;
    }
    this.to = arguments[0]
  }
  //当next里面传入其他参数(false)或者没有使用next方法
  else {
    
    
    if (this.mode == 'hash') {
    
    
      location.hash = this.from
    } else if (this.mode = 'history') {
    
    
      if (this.from) {
    
    
        let str = location.origin + this.from
        if (this.methodType == 'push') {
    
    
          history.pushState({
    
    }, '', str)
        } else if (this.methodType == 'replace') {
    
    
          history.replaceState({
    
    }, '', str)
        }
      } else {
    
    
        history.back()
      }
    }
    this.to = this.from
  }
}

Хорошо, после написания очередного метода нам нужно только судить, пусто ли this.saveBefore в методах push, replace, go и Monitoring.Если он не пуст, мы будем вызывать этот метод для перехвата маршрута.

5.роутер-ссылка

На самом деле, реализовать router-link должно быть относительно просто: мы напрямую сами инкапсулируем подкомпонент и внедряем его в main.js.

Основной из них является проблема передачи значений от родительского компонента к дочернему:
здесь я непосредственно беру код:

<template>
  <a @click="fn" class="routerlink">
    <slot></slot>
  </a>
</template>

<script>
import router from '../myrouter'

export default {
    
    
  name : 'routerLink',
  props : ['to','replace'],
  data(){
    
    
    return {
    
    
      title : 'router-link'
    }
  },
  created(){
    
    

  },
  methods : {
    
    
    fn(){
    
    
      if(this.to){
    
    
        this.$myrouter.push(this.to);
      }else if(this.replace){
    
    
        this.$myrouter.replace(this.replace)
      }
    }
  }
}
</script>

<style scoped>
  .routerlink{
    
    
    cursor:pointer
  }
</style>

6.просмотр маршрутизатора

Реализация router-view может потребовать большего внимания.Мы можем реализовать еще один подкомпонент, а затем заменить предыдущий тег h1 текущим подкомпонентом.

<template>
  <div class="routerview"></div>
</template>

Но в этом случае мы не можем реализовать вложенную маршрутизацию.

Итак, наше решение:

Соответственно сопоставьте путь и маршрутизаторы, затем запишите слой совпадающего значения, а затем визуализируйте представление маршрутизатора соответствующего слоя.

. . . . мд не могу понять.
эммм, это зависит от вашего собственного понимания. . . .

Для всех вышеперечисленных методов и принципов, потому что вставка кода может сопровождаться удалениями и могут возникнуть проблемы, поэтому в конце я вставлю исходный код, который написал непосредственно.

7. код myrouter.js

import Vue from 'vue'

var routerview = document.getElementsByClassName('routerview')
function myrouter (obj) {
    
    
  this.mode = obj.mode || 'hash'
  this.saveBefore = null
  this.saveAfter = null
  this.from = ''
  this.to = ''
  this.methodType = ''
  this.routes = obj.routes;
}

myrouter.prototype.push = function (data) {
    
    
  this.methodType = 'push'
  let orgin = location.origin;
  //判断路由模式
  if (this.mode == 'hash') {
    
    
    this.to = ifObj(data)
    this.from = location.hash.replace('#', '');
    //判断是否使用路由拦截
    if (this.saveAfter) {
    
    
      this.saveAfter(this.to, this.from);
    }
    if (this.saveBefore) {
    
    
      this.saveBefore(this.to, this.from, this.next)
    } else {
    
    
      location.href = orgin + '/#' + ifObj(data);
      h1.innerHTML = returnView(ifObj(data), this.routes)
    }
  }
  else if (this.mode = 'history') {
    
    
    this.to = ifObj(data)
    this.from = location.pathname;
    if (this.saveAfter) {
    
    
      this.saveAfter(this.to, this.from);
    }
    if (this.saveBefore) {
    
    
      this.saveBefore(this.to, this.from, this.next)
    } else {
    
    
      history.pushState({
    
    }, '', orgin + ifObj(data))
      routerview.innerHTML = ''
      routerview.appendChild(returnView(ifObj(data).replace('/', ''), this.routes))
    }
  }
}

myrouter.prototype.go = function (num) {
    
    
  this.methodType = 'go'
  history.go(num)
}

myrouter.prototype.replace = function (data) {
    
    
  this.methodType = 'replace'
  let orgin = location.origin;
  //判断路由模式
  if (this.mode == 'hash') {
    
    
    //判断是否使用路由拦截
    if (this.saveAfter) {
    
    
      this.saveAfter(this.to, this.from);
    }
    if (this.saveBefore) {
    
    
      this.to = ifObj(data)
      this.from = location.hash.replace('#', '')
      this.saveBefore(this.to, this.from, this.next)
    } else {
    
    
      location.replace(orgin + '/#' + ifObj(data));
      routerview.innerHTML = ''
      routerview.appendChild(ifObj(data).replace('/', ''))
    }
  } else if (this.mode == 'history') {
    
    
    if (this.saveAfter) {
    
    
      this.saveAfter(this.to, this.from);
    }
    if (this.saveBefore) {
    
    
      this.to = ifObj(data)
      this.from = location.pathname;
      this.saveBefore(this.to, this.from, this.next)
    } else {
    
    
      history.replaceState({
    
    }, '', orgin + ifObj(data))
      routerview.innerHTML = ''
      routerview.appendChild(ifObj(data).replace('/', ''))
    }
  }
}

//钩子的next回调函数
myrouter.prototype.next = function () {
    
    
  //当next中没有参数
  if (arguments[0] == undefined) {
    
    
    //判断路由模式
    if (this.mode == 'hash') {
    
    
      let str = location.origin + '/#' + this.to
      //判断是什么方法
      if (this.methodType == 'replace') {
    
    
        location.replace(str)
      } else if (this.methodType == 'push') {
    
    
        location.href = str
      }
      let arr = this.to.split('/');
      let path = '/' + arr[arr.length - 1]
      let com = (ifChild(this.to, this.routes))

      // let path = ('/' + location.hash.replace('#', '').split('/').pop());
      // let com = (ifChild(location.hash.replace('#', ''), this.routes))
      routerview[routerview.length - 1].innerHTML = ''
      routerview[routerview.length - 1].appendChild(returnView(path, com))
    } else if (this.mode = 'history') {
    
    
      let str = location.origin + this.to
      if (this.methodType == 'push') {
    
    
        history.pushState({
    
    }, '', str)
      } else if (this.methodType == 'replace') {
    
    
        history.replaceState({
    
    }, '', str)
      }
      routerview.innerHTML = ''
      routerview.appendChild(returnView(location.pathname, this.routes))
    }
    this.from = this.to
  }
  //当next中参数是某个路径
  else if (typeof arguments[0] == 'string') {
    
    
    //判断路由模式
    if (this.mode == 'hash') {
    
    
      location.hash = arguments[0]
    } else if (this.mode = 'history') {
    
    
      let str = location.origin + arguments[0]
      //判断是什么方法
      if (this.methodType == 'push') {
    
    
        history.pushState({
    
    }, '', str)
      } else if (this.methodType == 'replace') {
    
    
        history.replaceState({
    
    }, '', str)
      }
      routerview.innerHTML = ''
      routerview.appendChild(returnView(location.pathname, this.routes))
    }
    this.to = arguments[0]
  }
  //当next里面传入其他参数(false)或者没有使用next方法
  else {
    
    
    if (this.mode == 'hash') {
    
    
      location.hash = this.from
    } else if (this.mode = 'history') {
    
    
      if (this.from) {
    
    
        let str = location.origin + this.from
        if (this.methodType == 'push') {
    
    
          history.pushState({
    
    }, '', str)
        } else if (this.methodType == 'replace') {
    
    
          history.replaceState({
    
    }, '', str)
        }
      } else {
    
    
        history.back()
      }
    }
    this.to = this.from
  }
}

//前置钩子函数(主要用于保存回调函数)
myrouter.prototype.beforeEach = function (config) {
    
    
  this.saveBefore = config;
}

//后置钩子函数
myrouter.prototype.afterEach = function (config) {
    
    
  this.saveAfter = config
}

//挂在在window上的监听方法
myrouter.prototype.update = function () {
    
    
  let _this = this;
  //判断路由模式
  if (this.mode == 'hash') {
    
    
    //监听url哈希值的变化
    window.onhashchange = function () {
    
    
      //判断是否使用路由拦截
      if (_this.saveAfter) {
    
    
        _this.saveAfter(_this.to, _this.from);
      }
      let length = location.hash.replace('#', '').split('/').length - 1;
      if (routerview[length]) {
    
    
        routerview[length].remove()
      }
      if (_this.saveBefore) {
    
    
        _this.to = location.hash.replace('#', '')
        _this.saveBefore(_this.to, _this.from, _this.next)
      } else {
    
    
        routerview.innerHTML = ''
        routerview.appendChild(returnView(location.hash.replace('#/', ''), this.routes))
      }
    }
    window.onload = function () {
    
    
      if (location.hash.length == 0) {
    
    
        location.href = location.origin + '/#' + location.pathname
      }
      if (_this.saveAfter) {
    
    
        _this.saveAfter(_this.to, _this.from)
      }
      let arr = location.hash.replace('#', '').split('/');
      arr.shift()
      let too = ''
      arr.forEach(val => {
    
    
        if (_this.saveBefore) {
    
    
          too += ('/' + val)
          _this.to = too
          _this.saveBefore(_this.to, _this.from, _this.next)
        } else {
    
    
          routerview.innerHTML = ''
          routerview.appendChild(returnView(location.hash.replace('#/', ''), this.routes))
        }
      })
    }
  }
  else if (this.mode == 'history') {
    
    
    //监听history模式下前进后退的操作
    window.addEventListener('popstate', function () {
    
    
      _this.methodType = 'go'
      //判断是否使用路由拦截
      if (_this.saveAfter) {
    
    
        _this.saveAfter(_this.to, _this.from);
      }
      if (_this.saveBefore) {
    
    
        _this.to = location.pathname
        _this.saveBefore(_this.to, _this.from, _this.next)
      } else {
    
    
        routerview.innerHTML = ''
        routerview.appendChild(returnView(location.pathname, this, routes))
      }
    });
    window.onload = function () {
    
    
      if (location.hash.length != 0) {
    
    
        location.href = location.href.replace('/#', '')
      }
      if (_this.saveAfter) {
    
    
        _this.saveAfter(_this.to, _this.from);
      }
      if (_this.saveBefore) {
    
    
        _this.to = location.pathname
        _this.saveBefore(_this.to, _this.from, _this.next)
      } else {
    
    
        routerview.innerHTML = ''
        routerview.appendChild(returnView(location.pathname, this.routes))
      }
    }
  }
}



//判断参数类型
function ifObj (data) {
    
    
  if (typeof data == 'object') {
    
    
    return data.path
  } else {
    
    
    return data;
  }
}

//通过路径path返回dom实例的方法
function returnView (path, routes) {
    
    
  // debugger
  if (path && routes) {
    
    
    for (var i = 0; i < routes.length; i++) {
    
    
      if (routes[i].path == path) {
    
    
        if (typeof routes[i].component.template == 'string') {
    
    
          let div = document.createElement('div');
          div.innerHTML = routes[i].component.template
          return div
        } else {
    
    
          return toDom(routes[i].component)
        }
      }
    }
  }
  var div = document.createElement('div');
  div.innerHTML = '<h1>404</h1>'
  return div
}

function ifChild (path, routes) {
    
    
  let arr = path.replace('/', '').split('/');
  return returnChild(arr, routes);
}

function returnChild (arr, routes) {
    
    
  if (arr && routes) {
    
    
    if (arr.length == 1) {
    
    
      for (var i = 0; i < routes.length; i++) {
    
    
        if (arr[0] == routes[i].path.replace('/', '')) {
    
    
          return routes
        }
      }
    } else {
    
    
      for (var i = 0; i < routes.length; i++) {
    
    
        if (arr[0] == routes[i].path.replace('/', '')) {
    
    
          arr.shift();
          return returnChild(arr, routes[i].children)
        }
      }
    }
  }
}

//将vue组建转换成dom的方法
function toDom (view) {
    
    
  const Instance = new Vue({
    
    
    render (h) {
    
    
      return h(view);
    }
  });

  const component = Instance.$mount();
  return component.$el
}
export default myrouter

8. код main.js

import Vue from 'vue'
import App from './App.vue'
// import router from './router'
import myrouter from '../router/myrouter'
import view1 from '../router/view/view1.vue'
import view2 from '../router/view/view2.vue'
import child2 from '../router/view/child2.vue'

const a = {
    
     template: '<h1>a页面</h1>' }
const b = {
    
     template: '<h1>b页面</h1>' }

const child1 = {
    
     template: '<h1>child1页面</h1>' }

const routes = [
  {
    
    
    path: '/a', component: view2, children: [
      {
    
     path: '/achild1', component: child1 },
      {
    
     path: '/achild2', component: child2 }
    ]
  },
  {
    
     path: '/b', component: b },
  {
    
     path: '/c', component: view1 }
]

let router = new myrouter({
    
    
  mode: 'hash',
  routes: routes
})

router.beforeEach((to, from, next) => {
    
    
  // console.log(to, from);
  if (to == '/a') {
    
    
    router.next()
  } else if (to == '/b') {
    
    
    router.next()
  } else if (to == '/c') {
    
    
    router.next()
  } else if (to == '/d') {
    
    
    router.next()
  } else {
    
    
    router.next()
  }
})


router.afterEach((to, from) => {
    
    
  if (to == '/a' && from == '/b') {
    
    
    console.log('from b to a');
  }
})


Vue.prototype.$myrouter = router;
new Vue({
    
    
  el: '#app',
  render: h => h(App),
  // router,
})


おすすめ

転載: blog.csdn.net/weixin_46726346/article/details/119606892