路由

  1. 1.当给vue 注入router的时候,router会给vue注入$route 和$router两个属性
  2. $route是一个路由规则,存储了当前的路由的路劲(如有参数,也会包含有的)
  3. $router就是VueRouter的一个实例对象,它包含了当前实例的一些信息。可以调用一些方法,比如push
  4. 2.我们给路由注册组件的时候不用整个组件全部加载,可以使用路由懒加载来实现,如果我们用户不访问,这个组件不会创建的
  5. component:()=>import(/*webpackChunkName:"detail"*/ '../views/Detail.vue');
  6. 3.动态的路由我们在路由规则那里配置路由为true的时候可以获取路由的相关的参数。
  7. const routes =[{
  8. path:'/detail/:id',
  9. name:"Detail",
  10. //开启props,会把URL中的参数传递给组件
  11. //在组件通过props 来接受URL的参数·
  12. props:true,
  13. component:()=>import(/*webpackChunkName:"detail"*/ '../views/Detail.vue');
  14. }]
  15. //=================================================================================
  16. <template>
  17. //
  18. <div>{{$route.params.id}}</div>
  19. <div>{{id}}</div> // props:['id']
  20. </template>
  21. 4.路由跳转 this.$router.push('/') 或者this.$router.push({name:'Home'}) //必须在路由规则那里配置name属性。 this.$router.replace('/') //跟push 功能一样,不会记录历史的 左上角的后退不行的,但是push是记录历史的

Hash 和History区别

  1. 不管那种模式,前端路由都是客户端路由的实现方式,也就是当路径发生改变时,不会向服务器发送请求,是利用js监听路径的变化。然后根据不同的地址渲染不同的内容,如果需要服务器内容,会发送ajax请求来获取。

表现形式的区别

  1. hash 模式
  2. https://music.163.com/#/discover/toplist 地址中会存在 # 号
  3. history 模式
  4. https://music.163.com/discover/toplist 地址中没有# 类似于普通的地址,但是需要服务端配置支持

原理的区别

  1. hash 模式是基于瞄点,以及onhashchange事件
  2. history 模式是基于HTML5中的History API
  3. history.pushState() IE10以后才支持
  4. history.replaceState()

History 模式的使用

  1. History 需要服务器的支持
  2. 单页面应用中,如果刷新页面,会像服务器发送请求,而服务器不存在这样的地址就会返回找不到该页面从而出现404
  3. 在服务端应该除了静态资源外都返回单页应用的index.html

node 环境下支持history

在node环境下,启用对history模式的支持可以通过connect-history-api-fallback 这个中间件来完成

  1. // 导入处理 history 模式的模块
  2. const history = require('connect-history-api-fallback')
  3. // 导入 express
  4. const express = require('express')
  5. const app = express()
  6. // 注册处理 history 模式的中间件
  7. app.use(history())

Nginx 下支持history

  • 从官网下载 nginx 的压缩包
  • 把压缩包解压到 c 盘根目录,c:\nginx-1.18.0 文件夹
  • 修改 conf\nginx.conf 文件

运行nginx服务器基本指令

  1. 启动
  2. start nginx
  3. 重启
  4. nginx -s reload
  5. 停止
  6. nginx -s stop
  • 修改 conf\nginx.conf 文件 ```javascript location / { root html; index index.html index.htm;

    新添加内容

    尝试读取$uri(当前请求的路径),如果读取不到读取$uri/这个文件夹下的首页

    如果都获取不到返回根目录中的 index.html

    try_files $uri $uri/ /index.html; }
  1. <a name="Sbk3f"></a>
  2. ## Vue Router 实现原理
  3. ```javascript
  4. Vue Router 有两种模式,一种hash模式,一种history模式。
  5. 需要使用 插件 混入 vue.observable() 插槽 render函数 运行时和完整的vue
  6. 原理:
  7. Hash模式 是把URL中#后面的内容作为路劲地址,通过localtion.url来切换路由的地址,如果只改变URL中#号后面的地址,浏览器不会向服务器请求这个地址,但是会把这个地址记录在访问历史中,当hash改变了,会监听hashchange这个事件,我们会记录路由地址并找到对应的组件进行渲染
  8. history 路劲就是普通的url,通过history.pushState()方法改变地址栏,这个方法仅仅改变地址栏,并把当前地址记录在访问地址中,并不会跳转到指定的路劲,也就是浏览器不会向服务器发送请求,通过监听popstate 事件,可以监听到浏览器历史操作的变化,当地址改变的时候,根据当前路由地址找到对应的组件进行重新渲染
  9. !vue.use(Object || Funtinon) 如果传入一个对象 会调用一个install的方法

实现思路

2021042320430769.png

vue-router(history模式) - 图2

VueRouter-install 方法实现

要实现install方法,首先先分析一下该方法要做的事情:

  1. 判断当前插件是否已经被安装
  2. 把Vue构造函数记录到全局变量
  3. 把创建Vue实例时候传入的router对象注入到所有的Vue实例上

    添加 VueRouter 的constructor

    添加 VueRouter 的constructorVueRouter 的构造函数要初始化三个属性,分别是: options、data、routeMap。

options 是路由的构造配置对象
data 应该是一个响应式的对象,其中有一个属性 current 用来记录当前我们的路由地址,这里我们该如何才能创建一个响应式的对象呢?可以使用Vue的observable方法
routeMap 中记录了 options里的rules,rules解析出来 会以键值对的形式存在 routeMap中 ,key 就是路由地址,value 就是路由组件

createRouterMap

接下来我们来实现VueRouter类中 createRouterMap 这个方法,它的作用就是把 options 中rules 路由规则解析出来以键值对的形式存储在routeMap上

initComponents

下一步,来创建initComponents 方法,这个方法里我们要创建两个组件。分别是:RouterLink 和 RouterView
routerlink中添加点击事件,修改地址为了能够让链接成功完成跳转展示组件,我们需要对routerlink中的a标签添加点击事件

并且要在点击的时候,把最新的path更新到router实例的current上.

我们借助于history的pushState方法 该方法会修改浏览器地址栏中的地址,但不会向服务器发起请求,并且还可以将新地址记录在历史中

initEvent

现在功能基本上已经差不多了,但是还存在一个小问题,就是当我们点击浏览器的前进或者后退按钮的时候,组件不能实现切换展示,主要思路就是通过添加popstate监听地址变化

  1. let _Vue;
  2. export default class VueRouter {
  3. static install(Vue) {
  4. // 1. 判断当前插件是否已经被安装
  5. if (VueRouter.install.installed) return
  6. VueRouter.install.installed = true
  7. // 2. 把Vue构造函数记录到全局变量
  8. _Vue = Vue
  9. // 3. 把创建Vue实例时候传入的router对象注入到所有的Vue实例上
  10. // 利用混入让所有的vue实例加载router
  11. _Vue.mixin({
  12. beforeCreate() {
  13. // this.$options.name用来获取vue实例 data以外的属性
  14. // new Vue( { router } )
  15. if (this.$options.router) {
  16. _Vue.prototype.$router = this.$options.router
  17. console.log(this.$options.router.init);
  18. this.$options.router.init()
  19. }
  20. }
  21. })
  22. }
  23. constructor(options) {
  24. this.options = options
  25. this.routeMap = {}
  26. this.data = _Vue.observable({
  27. current: '/'
  28. })
  29. }
  30. createRouterMap() {
  31. this.options.routes.forEach(route => this.routeMap[route.path] = route.component)
  32. }
  33. initComponents(Vue) {
  34. // 创建RouterLink组件
  35. Vue.component('router-link', {
  36. props: {
  37. 'to': { type: String }
  38. },
  39. render(h) {
  40. return h('a', {
  41. attrs: { href: this.to },
  42. on: { click: this.clickHandle }
  43. }, [this.$slots.default])
  44. },
  45. methods: {
  46. clickHandle(e) {
  47. history.pushState({}, "", this.to)
  48. // 把点击的链接地址 更新到 current 上
  49. this.$router.data.current = this.to
  50. e.preventDefault()
  51. }
  52. }
  53. // template: `<a :href="to"><slot></slot></a>`
  54. })
  55. let self = this
  56. Vue.component('router-view', {
  57. render(h) {
  58. // routeMap以key value形式记录了path和component
  59. // data.current 记录了当前页面的path
  60. return h(self.routeMap[self.data.current])
  61. }
  62. })
  63. }
  64. init() {
  65. this.createRouterMap()
  66. this.initComponents(_Vue)
  67. this.initEvent()
  68. }
  69. initEvent() {
  70. //
  71. window.addEventListener("popstate", () => {
  72. this.data.current = window.location.pathname
  73. })
  74. }
  75. }