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

vue-router #38

Open
TieMuZhen opened this issue Nov 24, 2021 · 0 comments
Open

vue-router #38

TieMuZhen opened this issue Nov 24, 2021 · 0 comments
Labels

Comments

@TieMuZhen
Copy link
Owner

TieMuZhen commented Nov 24, 2021

一、vue-router是什么

路由就是SPA(单页应用)的路径管理器。为啥不能用a标签,这是因为用Vue做的都是单页应用(当你的项目准备打包时,运行npm run build时,就会生成dist文件夹,这里面只有静态资源和一个index.html页面),所以a标签是不起作用的,必须使用vue-router来进行管理。

二、vue-router实现原理

SPA(single page application):单一页面应用程序,只有一个完整的页面;它在加载页面时,不会加载整个页面,而是只更新某个指定的容器中内容。
vue-router在实现单页面前端路由时,提供了两种方式:Hash模式和History模式。

1、Hash模式:

vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。 hash(#)是URL 的锚点,代表的是网页中的一个位置,单单改变#后的部分,浏览器只会滚动到相应位置,不会重新加载网页,也就是说hash 出现在 URL 中,但不会被包含在 http 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面;同时每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置。hash 模式的原理是onhashchange事件(监测hash值变化),可以在window 对象上监听这个事件。

2、History模式:

由于hash模式会在url中自带#,如果不想要很丑的#,我们可以用路由的 history 模式,只需要在配置路由规则时,加入"mode: 'history'",这种模式充分利用了html5 中新增的pushState()replaceState()方法。这两个方法应用于浏览器记录栈,在当前已有的backforwardgo基础之上,这五个提供了对历史记录修改的功能。只是当这五个方法执行修改时,虽然改变了当前的 URL ,但浏览器不会立即向后端发送请求。

当我们用 history 的路由时,必然要能监听到路由的变化才行。全局有个popstate事件,别看这个事件名称中有个 state 关键词,但pushStatereplaceState被调用时,是不会触发popstate事件的,只有上面列举的backforwardgo3 个方法会触发。

location.hreflocation.replace切换时要向服务器发送请求,而pushStatereplace仅修改url,除非主动发起请求。

//main.js文件中
const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id,比较好看!
不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了。
所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。

 export const routes = [ 
  {path: "/", name: "homeLink", component:Home}
  {path: "/register", name: "registerLink", component: Register},
  {path: "/login", name: "loginLink", component: Login},
  {path: "*", redirect: "/"}]

此处就设置如果URL输入错误或者是URL 匹配不到任何静态资源,就自动跳到到Home页面

3、Abstract模式

abstract是vue路由中的第三种模式,本身是用来在不支持浏览器API的环境中,充当fallback,而不论是hash还是history模式都会对浏览器上的url产生作用,本文要实现的功能就是在已存在的路由页面中内嵌其他的路由页面,而保持在浏览器当中依旧显示当前页面的路由path,这就利用到了abstract这种与浏览器分离的路由模式。Weex就是利用的abstract模式

4、使用路由模块来实现页面跳转的方式

  • 方式1:直接修改地址栏
  • 方式2:this.$router.push(‘路由地址’)
  • 方式3:<router-link to="路由地址"></router-link>

三、vue-router使用方式

1、下载npm i vue-router -S
2、在main.js中引入import VueRouter from 'vue-router';
3、安装插件Vue.use(VueRouter);
4、创建路由对象并配置路由规则let router = new VueRouter({routes:[{path:'/home',component:Home}]});
5、将其路由对象传递给Vue的实例,options中加入router:router
6、在app.vue中留坑<router-view></router-view>

具体实现请看如下代码:

//main.js文件中引入
import Vue from 'vue';
import VueRouter from 'vue-router';
//主体
import App from './components/app.vue';
import Home from './components/home.vue'
//安装插件
Vue.use(VueRouter); //挂载属性
//创建路由对象并配置路由规则
let router = new VueRouter({
    routes: [
        //一个个对象
        { path: '/home', component: Home }
    ]
});
//new Vue 启动
new Vue({
    el: '#app',
    //让vue知道我们的路由规则
    router: router, //可以简写router
    render: c => c(App),
})

最后记得在在app.vue中“留坑”

//app.vue中
<template>
    <div>
        <!-- 留坑,非常重要 -->
        <router-view></router-view>
    </div>
</template>
<script>
    export default {
        data(){
            return {}
        }
    }
</script>

四、 vue-router参数传递

声明式的导航<router-link :to="...">和编程式的导航router.push(...)都可以传参,本文主要介绍前者的传参方法,同样的规则也适用于编程式的导航。

1、用name传递参数

在路由文件src/router/index.js里配置name属性

routes: [
    {
      path: '/',
      name: 'Hello',
      component: Hello
    }
]

模板里(src/App.vue)用$route.name来接收 比如:<p>{{ $route.name}}</p>

2、通过<router-link>标签中的to传参

这种传参方法的基本语法:

<router-link :to="{name:xxx,params:{key:value}}">valueString</router-link>

比如先在src/App.vue文件中

<router-link :to="{name:'hi1',params:{username:'jspang',id:'555'}}">Hi页面1</router-link>

然后把src/router/index.js文件里给hi1配置的路由起个name,就叫hi1.

{path:'/hi1',name:'hi1',component:Hi1}

最后在模板里(src/components/Hi1.vue)用$route.params.username进行接收.

{{$route.params.username}}-{{$route.params.id}}

3、利用url传递参数----在配置文件里以冒号的形式设置参数。

我们在/src/router/index.js文件里配置路由

{
    path:'/params/:newsId/:newsTitle',
    component:Params
}

我们需要传递参数是新闻ID(newsId)和新闻标题(newsTitle).所以我们在路由配置文件里制定了这两个值。
src/components目录下建立我们params.vue组件,也可以说是页面。我们在页面里输出了url传递的的新闻ID和新闻标题。

<template>
    <div>
        <h2>{{ msg }}</h2>
        <p>新闻ID:{{ $route.params.newsId}}</p>
        <p>新闻标题:{{ $route.params.newsTitle}}</p>
    </div>
</template>
<script>
export default {
  name: 'params',
  data () {
    return {
      msg: 'params page'
    }
  }
}
</script>

在App.vue文件里加入我们的<router-view>标签。这时候我们可以直接利用url传值了

<router-link to="/params/198/jspang website is very good">params</router-link>

4、使用path来匹配路由,然后通过query来传递参数

<router-link :to="{ name:'Query',query: { queryId:  status }}" >
     router-link跳转Query
</router-link>

对应路由配置:

   {
     path: '/query',
     name: 'Query',
     component: Query
   }

于是我们可以获取参数:

this.$route.query.queryId

五、vue-router配置子路由(二级路由)

实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL中各段动态路径也按某种结构对应嵌套的各层组件,例如:
如何实现H1页面和H2页面嵌套在主页中 ?

1、首先用<router-link>标签增加了两个新的导航链接

<router-link :to="{name:'HelloWorld'}">主页</router-link>
<router-link :to="{name:'H1'}">H1页面</router-link>
<router-link :to="{name:'H2'}">H2页面</router-link>

2、在HelloWorld.vue加入<router-view>标签,给子模板提供插入位置

 <template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <router-view></router-view>
  </div>
</template>

3、在components目录下新建两个组件模板H1.vueH2.vue
两者内容类似,以下是H1.vue页面内容:

 <template>
  <div class="hello">
    <h1>{{ msg }}</h1>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        msg: 'I am H1 page,Welcome to H1'
      }
    }
  }
</script>

4、修改router/index.js代码,子路由的写法是在原有的路由配置下加入children字段。

   routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld,
      children: [{path: '/h1', name: 'H1', component: H1},//子路由的<router-view>必须在HelloWorld.vue中出现
        {path: '/h2', name: 'H2', component: H2}
      ]
    }
  ]

六、单页面多路由区域操作

在一个页面里我们有两个以上<router-view>区域,我们通过配置路由的js文件,来操作这些区域的内容

1、App.vue文件,在<router-view>下面新写了两行<router-view>标签,并加入了些CSS样式

<template>
  <div id="app">
    <img src="./assets/logo.png">
       <router-link :to="{name:'HelloWorld'}"><h1>H1</h1></router-link>
       <router-link :to="{name:'H1'}"><h1>H2</h1></router-link>
    <router-view></router-view>
    <router-view name="left" style="float:left;width:50%;background-color:#ccc;height:300px;"/>
    <router-view name="right" style="float:right;width:50%;background-color:yellowgreen;height:300px;"/>
  </div>
</template>

2、需要在路由里配置这三个区域,配置主要是在components字段里进行

export default new Router({
    routes: [
      {
        path: '/',
        name: 'HelloWorld',
        components: {default: HelloWorld,
          left:H1,//显示H1组件内容'I am H1 page,Welcome to H1'
          right:H2//显示H2组件内容'I am H2 page,Welcome to H2'
        }
      },
      {
        path: '/h1',
        name: 'H1',
        components: {default: HelloWorld,
          left:H2,//显示H2组件内容
          right:H1//显示H1组件内容
        }
      }
    ]
  })

上边的代码我们编写了两个路径,一个是默认的‘/’,另一个是‘/h1’.在两个路径下的components里面,我们对三个区域都定义了显示内容。

七、$route 和 $router 的区别

我们先将这两者console.log打印出来:

$route 是“路由信息对象”,包括 path,params,hash,query,fullPath,matched,name 等路由信息参数。

$route.path
字符串,对应当前路由的路径,总是解析为绝对路径,如 "/order"。

$route.params
一个 key/value 对象,包含了 动态片段 和 全匹配片段,
如果没有路由参数,就是一个空对象。

$route.query
一个 key/value 对象,表示 URL 查询参数。
例如,对于路径 /foo?user=1,则有 $route.query.user为1,
如果没有查询参数,则是个空对象。

$route.hash
当前路由的 hash 值 (不带 #) ,如果没有 hash 值,则为空字符串。

$route.fullPath
完成解析后的 URL,包含查询参数和 hash 的完整路径。

$route.matched
数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。

$route.name当前路径名字

$router 是“路由实例”对象,即使用 new VueRouter创建的实例,包括了路由的跳转方法,钩子函数等。

$router常见跳转方法:

<button @click="goToMenu" class="btn btn-success">Let's order!</button>
.....
<script>
  export default{
    methods:{
      goToMenu(){
        this.$router.go(-1)//跳转到上一次浏览的页面
        this.$router.replace('/menu')//指定跳转的地址
        this.$router.replace({name:'menuLink'})//指定跳转路由的名字下
        this.$router.push('/menu')//通过push进行跳转
        this.$router.push({name:'menuLink'})//通过push进行跳转路由的名字下
      }
    }
  }
</script>

$router.push$router.replace的区别:

  • 使用push方法的跳转会向 history 栈添加一个新的记录,当我们点击浏览器的返回按钮时可以看到之前的页面。
  • 使用replace方法不会向 history 添加新记录,而是替换掉当前的 history 记录,即当replace跳转到的网页后,‘后退’按钮不能查看之前的页面。

八、如何设置404页面

用户会经常输错页面,当用户输错页面时,我们希望给他一个友好的提示页面,这个页面就是我们常说的404页面。vue-router也为我们提供了这样的机制。

1、设置我们的路由配置文件(/src/router/index.js)

{
   path:'*',
   component:Error
}

这里的path:'*'就是输入地址不匹配时,自动显示出Error.vue的文件内容

2、在/src/components/文件夹下新建一个Error.vue的文件。简单输入一些有关错误页面的内容。

<template>
    <div>
        <h2>{{ msg }}</h2>
    </div>
</template>
<script>
export default {
  data () {
    return {
      msg: 'Error:404'
    }
  }
}
</script>

此时我们随意输入一个错误的地址时,便会自动跳转到404页面

九、vue-router与location.href的区别

  • vue-router使用pushState进行路由更新,静态跳转,页面不会重新加载location.href会触发浏览器,页面重新加载一次
  • vue-router使用diff算法,实现按需加载,减少dom操作
  • vue-router路由跳转或同一个页面跳转location.href不同页面间跳转
  • vue-router异步加载this.$nextTick(()=>{获取url})location.href同步加载

十、导航钩子

导航守卫分为: 全局的单个路由独享的组件内的三种。

全局的

他的特点是所有路由配置的组件都会触发,直白点就是触发路由就会触发这些钩子函数,如下的写法。钩子函数按执行顺序包括beforeEach、beforeResolve(2.5+)、afterEach三个

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})

beforeEach:在路由跳转前触发,参数包括to,from,next(参数会单独介绍)三个,这个钩子作用主要是用于登录验证,也就是路由还没跳转提前告知。

路由独享的

是指在单个路由配置的时候也可以设置的钩子函数,其位置就是下面示例中的位置,也就是像Foo这样的组件都存在这样的钩子函数。目前他只有一个钩子函数beforeEnter

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

beforeEnter:和beforeEach完全相同,如果都设置则在beforeEach之后紧随执行,参数tofromnext

组件内的

是指在组件内执行的钩子函数,类似于组件内的生命周期,相当于为配置路由的组件添加的生命周期钩子函数。钩子函数按执行顺序包括beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave三个

<template>
  ...
</template>
export default{
  data(){
    //...
  },
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}
<style>
  ...
</style>

参考文章

@TieMuZhen TieMuZhen added the Vue label Nov 24, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant