Vue.js - Vue Router

Vue Router 是 Vue.js 的官方插件,用来快速实现单页应用。

单页应用


SPA(Single Page Application)单页面应用程序,简称单页应用。指的是网站的 “所有” 功能都在单个页面中进行呈现。具有代表性的有后台管理系统、移动端、小程序等。

优点:

  • 前后端分离开发,提高了开发效率。
  • 业务场景切换时,局部更新结构。
  • 用户体验好,更加接近本地应用。

缺点:

  • 不利于 SEO。
  • 初次首屏加载速度较慢。
  • 页面复杂度比较高。

前端路由


前端路由,指的是 URL 与内容间的映射关系,有 Hash 和 History 两种实现方式。

Hash 方式

Hash 方式就是通过 hashchange 事件监听 hash 变化,并进行网页内容更新。

<body>
  <div>
    <a href="#/">首页</a>
    <a href="#/category">分类页</a>
    <a href="#/user">用户页</a>
  </div>
  <div id="container">
    这是首页功能
  </div>
</body>
window.onhashchange = function () {
  var hash = location.hash.replace('#', '')
  var str = ''
  switch (hash) {
    case '/':
      str = '这是首页功能'
      break
    case '/category':
      str = '这是分类功能'
      break
    case '/user':
      str = '这是用户功能'
      break
  }
  document.getElementById('container').innerHTML = str
}

封装以备用

// 准备对象,用于封装 hash 功能
var router = {
  // 路由存储位置:保存了 url 与 内容处理函数 的对应关系
  routes: {},
  // 定义路由规则的方法
  route: function (path, callback) {
    this.routes[path] = callback
  },
  // 初始化路由的方法
  init: function () {
    var that = this
    window.onhashchange = function () {
      // 当 hash 改变,我们需要得到当前新的 hash
      var hash = location.hash.replace('#', '')
      // 根据 hash 触发 routes 中对应的 callback
      that.routes[hash] && that.routes[hash]()
    }
  }
}

var container = document.getElementById('container')
// 定义路由
router.rote('/', function () {
  container.innerHTML = '这就是首页功能'
})
router.rote('/category', function () {
  container.innerHTML = '这就是分类功能'
})
router.rote('/user', function () {
  container.innerHTML = '这就是用户功能'
})
// 初始化路由
router.init()

特点总结:

  • Hash 方式兼容性好。
  • 地址中具有 #,不太美观。
  • 前进后退功能较为繁琐。

History 方式

History 方式采用 HTML5 提供的新功能实现前端路由。在操作时需要通过 history.pushState() 变更 URL并执行对应操作。

<body>
  <div>
    <a href="/">首页</a>
    <a href="/category">分类页</a>
    <a href="/user">用户页</a>
  </div>
  <div id="container">
    这是首页功能
  </div>
</body>
var router = {
  // 存储路由的对象
  routes: {},
  // 定义路由的方法
  route (path, callback) {
    this.routes[path] = callback
  },
  // 用于触发指定的路由操作
  go (path) {
    // 更改 url
    // 参数1:与当前路径相关的状态对象 state(暂时不需要)
    // 参数2:标题 title (浏览器不支持)
    // 参数3:需要修改的地址路由
    history.pushState(null, null, path)
    // 触发路由对应的回调函数
    this.routes[path] && this.routes[path]()
  }
};

// 设置 a 标签的功能
var links = document.querySelectorAll('a')
var container = document.querySelector('#container')

links.forEach(function (ele) {
  ele.addEventListener('click', function (event) {
    router.go(this.getAttribute('href'))
    event.preventDefault()
  });
});

// 路由规则
router.route('/', function () {
  container.innerHTML = '首页功能';
});

router.route('/category', function () {
  container.innerHTML = '分类功能';
});

router.route('/user', function () {
  container.innerHTML = '用户功能';
});

前进后退功能,首先需要在更改 url 时保存路由标记。

go: function (path) {
  histroy.pushState({ path: path }, null, path)
  ...
}

通过 popstate 事件监听前进后退按钮操作,并检测 state。

init: function () {
  var that = this
  window.addEventListener('popstate', function (e) {
    var path = e.state ? e.state.path : '/'
    that.routes[path] && that.routes[path]()
  })
}

调用初始化方法监听前进后退操作并处理。

...
router.init()
...

Vue Router


Vue Router 是 Vue.js 官方的路由管理器,让构建单页面应用变得易如反掌。

基本使用

Vue Router 提供了用于进行路由设置的组件 <router-link> 与 <router-view>。
<router-link> 默认是一个 a 标签,如果想要改成其他形式可以使用 tag 属性更改。
<router-view> 用来显示路由匹配到的组件。

<div id="app">
  <router-link to="/">首页</router-link>
  <router-link to="/category">分类</router-link>
  <router-link to="/user">用户</router-link>
  <router-view></router-view>
</div>

定义路由中需要的组件,并进行路由规则设置。

var Index = {
  template: `<div>这是首页的功能</div>`
}
var Category = {
  template: `<div>这是分类的功能</div>`
}
var User = {
  template: `<div>这是用户的功能</div>`
}

var routes = [
  { path: '/', component: Index },
  { path: '/category', component: Category },
  { path: '/user', component: User }
] 

创建 Vue Router 实例,通过 routes 属性配置路由。

var router = new VueRouter({
  routes: routes
})

创建 Vue 实例,通过 router 属性注入路由。

var vm = new Vue({
  el: '#app',
  router: router
})

命名视图

如果导航后,希望同时在同级展示多个视图(组件),这时就需要进行命名视图。

<div id="app">
  <router-link to="/">首页</router-link>
  <router-link to="/user">用户</router-link>
  <router-view name="sidebar"></router-view>
  <!-- 未通过 name 命名的,默认名为 default -->
  <router-view></router-view>
</div>

路由中通过 components 属性进行设置不同视图的对应组件。

var SideBar = {
  template: `<div>这是侧边栏功能</div>`
}
..
{
  path: '/',
  components: {
    sidebar: SideBar,
    default: Index
  }
}
..

动态路由

当我们需要将某一类 URL 都映射到同一个组件,就需要使用动态路由。

定义路由规则时,将路径中的某个部分使用 : 进行标记,即可设置为动态路由。

var User = {
  template: `<div>这是用户的功能</div>`
}
var routes = [
  { path: '/user/:id', component: User }
]

设置为动态路由后,动态部分为任意内容均跳转到同一组件。

<div id="app">
  <router-link to="/user/1">用户1</router-link>
  <router-link to="/user/2">用户2</router-link>
  <router-link to="/user/3">用户3</router-link>
  <router-view></router-view>
</div>

: 部分对应的信息称为路径参数,存储在 vm.$route.params 中。

var User = {
  template: `
    <div>
      这是用户 {{ $route.params.id }} 的功能
    </div>
  `
}

如果要响应路由的参数变化,可以通过 watch 监听 $route。

var User = {
  template: `<div>这是用户 {{ $route.params.id }} 的功能</div>`,
  watch: {
    $route (route) {
      console.log(route)
    }
  }
}

路由参数还可以通过路由的 props 设置数据,并通过组件 props 接收。

var routes = [
  {
    path: '/user/:id',
    component: User
  },
  {
    path: '/category/:id',
    component: Category,
    props:true
  }
]
var User = {
  template: `<div>这是用户 {{ $route.params.id }} 的功能</div>`,
}
var Category = {
  props: ['id'],
  template: `<div>这是 {{ id }} 的功能</div>`
}

包含多个命名视图时,需要将路由的 props 设置为对象。

var SideBar = {
  template: `<div>这是侧边栏功能</div>`
}
{
  path: '/category/:id',
  components: {
    defualt: Category,
    sidebar: SideBar
  }
  props: {
    default: true,
    sidebar: false
  }
}

如果希望设置静态数据,可将 props 中的某个组件对应的选项设置为对象,内部属性会绑定给组件的 props。

var SideBar2 = {
  props: ['a', 'b'],
  template: `
    <div>
      这是右侧侧边栏功能: {{ a }} {{ b }}
    </div>
  `
}
{
  path: '/category/:id',
  components: {
    defualt: Category,
    sidebar: SideBar,
    sidebar2: SideBar2
  }
  props: {
    default: true,
    sidebar: false,
    sidebar2: { a: '状态1', b:'状态2' }
  }
}

嵌套路由

实际场景中,路由通常由多层嵌套的组件组合而成,这时需要使用嵌套路由配置。

使用 children 来进行嵌套路由中的子路由设置。

var routes = [
  {
    path: '/path',
    component: User,
    children: [
      {
        path: 'hobby',
        component: UserHobby
      },
      {
        path: 'info',
        component: UserInfo,
        children: [
          { path: 'age', component: UserInfoAge },
          { path: 'school', component: UserInfoSchool }
        ]
      }
    ]
  }
]

编程式导航

编程式导航,指的是通过方法设置导航。

router.push() 用来导航到一个新 URL。

vm.$router.push('/user')
vm.$router.push({path: '/user'})
vm.$router.push({path: '/user/123'})

<router-link> 的 to 属性使用绑定方式时也可属性对象结构。

<router-link :to="{ path: 'user/10' }" >用户10</router-link>

设置路由时添加 name 属性来命名路由。

var School = {
  template: `<div>School 组件:{{ $route.params }}</div>`
}
var routes = [
  {
    path: 'user/:id/info/school',
    name: 'school',
    component: School
  }
]

在 push() 中通过 name 导航到对应路由,参数通过 params 设置。

vm.$router.push({ name: 'school', prarams: { id: 20, demo: '其他数据' } })

也可以在 <router-link> 中使用。

<router-link :to="{ name='school', params: { id: 1 } }">用户学校</router-link>

重定向

示例如下:

var routes = [
  { path: '/', component: Index },
  { path: '/category/:id', component: Category },
  { path: '/category', redirect: '/' }
]

别名

示例如下

var routes = [
  {
    path: '/user/:id/info/school/intro/:date',
    name: 'school',
    component: School,
    alias: '/:id/:date'
  }
]
<router-link :to="{ name: 'school', params: { id: 1, date: 0101 } }">用户学校</router-link>
<router-link to="/10/0612">用户学校</router-link>

导航守卫

当路由发生改变的时候,可以通过跳转或者取消的方式进行守卫处理。

示例如下:

// beforeEach 在每条路由触发之前进行操作
// to 表示跳转到的路由的相关信息
// from 表示从哪个路由跳转的相关信息
// next 根据 to 和 from 的情况判断是否执行
router.beforeEach(function (to, from, next) {
  console.log(to, from)
  next()
})

History 模式

History 模式需要通过 Vue Router 实例的 mode 选项来设置,这样 URL 会更加美观,但同样需要后端支持避免问题。

var router = new VueRouter({
  mode: 'history',
  routes: [
    ...
  ]
})
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容