实现简易路由前置知识点

路由模式

route分为两种模式

hash模式

  • 通过锚点(也就是hash)进行跳转
  • #
  • URL会更改
  • 浏览器可以前进后退,当时浏览器不会刷新
  • 不合服务端交流
  • 路径名保存在 location.hash

    history模式

  • 无锚点 无hash

  • #
  • 是一个正常URL
  • 会和服务端交流
  • 路径名保存在 location.pathname

    路由信息

    $router

    路由实例对象
    可读可写
    路由信息保存再History下的对象current中

    $route

    只读,路由信息对象,数据是从$router.history.current下的数据的数据

    路由组件

    router-link

    组件支持用户在具有路由功能的应用中

    router-view

    渲染路径匹配到的视图组件

    路由的切换//页面的跳转

    hash模式

    页面的跳转也就是hash的改变
    可以监听 hashchange 事件 该事件会再hash改变时触发
    当一次打开页面视图组件啥都不显示
    此时需要使用 DomcontentLoaded 事件(该事件会在dom内容加载完毕时触发)将其hash值修改为首页的hash值
    原理
    就是监听 hashchange 事件当页面改变通过获取hash值来找到对应的组件来渲染页面
    注意
    当为a元素时,直接添加将路径添加到href上就行啦
    当不是a元素时,通过监听单击事件来改变hash值

    history模式

    所有的元素都是通过单击事件来触发页面的跳转
    触发单击事件时使用 history.pushState() 来实现页面的跳转
    history的路径信息是存储在 location.pathname
    但是页面的前进关于回退会失效
    可是使用 popstate 事件(当浏览器前进与后退时触发)将 location.pathname 中的值修改
    当一次打开页面视图组件啥都不显示
    此时需要使用 DomcontentLoaded 事件(该事件会在dom内容加载完毕时触发)将其 location.pathname 值修改为首页的hash值
    原理
    同过元素的单击事件时将其路由路径传入,通过 history.pushState() 来实现的页面的跳转,在通过 popstate 来实现也面的前进与后退,第一次代开页面 通过 DomcontentLoaded 来渲染首页

    use方法

    1. Vue.use(VueRouter)
    如上面代码

    use 方法是干嘛的

    use方法就是桥梁,开发一个在vue上使用的工具或插件它必须要使用vue的构造函数或vue类,通过use将vue传递给这个工具或插件,在被使用的类上必须要用一个install方法,只要被vue.use(类)就会自然而然的去调用类中的install方法

利用代码实现简易路由

app.vue

  1. <template>
  2. <div id="app">
  3. <div class="nav-box">
  4. <div class="logo">zxt</div>
  5. <div class="nav-list">
  6. <router-link tag="div" to="/">首页</router-link>
  7. <router-link tag="div" to="/learn">课程学习</router-link>
  8. <router-link tag="div" to="/show">学员展示</router-link>
  9. <router-link tag="div" to="/about">关于</router-link>
  10. <router-link tag="div" to="/community">社区</router-link>
  11. </div>
  12. </div>
  13. <div class="content">
  14. <router-view></router-view>
  15. </div>
  16. </div>
  17. </template>
  18. <script>
  19. export default {
  20. name: 'App',
  21. components: {
  22. },
  23. }
  24. </script>
  25. <style>
  26. #app {
  27. font-family: Avenir, Helvetica, Arial, sans-serif;
  28. -webkit-font-smoothing: antialiased;
  29. -moz-osx-font-smoothing: grayscale;
  30. text-align: center;
  31. }
  32. .nav-box{
  33. width: 100%;
  34. height: 60px;
  35. display: flex;
  36. justify-content: space-around;
  37. align-items: center;
  38. background: rgba(0, 0, 200, .5);
  39. }
  40. .logo{
  41. color: #fff;
  42. font-size: 30px;
  43. font-weight: bold;
  44. }
  45. .nav-list{
  46. display: flex;
  47. width: 60%;
  48. justify-content: space-between;
  49. align-items: center;
  50. }
  51. .nav-list a{
  52. text-decoration: none;
  53. color: #000;
  54. font-size: 18px;
  55. }
  56. .nav-list a.router-link-exact-active{
  57. font-weight: bold;
  58. color: #fff;
  59. }
  60. .content{
  61. margin-top: 40px;
  62. font-size: 20px;
  63. }
  64. </style>

main.js

  1. import Vue from 'vue'
  2. import App from './App.vue'
  3. import router from './router'
  4. new Vue({
  5. // 挂载路由
  6. router,
  7. render: h => h(App),
  8. }).$mount('#app')

route.js

  1. import Vue from 'vue'
  2. import VueRouter from './vue-router'
  3. // 桥梁
  4. // 想要别vue使用的工具或插件必须通过use方法
  5. // use方法会调用要被vue使用的工具或插件上的instal方法,同时会将vue传递过去
  6. // 毕竟要被vue使用的工具或插件要用的vue
  7. Vue.use(VueRouter)
  8. Vue.config.productionTip = false
  9. // 定义路由
  10. // 路由表 将其当做router的配置参数
  11. const routes = [
  12. {
  13. path: '/',
  14. // 异步加载组件,当标签被点击才会加载组件
  15. component: () => import('./components/views/Home.vue')
  16. },
  17. {
  18. path: '/learn',
  19. component: () => import('./components/views/Learn.vue')
  20. },
  21. {
  22. path: '/show',
  23. component: () => import('./components/views/Show.vue')
  24. },
  25. {
  26. path: '/about',
  27. component: () => import('./components/views/About.vue')
  28. },
  29. {
  30. path: '/community',
  31. component: () => import('./components/views/community.vue')
  32. },
  33. ]
  34. // 创建实例
  35. export default new VueRouter({
  36. mode: 'history',
  37. routes,
  38. })

vue-router 的代码
idnex.js

  1. import History from './history'
  2. import install from './install'
  3. class VueRouter {
  4. constructor(option){
  5. //接受路由表
  6. this.routes = option.routes;
  7. // 将其路由表格式成容易操作的对象
  8. // key : 路径
  9. // value: 子组件
  10. this.routeMap = this.createRouterMap(this.routes) || []
  11. this.history = new History()
  12. this.mode = option.mode || 'hash'
  13. this.init()
  14. }
  15. /**
  16. * 将其路由表格式化为对象
  17. * 格式为: {路径:子组件 ...}
  18. * @param { Array } routes 路由表
  19. * @returns 返回一个对象
  20. */
  21. createRouterMap(routes){
  22. let routeMap = {}
  23. for (let i = 0; i < routes.length; i++) {
  24. let routesItem = routes[i]
  25. routeMap[routesItem.path] = routesItem.component
  26. }
  27. return routeMap
  28. }
  29. init(){
  30. if(this.mode === 'hash'){
  31. location.hash ? '' : location.hash = '/'
  32. // 第一次页面加载的时候视图是空的
  33. document.addEventListener('DOMContentLoaded', () => {
  34. this.history.current.path = location.hash.slice(1)
  35. })
  36. // 监听hash是否改变
  37. window.addEventListener('hashchange', () => {
  38. this.history.current.path = location.hash.slice(1)
  39. })
  40. }else{
  41. // 第一次页面加载的时候视图是空的
  42. document.addEventListener('DOMContentLoaded', () => {
  43. this.history.current.path = location.pathname
  44. })
  45. // 修复浏览器无法前进与后退
  46. window.addEventListener('popstate',() =>{
  47. this.history.current.path = location.pathname
  48. })
  49. }
  50. }
  51. }
  52. // use 会默认调用函数中的install方法
  53. VueRouter.install = install;
  54. export default VueRouter

history.js

  1. /**
  2. *
  3. * $route中存放当前路由信息来自$.router.history下的current
  4. * 同时$router上也存放着
  5. */
  6. class History {
  7. constructor() {
  8. this.current = {
  9. path:null,
  10. }
  11. }
  12. }
  13. export default History

install.js

  1. import link from './link';
  2. import view from './view'
  3. export default function install(Vue) {
  4. Vue.mixin({
  5. beforeCreate(){
  6. // console.log(this.$options.router)
  7. /**
  8. * 找到根实例上的router
  9. */
  10. if(this.$options.router){
  11. this._router = this.$options.router
  12. // this._route = this._router.history.current;
  13. // 一旦属性值改变啦页面就会进行重新渲染
  14. Vue.util.defineReactive(this,'_route',this._router.history.current)
  15. }
  16. }
  17. })
  18. /**
  19. * Object.defineProperty(Vue.prototype, '$router', {}
  20. * 中的this指向 由于有属性描述get会将this传入,那吗他的this 就指向Vue的实例
  21. */
  22. Object.defineProperty(Vue.prototype, '$router', {
  23. get() {
  24. return this.$root._router;
  25. }
  26. })
  27. Object.defineProperty(Vue.prototype,'$route',{
  28. get(){
  29. return this.$root._route;
  30. }
  31. })
  32. // 会显示找不到router-link 与 router-view 两个组件
  33. Vue.component('router-link', link )
  34. Vue.component('router-view', view )
  35. }

link.js

  1. /**
  2. * 等效于
  3. * Vue.component('router-link',{})
  4. */
  5. export default {
  6. // 接受元素传递过来的hash路径,以及元素名字
  7. props: {
  8. to:{
  9. type: String,
  10. required: true,
  11. },
  12. tag:{
  13. type:String,
  14. default:'a'
  15. }
  16. },
  17. methods:{
  18. handleClick(){
  19. const mode = this.mode;
  20. if(mode === 'hash'){
  21. location.hash = this.to;
  22. }else{
  23. history.pushState(null,null,this.to)
  24. this.$router.history.current.path = this.to;
  25. }
  26. }
  27. },
  28. // 渲染
  29. render(h) {
  30. const to = this.to;
  31. const tag = this.tag;
  32. const data = {}
  33. const mode = this.mode;
  34. // 当为其a元素时 使用的hash来修改的路由,
  35. // 当不是a元素时,通过history模式(就是当触发单击事件时,将其路由修改)
  36. if(tag === 'a' && mode === 'hash'){
  37. const href = '#' + to
  38. data.attrs = { href }
  39. }else{
  40. data.on = { click: this.handleClick }
  41. }
  42. // 渲染元素, 特性, 以及需要显示的信息
  43. return h(this.tag, data, this.$slots.default)
  44. }
  45. }

view.js

  1. /**
  2. * 函数是组件
  3. * 通过父级找到存放路由信息的对象
  4. * 通过路径找到需要渲染的组件
  5. * 将其渲染
  6. */
  7. export default {
  8. functional: true,
  9. render(h, {parent}) {
  10. let routeMap = parent.$router.routeMap;
  11. let path = parent.$route.path
  12. return h(routeMap[path])
  13. }
  14. }

思维导图

做的不怎嘛滴
vue-router.xmind
vue-router.xmind

完整代码的压缩包

router原理.rar