vue2.0一起在懵逼的海洋里越陷越深(六)

前言

本文章系列:vue2.0一起在懵逼的海洋里越陷越深 (http://leenty.com/tags/vuejs/)
演示场地vue2.0 Demo,这是源码地址,觉得靠谱的话欢迎加星跟随,有问题欢迎评论和指正😃

在vue开发SPA应用的过程中,多数情况下我们需要解决一个问题
就是在路由跳转的过程中需要更新你SPA应用的title
这一节不说其他,就展示如何使用vue-router导航钩子去解决这么一个问题。
接下来就愉快的去玩耍啦!
红红火火恍恍惚惚

正文

好的,介绍下背景,我有这么一个博客的demo,里面有多个版块,每个版块有着不同的名称(title)
先看一下Demo的路由结构

1
2
3
4
5
6
7
8
9
10
11
vue2.leenty.com
├── home # 首页版块
├── article # 文章版块
│ ├── vue2-1 # 具体文章一
│ ├── vue2-2 # 具体文章二
│ ├── vue2-3 # 具体文章三
│ ├── vue2-4 # 具体文章四
│ ├── vue2-5 # 具体文章五
│ └── vue2-6 # 具体文章六
└── demo # 演示版块
└── demo-1 # 具体演示一

好的,接下来要实现的是在切换路由的时候同时的去切换你页面的title

思路

这里思路是使用vue-router的路由全局导航钩子去解决这个问题
在路由对象里添加一个title字段以供路由全局导航钩子读取并更新页面title

配置路由

所以第一步,先在路由对象里添加这一个字段。
打开src/routes.js(源文件地址:https://github.com/leenty/vue2/blob/master/src/routes.js)
(注意是routes.js,这是咱用来存放路由对象的文件)
在原有数据的基础上添加title
这里其实vue1.0和vue2.0的实现是差不多的,所以vue1.0也是可以使用的。
vue2.0路由对象提供了一个meta字段来给你存放一些其他信息,所以这里就可以用来存放title
vue1.0的话是没有这个字段的,所以可以直接与path平级。
具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
const routes = [
{
name: 'Home',
path: '/',
meta: {
title: 'home' // 主页的title为home
},
component: require('./components/Home.vue')
},
{
name: 'Article',
path: '/article',
meta: {
title: 'article' // 文章模块相应的title为article
},
component: require('./components/Article.vue'),
children: [
{
name: 'vue2_1',
path: '/article/vue2_1',
meta: {
title: 'vue2.0一起在懵逼的海洋里越陷越深(一)' // 子路由也是一样的道理
},
component: require('./md/articles/vue2-1.md')
},
// ... 子路由和父路由都差不多,所以后面的就省略了
]
},
{
name: 'Demo',
path: '/demo',
meta: {
title: 'demo' // 演示模块title为demo
},
component: require('./components/Demo.vue'),
children: [
{
name: 'DemoVuexState',
path: 'vuex_state',
meta: {
title: 'vuex演示'
},
component: require('./components/DemoVuexState.vue')
}
]
}
]

export default routes

如此这般,各个页面的title就预设好了

小明:”为什么title里不加上站点名后缀?像demo - leenty blog这样?“
老师:“滚出去!”

其实是这样的,后缀如果一个个加也是可以的,但为什么不用语句帮我们加上去呢?
这样就一劳永逸啦,再也不用自己一个个打后缀了,哈哈哈,真TM机智!
mdzz

路由导航钩子介绍

讲一讲这个所谓的全局导航钩子,听起来玄不愣登的。。。

导航是发生在路由改变时的事件,这也是为何网页的导航条叫导航条的原因
尤大大的原话是:“正如其名,vue-router 提供的导航钩子主要用来拦截导航,让它完成跳转或取消。有多种方式可以在路由导航发生时执行钩子:全局的, 单个路由独享的, 或者组件级的”
说的很明白,言简意赅,其实就是能让你控制导航的一个方法而已
导航钩子分为全局,单个路由独享和组件级别的。
但不论如何,导航钩子都接受一个函数为参数,并会在导航过程中调用这个函数。
函数会被传入3个参数,分别为to, from, next
没错,你看字面意思应该理解了个大概,即:
from:你从哪里来?(问询消息的小弟A)
to:要到哪里去?(问询消息的小弟B)
next:让不让过去还得看老子我的!(大哥你懂不)

上面这位大哥(next)会有三中方法!

1
2
3
next() // 默认通过路由
next(false) // 中止导航,那么将会跳回到from的地址
next({ path: '/' }) // 跟一个路由参数对象,将会中止当前导航并跳往指向的路由

好的,先看看全局的写法
全局导航钩子一共两个,router.beforeEachrouter.afterEach
一个触发于导航开始前,一个触发于导航开始后。用法呢,都是一样的,如下!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
console.log('小弟B:哎呀妈呀!大兄弟,这是要去哪呀?', to)
console.log('小弟A:大兄弟,哪儿旮沓的呀!', from)
next(false) // 大哥:谁让你过去的?
// 调用next(false)中止导航,于是页面回到跳转前
})

router.afterEach((to, from, next) => {
console.log('小弟B:哎呀妈呀!大兄弟,这是要去哪呀?', to)
console.log('小弟A:大兄弟,哪儿旮沓的呀!', from)
next() // 大哥:过去吧!
// 调用next通过路由
})
``

**单个路由独享的钩子**,同样是两个方法`beforeEnter``afterEnter`,同样的套路。
套路如下:
```js
const router = new VueRouter({
routes: [
{
path: '/demo',
component: Demo,
beforeEnter: (to, from, next) => {
console.log('小弟B:哎呀妈呀!大兄弟,这是要去哪呀?', to)
console.log('小弟A:大兄弟,哪儿旮沓的呀!', from)
next() // 大哥:过去吧!
// 调用next通过路由
},
afterEnter: (to, from, next) => {
console.log('小弟B:哎呀妈呀!大兄弟,这是要去哪呀?', to)
console.log('小弟A:大兄弟,哪儿旮沓的呀!', from)
next({ path: '/' }) // 大哥:像那边走!
// 调用next({ path: '/' })中止导航,并跳到首页
}
}
]
})

组件内的钩子,依然是一对基友方法beforeRouteEnterbeforeRouteLeave
套路还是一样的0.0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const Demo = {
template: `<div>this is a Demo </div>`,
beforeRouteEnter (to, from, next) {
console.log('小弟B:哎呀妈呀!大兄弟,这是要去哪呀?', to)
console.log('小弟A:大兄弟,哪儿旮沓的呀!', from)
next() // 大哥:过去吧!
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当钩子执行前,组件实例还没被创建
},
beforeRouteLeave (to, from, next) {
console.log('小弟B:哎呀妈呀!大兄弟,这是要去哪呀?', to)
console.log('小弟A:大兄弟,哪儿旮沓的呀!', from)
next() // 大哥:过去吧!
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}

配合路由全局导航钩子去更新title

好的,三种都介绍完了,那么打开src/router.js,没错,这回是router.js,这是咱装载路由的文件

在此之前,我们还需要知道在一个嵌套路由情况下的节点分布。
三个参数之一的to存在属性to.matched,里面存在了一个包含路由节点的数组
顺序是从子路由到根路由

好的,确定下title文案

router title
├── home leenty blog
├── article article - leenty blog
│ ├── vue2-1 vue2.0一起在懵逼的海洋里越陷越深(一) - article - leenty blog
│ ├── ... … - article - leenty blog
│ └── vue2-6 vue2.0一起在懵逼的海洋里越陷越深(六) - article - leenty blog
└── demo demo - leenty blog
└── demo-1 具体演示1 - demo - leenty blog

里面的结构是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './routes'

const title = 'leenty blog'
// 定义我们站点的名字

Vue.use(VueRouter)

/* eslint-disable no-new */
const router = new VueRouter({
mode: 'history',
linkActiveClass: 'u-link--Active',
routes
})

// 路由导航钩子,beforeEach,在路由进入前调用
router.beforeEach((to, from, next) => {
let titleStr = ''
// 检测是不是要跳转首页,如果是,则不处理
if (to.name !== 'Home') {
// 倒序遍历数组获取匹配到的路由节点,拼接各部分title
for (let i = to.matched.length - 1; i >= 0; i--) {
titleStr += `${to.matched[i].meta.title} - `
}
}
// 添加站点名
titleStr += title
// 更新title
document.title = titleStr
// 继续路由导航
next()
})

export default router

ok,打完收工!现在可以切换路由看看title有没有在变化了。
可以看我的Demohttp://vue2.leenty.com,四处切换路由,看看标题如何变化吧!

其他

演示地址(http://vue2.leenty.com)
源码地址(https://github.com/leenty/vue2)
github主页,觉得靠谱的话欢迎加星跟随

vue2.0一起在懵逼的海洋里越陷越深(五)

前言

本文章系列:vue2.0一起在懵逼的海洋里越陷越深 (http://leenty.com/tags/vuejs/)
之前的几篇文章的说了vue-router以及vuex的简单使用,这次就开始组合这两者进行实际应用。
场地呢就在我的vue2.0Demo,这是源码地址,觉得靠谱的话欢迎加星跟随,有问题欢迎评论和指正😃
OK交代完毕,说下这回要完成的内容。
这回要做的是一个带缩放功能的滑动菜单(就是一个侧拉栏)和文章列表,以及文章的展示路由
使用vuex做状态管理控制css完成动画过度效果以及使用vue-router的嵌套路由功能。

滴滴滴!上车请刷卡!

正文

首先,我们先把核心功能完成,也就是vue-router的嵌套路由功能

嵌套路由功能

有些时候我们会遇到一个模板重复使用的情况,注意我说的并不是一个子模板在其父模板上的重复,而是多个页面之间的使用到相同的父模板。
就好比有这么几个路径

  • vue2.leenty.com/article/demo1
  • vue2.leenty.com/article/demo2
  • vue2.leenty.com/article/demo3
    可以发现其中共有的部分vue2.leenty.com/article
    1
    2
    3
    4
    5
    6
    7
    8
    /article/demo1                 /article/demo2               /article/demo3
    +------------------+ +-----------------+ +-----------------+
    | article | | article | | article |
    | +--------------+ | | +-------------+ | | +-------------+ |
    | | demo1 | | +-----> | | demo2 | | +-----> | | demo3 | |
    | | | | | | | | | | | |
    | +--------------+ | | +-------------+ | | +-------------+ |
    +------------------+ +-----------------+ +-----------------+

没错,这个路径指向的就是父模板,他在多个子路由里被重复的渲染,如果我们没有对其做嵌套路由的处理,结果将是不仅我们写的累,vuejs渲染的也多余,因为这个模板被反复的使用却每次都被重新渲染。
so,我们开始做嵌套路由吧!
打开路由列表文件(routes.js)
tips:不要把routes.js和项目里的router.js搞混咯,一个是路由列表文件routes.js,一个是路由导出文件router.js
在里面我们假定有一个基础的路由(这个文件在本系列第二章里有讲到)

1
2
3
4
5
6
7
8
9
10
11
const routes = [
{
name: 'Article',
path: '/article',
component: require('./components/Article.vue')
// 这里因为书写以及以后做异步组件的方便,将原先的import导入的方式改成了require的方式
// 这么做不会实质上没和之前没有变化的,只是单纯的写法不同。
}
]

export default routes

好,接下来我们加入子路由,
目前本系列已经有五篇文章,那么现在一一加入进去。
当前的项目路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
src
├── main.js # vuejs应用入口
├── router.js # 路由导出文件
├── routes.js # 路由列表文件
├── App.vue # vuejs应用根模板
├── components
│ ├── Home.vue
│ ├── Article.vue
│ ├── Demo.vue
│ ├── ... # 存放各个路由入口文件
│ └── templates # 存放模板
│ ├── AppArticleList.vue # 文章列表
│ └── ...
└── store
│ ├── index.js # 组装模块并导出 store 的地方
│ ├── actions.js # 根级别的 action
│ ├── mutations.js # 根级别的 mutation
│ ├── types.js # mutation命名空间
│ └── modules
│ ├── demo.js # demo模块
│ └── status.js # 全局应用状态模块
└── md # 存放md文件
└── articles # 文章路径
├── vue2-1.vue
├── vue2-2.vue
├── vue2-3.vue
├── vue2-4.vue
└── vue2-5.vue
(这里为了讲解方便,会将.md文件替换为.vue文件,)
(效果是一样的只是怕读者有些乱,.vue文件你们尝试的时候可以写些东西)

嵌套路由时会在原有路由对象的基础上添加一个children的节点
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
const routes = [
{
name: 'Article',
path: '/article',
// 父路由上尽量写绝对路径,不然可能会引起一些麻烦,
// 有捣(xia)鼓(gao)精神的同学可以试试
component: require('./components/Article.vue'),
children: [
// children节点:传入一个子路由数组,格式与父路由是一样的
{
name: 'vue2_1',
path: 'vue2_1',
// path: 导入的路径是相对路径,你不需要写全,如下的path将会被vuejs理解为:
// '/article/vue2_1'
// *** 注意: ***
// 如果你写的是这样的相对路径,就千万别在前面加'/',不然就意味着这是一个绝对路径
component: require('./md/articles/vue2-1.vue')
},
{
name: 'vue2_2',
path: '/article/vue2_2',
// 演示一个绝对路径,这样子的路径会被vuejs理解为其本意'/article/vue2_2'
// 如果这里只写了'/vue2_2'的话,vuejs就会把路由理解为'/vue2_2',
// 不过不用担心他显示上的问题,只是url会有些有趣的区别
// 你会发现虽然url变短了,但是父模板依然正常的显示着。
component: require('./md/articles/vue2-2.vue')
},
{
name: 'vue2_3',
path: 'vue2_3',
component: require('./md/articles/vue2-3.vue')
},
{
name: 'vue2_4',
path: 'vue2_4',
component: require('./md/articles/vue2-4.vue')
},
{
name: 'vue2_5',
path: 'vue2_5',
component: require('./md/articles/vue2-5.vue')
}
]
}
]

export default routes

嗯,好了,这样嵌套路由就建立好了

对侧拉栏做状态管理

打开src/store/modules/status.js
代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import * as types from '../types'
// 引入命名空间,放置在types里

import getScrollData from '../../utils/scroll'
// 这是获得滚动数据的js文件,具体怎样写的可以去我github开源项目里看
// 导出的是一个方法,这个方法返回一个对象
// {
// scrollTop: 0,
// scrollHeight: 0,
// windowHeight: 0,
// scrollBottom: 0
// }

const state = {
articleList: false,
// 布尔值,表示侧拉文章列表的开关
scroll: {
// 滚动相关的数据
scrollTop: 0,
scrollHeight: 0,
windowHeight: 0,
scrollBottom: 0
}
}

const getters = {
// articleList: ({status}) => status.articleList
// tips: 上面这种方法是错误的。这里的getters传入进来的不是state的根对象,
// 而是当前的state对象,在这里也就相当于是state = state.status
// 所以可以放心的用state
articleList: state => state.articleList
// 设置Getters,在.vue文件里可以使用mapGetters方法获得
}

const mutations = {
[types.ARTICLE_LIST] (state) {
state.articleList = !state.articleList
},
// 设置mutations,只有这个方法可以改变state
[types.SCROLLDATA] (state, scrollObj) {
state.scroll = scrollObj
}
}

const actions = {
articleListSwitch ({ commit }) {
commit(types.ARTICLE_LIST)
},
// 设置actions,可以在这里做一些异步的工作,然后使用commit来调用mutations修改state
pushScrollData ({ commit }) {
commit(types.SCROLLDATA, getScrollData(), { silent: true })
// 因为页面滚动触发频繁,会影响到我们平时的vuex的审查,所以这里选择静默提交
}
}

export default {
state,
getters,
actions,
mutations
}
// 而后的.vue文件就可以调用辅助方法达到获取状态或者控制状态的目的了

调试vuex最好使用vuejs的谷歌插件Devtools
或者去github下载源码自己编译
翻墙要梯子(vpn),想翻墙没vpn的我这里安利一个vpn,没什么其他,因为我正用的就是这个。
贴一个云梯推荐码http://referyt.com/?r=f688f08a26bf108d,通过推荐码注册可以优惠10元,笔者也可以得到10元优惠,在得到优惠的同时也是对我的鼓舞吧😂

渲染链接列表

接下来要做的就是做链接跳转。
切到AppArticleList.vue文件,也就是文章列表页。
大家可以在我的Demo里点击左上角的菜单按钮看到这个列表
!(AppArticleList.vue)[/img/vue/AppArticleList.jpg]

里面的内容是这样子的
(因为内容有些复杂,为了看的清晰点这是我提取出来的核心部分)
(源文件可以看https://github.com/leenty/vue2/blob/master/src/components/templates/AppArticleList.vue)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<!-- 大致的内容就是渲染一个文章列表 -->
<template lang="pug">
.articleList(:class="{'articleList--Active': articleList}").l-page--Full
ul.articleList__content.u-ul--Reset
li(
v-for="(item, index) in list",
// vuejs的for循环,list指向了data里的list数组(看js部分)
:style="calcDelay(index)",
// 绑定返回一个css延时代码(在methods方法下)
// 主要功能是给菜单提供依次进入的动画效果
@click="articleListSwitch"
// 绑定点击事件,控制文章列表的出现和隐藏
).articleList__li
router-link(
// 通过list里的数据循环生成路由链接
:to="{name: item.name}",
// 绑定路由链接,这里用了{name: item.name}是因为相对来说,name比path方便
// item就是之前for循环里的item
active-class="articleList__link--Active"
// 之前的文章的第二篇里对路由时匹配的`linkActiveClass`做了配置,
// 这里用不到原来配置的class,所以可以用这个方法单独的做处理
// 把匹配到的链接的class改成了'articleList__link--Active'
).articleList__link.u-borderBox.u-link__inherit.l-flexV--c
.articleList__title
svg.svg__home.u-va--tb
use(xlink:href="#svg__articleIcon")
// 这里用的图标是svg的,关于这种用法,如果有兴趣我可以专门的介绍一下
| {{item.title}}
// 绑定链接的文字
</template>

<script>
import { mapActions, mapGetters } from 'vuex'
// 导入vuex的两个将要用到的辅助方法
export default {
// export defalut是es6的模块导出方法,这里导出一个默认模块,即是一个vue对象
data () {
// 这是一种es6对象字面量的缩写,指向一个函数
// 扩展出来就是data: function data () {}
return {
// 而后在函数里去返回将要用到的数据
// 这么做返回的数据可以使用this访问到
list: [
{
name: 'vue2_1',
title: 'vue2.0一起在懵逼的海洋里越陷越深(一)'
},
{
name: 'vue2_2',
title: 'vue2.0一起在懵逼的海洋里越陷越深(二)'
},
{
name: 'vue2_3',
title: 'vue2.0一起在懵逼的海洋里越陷越深(三)'
},
{
name: 'vue2_4',
title: 'vue2.0一起在懵逼的海洋里越陷越深(四)'
},
{
name: 'vue2_5',
title: 'vue2.0一起在懵逼的海洋里越陷越深(五)'
}
]
}
},
computed: mapGetters(['articleList']),
// vue的计算方法对象,用法和data里的数据是一样的,
// 这里使用了mapGetters辅助函数,用来获得vuex的Getters里的articleList
// 这个方法会返回一个对象,所以如果没有其他计算方法的话直接赋值给computed就好了
// 有的话可以向下面这样:
// computed: {
// demo () {
// return this.list[0].name
// 在这里你可以使用this访问到data里的list
// },
// ...mapGetters(['articleList'])
// ...延展操作符,用于展开mapGetters得到的对象,使得与demo属性平级
// },
methods: {
// vue的方法对象,你可以在这里写你的事件处理函数或是其他方法
calcDelay: function (index) {
// 这是分配css动画延迟量的函数
return {
'transitionDelay': `.${index + 2}s`
}
},
...mapActions(['articleListSwitch'])
// 通过延展操作符,展开mapActions得到的actions对象
}
}
</script>

<style lang="stylus">
// css就不多说了
@import '../../assets/stylus/preinstall'
.articleList
background-color c-master
.articleList__content
width s-articleList
margin-top 8vh
max-height 84vh
color #fff
.articleList__li
transform translateX(-(s-articleList))
transition transform .3s cb-duang .2s
.articleList__link
width 100%
line-height 1.3em
padding-left 10px
min-height 50px
background-color rgba(255,255,255,.1)
&:hover
background-color rgba(255,255,255,.3)
svg
transform-origin bottom center
transform scale(.7)
.articleList__link--Active
background-color rgba(255,255,255,.4)
.articleList--Active .articleList__li
transform translateX(0)
.articleList__title
display block
width 100%
font-size 12px
&:first-line
font-size 14px
</style>

在写的过程中碰到了一个浏览器bug,这里还是说下
bug: 如果父元素进行了transform变换,那么子元素的position fixed将会失去作用,退化成position absolute
解决方案: 之前试过想要使用css的方式去绕过这个问题,但是发现不行,无奈使用了js去计算子元素退化成absolute后的差值,对其进行位移达到视觉的效果。
有兴趣的可以打开http://vue2.leenty.com/article/vue2_2然后审查元素,查看我的导航条也就是.header__content,这个时候你打开侧边栏然后上下滚动就会看到.header__content上js所不全的差值了

装载嵌套路由

打开article.vue文件,文件很简单,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template lang="pug">
.article
h1.l-ta--c 文章页面
.md-content
router-view
// 这个就是二级路由的装载点了
// 之前的`APP.vue`的`router-view`会装载当前模板,而后的文章都会装载到这个点上。
</template>

<script>
export default {
data () {
return {
}
}
}
</script>

<style lang="stylus">
</style>

关于触发滚动之后的一系列事件

关于触发滚动,考虑到之后可能会有多个页面需要使滚动方面的数据,所以就放在了vuex里作为状态的一部分。
所以滚动的监听就只需要监听一次就好了,多个地方的监听会浪费资源
最终,我把监听放在了App.vue里面,在里面监听’scroll’事件,然后放在vuex上,供有需要的组件去使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<template lang="pug">
.app.u-clearfix
app-article-list
.app__content(
:class="{'app__content--Active': articleList}",
// 如果侧拉文章列表打开,那么添加这个class,相应的css效果将被触发
@scroll="pushScrollData"
// 绑定scroll事件
).l-page--Full
home-header
.app__bodyer.l-mH--auto
router-view
include ./assets/svg/all
</template>

<script>
import HomeHeader from './components/templates/HomeHeader.vue'
import AppArticleList from './components/templates/AppArticleList.vue'
import { mapGetters, mapActions } from 'vuex'
export default {
components: {
HomeHeader,
AppArticleList
},
computed: mapGetters([
'articleList'
]),
methods: {
...mapActions([
'pushScrollData',
'articleListSwitch'
])
}
}
</script>

<style lang="stylus">
@import './assets/stylus/main'
.app__menu
background-color c-master
.app__content
overflow auto
transition transform .5s
box-shadow 0 0 100px 5px rgba(0,0,0,0.3)
background-color c-bgc
transform-origin s-articleList center
.app__content--Active
transform scale(.9) translateX(s-articleList)
.app__bodyer
max-width max-width
background-color c-bgc
@media screen and (max-width: max-width)
.app__bodyer
padding 0 10px
</style>

ok,这么个流程走下来,算是把vuejs,vue-routervuex用在了一起

其他

演示地址(http://vue2.leenty.com)
源码地址(https://github.com/leenty/vue2)
github主页,觉得靠谱的话欢迎加星跟随

vue2.0一起在懵逼的海洋里越陷越深(四)

前言

本文章系列:vue2.0一起在懵逼的海洋里越陷越深 (http://leenty.com/tags/vuejs/)
前面一篇讲了vue-router的router-link,
有读者说希望能先看到vuex的文章
其实vuex在我的vue2.0的demo里已经在使用了
想想确实应该先来点vuex的了,因为有些有关的状态管理急需vuex的加入
好,那么这次就先上vuex吧!

正文

OK!先介绍下vuex
vuex是vuejs的状态管理方案
因为vuex有些复杂,一般应用在较复杂的状态环境下,如果是比较简单的应用就不需要安装vuex了

vuex单向数据流

vuex单向数据流
如上图所示是vuex单向数据流的流程

我知道,如果是第一次接触这玩意,到这就懵逼了,就像我第一次看文档一样
vuex单向数据流

下面解释下State
前面说了vuex是vuejs的状态管理方案,这里的状态就是图中的state
在平时写写前端的过程中,都会涉及到状态的问题,
比如你看到了一篇文章写的不错,就想要收藏,
然后又对作者有些兴趣,于是又加了关注,当然这些最后都是需要前端来表达出来
这里面就涉及了两个状态就是收藏与关注
这里的收藏与关注我们可以将其用一个变量来表示,当然如这两种只存在两个相对状态的用布尔值(bool)来表示就可以了

然后是view
view就是视图,也就是前端所写的页面,前面的state就可以在view里面得到体现,即是你看到的是否关注了作者

而后是Actions
ActionsView用来改变State的方法,通过改变State从而改变View的显示

这么说有些抽象,打个比方
假设学生小明生病了
小明要向班主任请假(小明就是View,小明想改变自己的显示状态,从上学状态变成请假状态)
于是小明口头向班主任请假(请假就是Actions,小明通过请假这一个方法达到请假的目的)
班主任批准,小明请假成功(班主任就是State的提供者,小明通过请假方法使班主任改变小明的State状态)
着也就解释了为什么要有这个流程,有了这个流程就可以统一管理各个view的各个状态

vuex多个组件共享状态

先贴个流程图,懵逼是没啥关系的
vuex多个组件共享状态
可以看到这一张图比前面一张复杂了许多,其实里面的实线框就是上图的升级版

情况是这样,原来小明学校里这段时间许多同学得了流感,于是同学们一窝蜂的要求请假,
学校里因为人太多,又没有一个记录,人数清点不过来了,于是班主任要求请假必须提交请假条

好的,来解释下上图内的实线部分,之前的View在这里改名成了Vue Components,代表了多个组件(这里即是指多个同学)
包括小明在内的多个同学Vue Components都得了流感,一起想要请假Actions(他们即将共享一个请假状态),小明代表多个同学写了一张请假条交给班主任(State提供者)签字同意(签字同意这个动作就是Mutations,触发了State的改变),班主任同意后统一给他们改变了State(请假成功)

这里的多了一个同步与异步的区别,其中Mutations只受理同步处理,而Actions就是执行异步操作的函数,作为代价,Actions不再能直接触发改变State状态,而是需通过Mutations来触发State的改变

情况是这样,因为不只是一个班的学生得了流感,请假的人比较多,学校Backend API需要做一个统计,于是请假需要得到学校专门的请假条的并通过学校签字才能生效,这里就有了虚线部分。
请假Actions改变State的方法不再是仅仅班主任签字就可以了,而是需要通过学校这个前提,于是Actions就变成了一个异步请求,需要得到学校Backend API的专门的请假条和签字同意后,才能向班主任提交改变State的申请

这里想要表达的意思就是,Vue Components改变自身状态需要通过Actions(有需要的话要向后端接口Backend API发送请求)来操作Mutations改变State从而改变自身状态。
至于图中没有说到的Devtools是一个开发者工具,是一个便于查看和管理vue应用以及vuex状态的浏览器插件。
你可以在里面看到你vue对象和vuex每一次的commit,commit后面会说到。还有你当前的应用状态
devtools
附上一个devtools库地址

架构vuex

项目结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
├── main.js
├── App.vue
├── components
│ ├── Home.vue
│ └── ...
└── store
├── index.js # 组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
├── types.js # mutation命名空间
└── modules
├── demo.js # demo模块
└── status.js # 全局应用状态模块

结构可以根据个人需要进行调整

下面开始构建store/index.js
当然在这之前,如果没有安装vuex的需要先安装

1
npm i vuex -S

由于vuex的内容太多,这里先说基础用法
首先为store/index.js编写内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Vue from 'vue'
// 导入vue
import Vuex from 'vuex'
// 导入vuex

import status from './modules/status'
// 导入status模块(这是我管理全局应用状态的模块)
import demo from './modules/demo'
// 导入demo模块(这个的部分演示可以在http://vue2.leenty.com/demo/vuex_state/里看到)

Vue.use(Vuex)
// 告诉vue将要使用vuex

const debug = process.env.NODE_ENV !== 'production'
// env里去获取当前的环境是否需要开启严格模式
// 在发布环境开启严格模式会造成性能上不必要的损失

export default new Vuex.Store({
// 默认导出vuex模块
modules: {
// 导入模块
status,
demo
},
strict: debug
// 是否开启严格模式
})

构建state模块
这里以demo.js为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import * as types from '../types'
// 导入mutations的命名空间

const state = {
// 定义state
demoFollow: false,
// 这里模拟的是关注的状态,布尔值表示是否关注
demoFollowPending: false
// 是否在请求中(actions是否在执行异步操作)
}

const getters = {
// 定义getters, getters是对state的扩展,可以以state衍生出其他状态
demoFollowStatus: state => state.demoFollow ? '已关注' : '未关注'
// demoFollowStatus是demoFollow的衍生量,将原来的布尔值映射为'已关注' : '未关注'
}

const mutations = {
// 只有mutations才能操作改变state,mutations是同步执行的
// 所以有关异步的操作请放在actions里执行
[types.DEMO__VUEX_FOLLOW] (state, status = NaN) {
// 使用预定义好的名字来写mutations方法
state.demoFollow = isNaN(status) ? !state.demoFollow : status
},
[types.DEMO__VUEX_FOLLOW_PENDING] (state, status = NaN) {
// 这里对status传值做了审查,如没有传,则对要改变的布尔值进行取反操作
state.demoFollowPending = isNaN(status) ? !state.demoFollowPending : status
}
}

const actions = {
// actions是可以执行异步操作的,操作完毕后触发mutations里的方法去改变state的状态
demoFollowAjax ({commit}, status) {
commit(types.DEMO__VUEX_FOLLOW_PENDING)
// 在异步操作前通过mutations告诉应用,现在正在进行异步操作
setTimeout(() => {
// 利用延时函数模拟异步的ajax操作
commit(types.DEMO__VUEX_FOLLOW_PENDING)
// commit 是在actions里用来触发mutations的方法
// 告诉应用,异步操作结束
commit(types.DEMO__VUEX_FOLLOW, status)
// 为关注按钮赋予新的状态
}, 2000)
}
}

export default {
// 导出整个demo模块
state,
getters,
actions,
mutations
}

这里解释下mutations里的方法的奇怪写法,我知道如果对es6于法了解不多这里是会懵逼的

1
2
3
4
5
const mutations = {
[types.DEMO__VUEX_FOLLOW] (state, status = NaN) {
state.demoFollow = isNaN(status) ? !state.demoFollow : status
}
}

这是对象内方法的简化写法,其中types.DEMO__VUEX_FOLLOW是在types.js里预定义的,内容如下

1
2
3
4
export const DEMO__VUEX_FOLLOW = 'DEMO__VUEX_FOLLOW'
export const DEMO__VUEX_FOLLOW_PENDING = 'DEMO__VUEX_FOLLOW_PENDING'
// 其实就是字符串集合,最后用 import * as types from '../types' 方法导入vuex模块里
// 就变成了一个types对象

上例中全部展开是这样的

1
2
3
4
5
6
7
8
9
10
11
const mutations = {
[types.DEMO__VUEX_FOLLOW]: (state, status = NaN) => {
// [types.DEMO__VUEX_FOLLOW]是提取types.DEMO__VUEX_FOLLOW的值的一种方式
// 在这里[types.DEMO__VUEX_FOLLOW]提取出来就是types.js里预定义的'DEMO__VUEX_FOLLOW'
if (isNaN(status){
state.demoFollow = !state.demoFollow
} else {
state.demoFollow = status
}
}
}

好,到此为止,vuex就已经建立好了,下面就说如何使用了

应用状态的使用与改变

vuex提供了4个辅助函数

1
import { mapState, mapGetters, mapActions, mapMutations } from 'vuex'

是分别用来获取State, Getters, Actions, Mutations的map方法
在vue Components里面你可以以这样的方式来导入需要使用的方法

这4个方法拥有统一的参数格式以及一个统一的返回格式
可以传入一个数组

1
2
3
4
5
6
mapGetters(['demoFollowStatus'])
// 此方法导出的是一个Getters对象
mapGetters({
demoFollow: 'demoFollowStatus'
})
// 可以通过传入对象的形式来改变得到的getters方法名

同理,不同的map方法会导出各自的方法
其中mapState, mapGetters将会导出适用于computed的方法
mapMutations, mapActions将会导出适用于methods的方法

这里以一个demo组件为例DemoVuexState.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
export default {
data () {
return {
}
},
computed: {
// vue的计算属性,计算属性内方法的私有变量变动会触发这个属性的重新计算
...mapState({
mapStateFollow: ({demo}) => demo.demoFollow,
mapStateFollowPending: ({demo}) => demo.demoFollowPending
}),
// mapState()直接读取State里的状态内容
...mapGetters(['demoFollowStatus'])
// mapGetters()通过getters转化state从而得到你想要的内容
},
methods: {
// vue的方法,可以是一个事件的方法,也可以是一个vuex的方法,也可以是一个普通函数
...mapMutations(['DEMO__VUEX_FOLLOW']),
// 同步的改变状态
...mapActions(['demoFollowAjax'])
// 异步的改变状态
}
}

这段代码是一个demo里的片段,效果大家可以看这个呆萌,如果开心了可以在github库里加个星,如果有问题可以在下方评论里讨论讨论😀

vue2.0一起在懵逼的海洋里越陷越深(三)

本文章系列:vue2.0一起在懵逼的海洋里越陷越深 (http://leenty.com/tags/vuejs/)
前面写了vue-router组件的初步用法,原来这次想写vuex的,但是想想还是先继续把vue-router写完吧🤔
vue2.0

上回说到: vue-router的基本用法,下面继续深入

啊不!是继续向着懵逼海洋的海底继续下沉,哈哈哈哈
前面已经设置了两个路由,一个是首页,一个是文章页,现在在开一个演示页的路由
首页以后顶多就展示下大致的新内容,文章页顶多放放文章详情
这演示页可就厉害了,接下来的一些演示都会放在这里,具体的布局还没想好,先这么用着吧!

vue-router的超链接标签“router-link”

在vue1.0版本的超链接标签还是原来的a标签,链接地址由v-link属性控制(具体的这里也不多说了,vue1.0已经过去啦)
而vue2.0版本里超链接标签由a标签被替换成了router-link标签,当然,没有必要不用担心什么,因为最后没有什么特殊的设置的话,router-link标签还是会被渲染成a标签的。
至于为什么在vue2.0的时候被替换成了router-link标签呢?
大致YY了下,部分情节应该是这样的:

  • 最主要情况,在上个版本中,链接只能以a标签的形式存在,不能满足一些特殊的要求,比如在列表(ul>li)下,你需要为在里下面再加上一个a链接才能完成点击列表跳转页面,so2.0版本中路由从a标签变成了可以任意渲染成各种元素的router-link标签,如此一来,就可以直接渲染成li标签,省下了a标签(具体怎么做下面会统一说明)
  • 为什么不直接使用正常a标签写法做链接跳转呢?当点击正常的a链接时,就会感觉到页面跳转时的页面刷新重载的感觉,因为这个a标签并没有被vue-router所监听到事件的触发,所以就会发生正常的页面跳转,所以页面就会重载。那是传统的网页上才会发生的事。使用了vue-router组件所提供的router-link后,页面内的a标签就会被vue所监听,以便在用户点击链接的时候阻止浏览器的默认跳转行为,而转为无刷新加载的方式。当然这只作用于自己的站点内。

“router-link”标签的属性(Props)

没错,只要是标签,多多少少都是有属性的,最次class和id属性也是可以添加的。
下面说说router-link标签的各个属性

“to”属性

to属性最简单的用法就是如a标签里的href属性一样的填写,简单粗暴,想去哪就去哪。
其实,to属性的值可以有两种:

其一为字符串形式,也就是前面说到的如href一样的用法

1
2
3
4
<!-- 字符串 -->
<router-link to="/article">Article</router-link>
<!-- 渲染结果 -->
<a href="/article">Article</a>

其二为对象形式,下面看看to属性的 对象的主要结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 主要结构
{
name: 'Article'
}
// 或者
{
path: '/article'
}
// 二者选其中之一,如果两种都使用,那么vue会选择使用name字段
// eg
{
name: 'Article',
path: '/demo'
}
// 实际效果等同于:
{
name: 'Article'
}

对于里面的path字段,其实就是就是想去哪就去哪的to属性字符串的对象形式
至于name字段,是不是会有些懵逼?这个name其实就是上一篇里说到的routes.js文件里的name字段啦!
就是这个!填成一样的就好啦!注意大小写哦
routes.js-name字段

to属性的 对象的可选结构

1
2
3
4
5
6
7
8
9
10
{
name: 'Article', // 与path字段二选一
path: '/article', // 与name字段二选一
params: { // 可选字段 隐性的传递参数(多用来页面传参)
userId: 123 // 传递的参数demo
},
query: { // 可选字段 查询参数,就是url里‘?’之后的部分
plan: 'private' // 查询参数demo
}
}

可以使用chrome插件vue.js devtools来查看vue的路由的params参数,安装这个插件需要翻墙下载
或者可以去github上下代码编译插件,如果觉得麻烦也可以百度去其他网站下
对于可选参数是不是会有些懵,下面贴几张图就明白了
查看路由参数
其中params是隐性传递,query是显性传递。
路由query
红框部分就是传递的query

“replace”属性

顾名思义,replace是替换的意思,所以在router-link标签添加了这个属性后,页面切换时不会留下历史记录

1
<router-link :to="{ path: '/abc'}" replace></router-link>

“append”属性

append会把当前路径设置为相对路径,何为相对路径?比如你当前在vue2.leenty.com/demo下,点击这个链接<router-link :to="{ path: 'article'}" append></router-link>,如果没带append属性,那么就会跳到vue2.leenty.com/article下(hash模式下是vue2.leenty.com/#/article),而现在却是跳到了vue2.leenty.com/demo/article下。这个属性用处不是很大,在特殊情况下才会用到。
推荐还是把自己地址写全,不要使用相对地址,这样容易搞错
另外加个小技巧,在地址前加/能保证是跳转根目录的

“tag”属性

具有tag属性的的router-link标签会被渲染为相应的标签

1
<router-link to="/article" tag="li">article</router-link>

将会被渲染为

1
<li>article</li>

这里不需要担心li的跳转问题,vue会自动为其绑定点击事件,并跳转页面

“active-class”属性

这个属性如字面意思上说的,是设置激活链接时的class属性
即是当前页面所有与当前地址所匹配的链接都会被添加这个class属性

1
<router-link to="/article" active-class="u-link--Active">article</router-link>

如此,在vue2.leenty.com/article页面下(hash模式下是vue2.leenty.com/#/article)就会被渲染为

1
<a href="/article" class="u-link--Active"></a>

active-class属性的默认值是router-link-active,所以如果没有设置,就会被渲染为这个class

其实在上一章节有说到统一的设置这个active-class属性
打开router.js文件就能看到

1
2
3
4
5
const router = new VueRouter({
mode: 'hash',
linkActiveClass: 'u-link--Active', // 这是链接激活时的class
routes
})

“exact”属性

开启router-link的严格模式

1
<router-link to="/" exact>home</router-link>

上面这个标签如果不加exact属性,则会在vue2.leenty.com/article页面下也会被匹配到,
这却不是我们的本意,在加了这个属性后就会正确的匹配到vue2.leenty.com

以上为vue-router的router-link的介绍

vue2.0一起在懵逼的海洋里越陷越深(二)

本文章系列:vue2.0一起在懵逼的海洋里越陷越深 (http://leenty.com/tags/vuejs/)

说好了一起懵逼,那么我们继续下沉

在上一篇里已经将vue2.0需要的依赖都装齐了
那么接下来
vue2.0
因为vue最后生成的页面看似静态页面(对于静态页面这里有些偷鸡用法,不管你怎样,反正我已经露出了诡异的微笑😏,再贴一个vue2.0 demo的项目地址,大爷有兴趣可进去看看演示,开心了就加个星),其实却是个SPA(单页面应用)
没错!就是一个SPA

作为一个SPA,当然有不同的地方

SPA与传统网页区别的地方是SPA具有前端路由来模拟页面跳转,当然这是众多不同之一,这篇只说前端路由。
上一篇中有安装vue-router组件,这个就是vue的前端路由
vue + vue-router简直是爽,页面跳转的速度简直是不要不要的
不光是用户体验上的提升,作为一名开发者,在使用了如vue,react等这类MVVM框架后,就不会再想回到jQuery的时代了。

前面搞了半天,现在要开始coding啦!

好的,用自己的小编辑器打开vue项目(我用的是sublime)
可以看到项目目录是这样子的
files-tree
(插一句,如果喜欢这个sublime主题可以查看这篇文章)
与自己的目录对比发现少几个文件,那是没有什么关系的,接下来要做的就是去创建这些文件。

作为一个SPA,首先要有路由

从目录图片里可以看到main.js这个文件,没错这个就是程序的入口
这个文件的内容是这样的

1
2
3
4
5
6
7
8
9
10
import Vue from 'vue'
import router from './router'

import App from './App'

/* eslint-disable no-new */
new Vue({
router,
render: h => h(App)
}).$mount('#app')

main.js地址
这里用的是ES6的语法,使用import来导入包
这里导入了vue包,还有两个文件,分别是router.jsApp.vue
.js后缀是可以省略的(毕竟是亲生的,你不说导入什么类型的文件,肯定是自家人毕竟亲呀)
其实.vue后缀也是可以省略的,我建议还是写一下,如果遇到两个同名文件就尴尬了。

好的,这个router.js就是路由的输出口啦,
App.vue就是目录里已经存在的那个模板文件啦,你的界面就从这里开始啦。
引入了包就可以开始设置路由和挂载模板了

顺带一提,可以看到在new Vue()时传入了一个对象,但是这个对象却不是键值对,
是这样,这是ES6的一种语法,当引用的变量名和键名相同时,就可以简写成这样
如果还原来是这样的

1
2
3
4
5
6
7
8
9
10
new Vue({
router: router,
render: h => h(App)
}).$mount('app')
// 被简写成了
new Vue({
router, // 这是ES6对象的简写,扩展开就是router: router
// 箭头函数(=>)是ES6的新语法
render: h => h(App) // 这里扩展开就是render: (h) => { return h(App) }
}).$mount('app')

PS:ES6的新语法现在网上文章已经有很多了,我之后也会发一版常用的语法

那么现在在src目录下创建router.js文件

内容是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './routes'

// 告诉vue要使用router
Vue.use(VueRouter)

/* eslint-disable no-new */
// 实例化router对象
const router = new VueRouter({
mode: 'hash', // 设置路由模式 可选值: "hash" | "history" | "abstract",默认"hash"
linkActiveClass: 'u-link--Active', // 这是链接激活时的class
// base: '/app/', // 这个是设置根目录路径,一般用不到,默认'/'
routes // 挂载路由集合 后面会说
})
// 导出router对象
export default router

这是router.js地址
这里引入了两个包vue,vue-router和一个包含路由集合的routes.js文件
整个文件的逻辑就是,使用Vue.use()方法告诉vue我们使用了路由
然后就大大方方的导出路由对象
main.js router
这个时候main.js里就可以接受到这里导出的路由对象,并挂载到vue对象上

在src目录下创建routes.js文件

routes.js是用来放置路由集合的文件
其实路由集合是可以写在router.js里的,这里为什么不写在一起呢?
因为当路由集合变得庞大时,如果还是写在router.js里,就会显得拥挤,不便于阅读
所以这里推荐单独写出来。
同时呢也可以创建路由所对应的模板文件(.vue文件,我把它称为模板文件),Article.vueHome.vue
模板文件叫什么名由自己决定,于是就能看到这张图里所有文件都齐了
files-tree

那么在编写routes.js文件之前,需要先写好两个模板的内容(不然一会有没有成功都不清楚了,23333)

好的,贴一下home.vue的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template lang="pug">
.home
h1.l-ta--c Material Desgin
</template>

<script>
export default {
data () {
return {
}
}
}
</script>

<style lang="stylus">
</style>

vue模板是html结构的,这也是对界面编写最友好的方式
里面的<template> </template>标签就是视图
<script></script>就是js,这个没有争议
<style></style>是写css的,这个也没有问题
但是当仔细看我的代码,发现里面html使用了pug,css使用了stylus
其实不用他们也是可行的,只是用pug和stylus写结构比较清晰
不用也可以的
如果要使用,请打开你的终端,给项目添加几个包

1
npm i pug stylus stylus-loader -D

里面的css的class(.l-ta--c)看着有点懵逼的,可以看看使用BEM+emmet的css书写与命名技巧
好的,模板不需要太复杂,只要有字能显示就好了,至于Article.vue也是一样的,这里就不贴了

Tips:模板里推荐有一个根元素,就像这里的.home就是根元素,这样不容易混乱,结构会清晰

写好了模板就可以开始编写routes.js

先贴代码!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 导入之前写好的两个模板
import Home from './components/Home.vue'
import Article from './components/Article.vue'

// 编写路由集合
const routes = [
{
name: 'Home', // 路由名,这个字段是可选的
path: '/', // 路由路径,这里是根路径所以是'/'
component: Home // 模板
}, // 这些是常用的
{
name: 'Article',
path: '/article',
component: Article
}
]
// 导出路由集合
export default routes

然后是文件地址
最后导出了路由集合(routes)后就可以在router.js里使用了
于是,前面的router.js里的routes就有了。

现在进行最后一步,到App.vue里添加路由

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template lang="pug">
.app
header
//- 制作一个跳转链接
//- 这里不要直接用a链接跳转,那样会导致页面重载,
//- 相比之下用router-link是高效的方案
//- to属性就是链接的地址啦
router-link(to="/") home
router-link(to="/article") article
bodyer
//- 路由地址所对应的模板将会被挂载到router-view标签上
router-view
</template>

<script>
export default {
data () {
return {
}
}
}
</script>

<style lang="stylus">
</style>

地址
好了,到此为止就完成了路由搭建与使用了。