Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

91.VueRouter原理实现 #91

Open
webVueBlog opened this issue Feb 19, 2023 · 0 comments
Open

91.VueRouter原理实现 #91

webVueBlog opened this issue Feb 19, 2023 · 0 comments

Comments

@webVueBlog
Copy link
Member

vue router 原理实现

基本使用

import Vue from "vue";
import VueRouter from "vue-router";
import Index from "./views/Index.vue";

Vue.use(VueRouter);

const routes = [
    {
        path: "/",
        name: "Index",
        component: Index,
    },
    {
        path: "/blog",
        name: "Blog",
        // route level code-splitting
        // lazy-loaded
        component: import(/* webpackChunkName: "blog" */'./views/Blog.vue'),
    }
];

const router = new VueRouter({
    routes
});

new Vue({
    router,
    el: "#app"
});

// Index.vue
<template>
    <router-view></router-view>
</template>

router 会给组件注入 $route 和 $router 两个属性。

动态路由匹配

const routes = [
    {
        path: "/blog/:id",
        name: "blog",
        // 路由参数 id 会传入组件中
        props: true,
        component: Blog
    }
]

嵌套路由

const routes = [
    {
        path: "/",
        component: Layout,
        children: [
            {
                path:"",
                name:"index",
                component: Index
            }
        ]
    }
]


<script>
export default {
    name: "blog",
    props: ["id"]
}

</script>

编程式导航

// 字符串
router.push('home')

// 对象
router.push({ path: 'home' })

// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})

// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})

Hash 模式和 History 模式的区别

  • Hash 模式是基于锚点,以及 onhashchange 事件
  • History 模式是基于 html5 中的 history API
    • history.pushState() IE10 以后才支持
    • history.replaceState()

History 模式的使用

History 需要服务器的支持 history 模式下, 如果浏览器刷新,会向服务器发起请求,如果服务器没有配置的话,就可能返回 404。

History 模式 - nodejs

const path = require("path")
const history = require("connect-history-api-fallback")
const express = require("express");

const app = express();

app.use(history)
app.use(express.static(path.join(__dirname, "./public"));

app.listen(3000, () => {
    console.log("服务器开启,端口: 3000")
})

History 模式 - nginx

start nginx
nginx -s reload

nginx -s stop


http {

    location / {
        root html;
        index index.html index.htm
        try_files $uri $uri/ /index.html
    }
}

VueRouter History 模式下的实现原理

VueRouter 类图

image

let _Vue = null;

export default class VueRouter {
  static install(Vue) {
    // 1. 判断当前插件是否已经被安装
    if (VueRouter.install.installed) {
      return;
    }
    VueRouter.install.installed = true;

    // 2,把 Vue 构造函数记录到全局变量
    _Vue = Vue;
    // 3. 把创建 Vue 实例时传入的 routes 对象注入到 Vue 实例上
    // 混入
    _Vue.mixin({
      beforeCreate() {
        if (this.$options.router) {
          _Vue.prototype.$router = this.$options.router;
          this.$options.router.init();
        }
      },
    });
  }

  constructor(options) {
    this.options = options;
    this.routeMap = {};
    // 创建响应式对象
    this.data = _Vue.observable({
      current: "/",
    });
  }

  init() {
    this.createRouteMap();
    this.initComponents(_Vue);
    this.initEvent();
  }

  createRouteMap() {
    // 遍历所有的路由规则, 把路由规则解析成键值对的形式 存储到 routeMap 中
    this.options.routes.forEach((route) => {
      this.routeMap[router.path] = router.component;
    });
  }

  initComponents(Vue) {
    const self = this;
    Vue.component("router-link", {
      props: {
        to: String,
      },
      // runtime 版本的 vue 不支持 template 模版
      //   template: '<a :href="to"><slot></slot></a>',
      render(h) {
        return h(
          "a",
          {
            attrs: {
              href: this.to,
            },
            on: {
              click: this.clickHandler,
            },
          },
          [this.$slote.default]
        );
      },
      methods: {
        clickHandler(e) {
          history.pushState({}, "", this.to);
          // 修改后会触发组件的重新渲染
          this.$router.data.current = this.to;
          e.prevenDefault();
        },
      },
    });

    Vue.component("router-view", {
      render(h) {
        // 当前路由对应的组件
        const component = self.routeMap[self.data.current];
        return h(component);
      },
    });
  }

  initEvent() {
    // 前进 后退,历史记录被激活时,会触发 popstate 事件
    window.addEventListener("popstate", () => {
      this.data.current = window.location.pathname;
    });
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant