实现简易路由前置知识点

路由模式

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