0%

前言

我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。比如在开发新闻模块的过程中,我们通常是根据ID获取对应的数据的,对于数据的渲染用的都是同一个组件。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果。

简单使用

我们在components文件夹中新建两个文件(也是组件),新闻列表v-news.vue和新闻详情v-news-detail.vue

新增组件代码

v-news.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
<template>
<div class="v-news">
<ul>
<li v-for="item in lists" :key="item.id">
<router-link :to="'/news/' + item.id">{{ item.title }}</router-link>
</li>
</ul>
</div>
</template>

<script>
export default {
name: 'v-news',
desc: '新闻列表',
data() {
return {
lists: [
{ id: 1, title: '新闻标题 1' },
{ id: 2, title: '新闻标题 2' },
{ id: 3, title: '新闻标题 3' },
{ id: 4, title: '新闻标题 4' },
{ id: 5, title: '新闻标题 5' },
{ id: 6, title: '新闻标题 6' },
]
}
}
}
</script>

<style scoped>
.v-news {
padding: 20px 50px;
}
</style>

v-news-detail.vue完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div class="v-news-detail">
<div>我是新闻详情,我的id是:{{ id }}</div>
</div>
</template>

<script>
export default {
name: 'v-news-detail',
desc: '新闻详情',
data() {
return {
id: this.$route.params.id || ''
}
}
}
</script>

<style scoped>
.v-news-detail {
padding: 20px 40px;
}
</style>

修改菜单和路由配置

v-header.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
<template>
<div class="v-header">
<ul class="menus">
<li
class="menu"
v-for="item in menus"
:key="item.id"
>
<router-link :to="item.path">{{ item.name }}</router-link>
</li>
</ul>
</div>
</template>

<script>
export default {
name: 'v-header',
desc: '头部信息',
data() {
return {
menus: [
{ id: 1, name: '首页', path: '/' },
{ id: 2, name: '新闻', path: '/news' },
{ id: 3, name: '关于', path: '/about' },
]
}
}
}
</script>

<style scoped>
.v-header {
width: 100%;
height: 70px;
background-color: #fff;
box-shadow: 3px 3px 3px #ddd;
color: #333;
}
.menus {
list-style: none;
padding: 0 20px;
overflow: hidden;
}
.menu {
float: left;
padding: 0 20px;
height: 70px;
line-height: 80px;
transition: .3s;
}
.menu:hover {
cursor: pointer;
color: #0051ff;
background-color: #efefef;
}
</style>

router -> index.js完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 引入 Vue
import Vue from 'vue';
// 引入 vue-router
import VueRouter from 'vue-router';
// 安装使用 vue-router
Vue.use(VueRouter);
// 引入首页
import vIndex from '../components/v-index.vue';
// 开始使用 vue-router
let routes = new VueRouter({
routes: [
{ path: '/', component: vIndex },
{ path: '/about', component: () => import(/* webpackChunkName: "about" */ '../components/v-about.vue') },
{ path: '/success', component: () => import(/* webpackChunkName: "success" */ '../components/v-success.vue') },
{ path: '/news', component: () => import(/* webpackChunkName: "news" */ '../components/v-news.vue') },
{ path: '/news/:id', component: () => import(/* webpackChunkName: "news" */ '../components/v-news-detail.vue') },
]
});
// 提供接口给外面使用
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
.
|-- node_modules
|-- public
| |-- favicon.
| |-- index.html
|-- src
| |-- assets
| |-- components
| | |-- HelloWorld.vue
| | |-- v-about.vue
| | |-- v-header.vue
| | |-- v-index.vue
| | |-- v-news-detail.vue
| | |-- v-news.vue
| | |-- v-success.vue
| |-- router
| | |-- index.js
| |-- App.vue
| |-- main.js
|-- .gitignore
|-- babel.config.js
|-- package-lock.json
|-- package.json
|-- README.md
.

大家可以对照一下目录,看看文件是否有缺失,另外也看一下代码是否能正常运行,如果有报错,可以私聊我哈。希望大家能动手敲一下代码,好记性不如烂笔头,至理名言。

前言

在上一章中,我们是依靠<router-link>进行跳转的,其实这个就是创建 a 标签来定义导航链接的。但是在我们实际的开发过程中,往往需要在跳转前处理一些逻辑或验证之类的事情,比如提交一个表单的数据,当点提交时,往往需要先做一个验证,确认数据无误后再进行提交,提交成功后跳转进入提交成功提醒页面。这个时候,我们就可以借助 router 的实例方法,通过编写代码来实现。

router 的实例方法

代码模版:

1
2
3
4
5
6
7
8
9
10
11
// 字符串
router.push('home')

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

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

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

我们在components文件夹下新建一个名字为v-success.vue的文件,在提交信息成功后出现。

v-success.vue的完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<div class="v-success">
<h1>提交成功</h1>
</div>
</template>

<script>
export default {
name: 'v-success',
desc: '提交成功'
}
</script>

既然加了新的跳转组件,那么我们肯定也需要修改一下路由配置文件。
打开 router -> index.js 文件,完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 引入 Vue
import Vue from 'vue';
// 引入 vue-router
import VueRouter from 'vue-router';
// 安装使用 vue-router
Vue.use(VueRouter);
// 引入首页
import vIndex from '../components/v-index.vue';
// 开始使用 vue-router
let routes = new VueRouter({
routes: [
{ path: '/', component: vIndex },
{ path: '/about', component: () => import(/* webpackChunkName: "about" */ '../components/v-about.vue') },
{ path: '/success', component: () => import(/* webpackChunkName: "success" */ '../components/v-success.vue') },
]
});
// 提供接口给外面使用
export default routes;

最后我们在v-about.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
<template>
<div class="v-about">
<h1>我是关于</h1>
<div class="msg">
<h4>信息填写</h4>
<form style="margin-top: 10px;">
<label>
<span>姓名:</span>
<input v-model="myName" />
</label>
<button style="margin-left: 20px; padding: 2px 10px;" @click.prevent="submit">提交</button>
</form>
</div>
</div>
</template>

<script>
export default {
name: 'v-about',
desc: '关于',
data() {
return {
myName: ''
}
},
methods: {
submit() {
let myName = this.myName;
if (myName) {
this.$router.push('/success');
} else {
alert('姓名不能为空!')
}
}
}
}
</script>

<style scoped>
.v-about {
padding: 20px 30px;
}
.msg {
width: 300px;
padding: 20px;
border: 1px solid #eee;
margin-top: 20px;
}
</style>

修改完后,我们在浏览器中看看效果,如果我不填写姓名就点击提交,页面出现姓名不能为空!的提示,填写完姓名后,成功跳转进入成功提示页面。

目前项目目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.
|-- node_modules
|-- public
| |-- favicon.
| |-- index.html
|-- src
| |-- assets
| |-- components
| | |-- HelloWorld.vue
| | |-- v-about.vue
| | |-- v-header.vue
| | |-- v-index.vue
| | |-- v-success.vue
| |-- router
| | |-- index.js
| |-- App.vue
| |-- main.js
|-- .gitignore
|-- babel.config.js
|-- package-lock.json
|-- package.json
|-- README.md
.

总结

以上只是一个简单的例子,代码在书写规范上随意了一些,所以请大家自动忽略我这样的随意,我们主要是看一下这个东西是怎么用的。

介绍

Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含的功能有:

  • 嵌套的路由/视图表
  • 模块化的、基于组件的路由配置
  • 路由参数、查询、通配符
  • 基于 Vue.js 过渡系统的视图过渡效果
  • 细粒度的导航控制
  • 带有自动激活的 CSS class 的链接
  • HTML5 历史模式或 hash 模式,在 IE9 中自动降级
  • 自定义的滚动条行为

对于初学者来说,对于上面的理解可能会有迷糊,不过没关系,我们先来直接使用一下它,慢慢感受下,然后再回来看一下这一些解释。

安装 vue-router

引入 vue-router 的方法有两种:
第一种:CDN

1
https://unpkg.com/vue-router/dist/vue-router.js

第二种:NPM

1
npm install vue-router

简单使用

首先,我们在vue-cli项目中的根目录中新建一个名称为router的文件夹,然后在文件夹中新建一个新的文件index.js

项目目录如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.
|-- node_modules
|-- public
| |-- favicon.
| |-- index.html
|-- src
| |-- assets
| |-- components
| | |-- HelloWorld.vue
| | |-- v-about.vue
| | |-- v-header.vue
| | |-- v-index.vue
| |-- router
| | |-- index.js
| |-- App.vue
| |-- main.js
|-- .gitignore
|-- babel.config.js
|-- package-lock.json
|-- package.json
|-- README.md
.

router -> index.js 完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 引入 Vue
import Vue from 'vue';
// 引入 vue-router
import VueRouter from 'vue-router';
// 安装使用 vue-router
Vue.use(VueRouter);
// 引入首页
import vIndex from '../components/v-index.vue';
// 开始使用 vue-router
let routes = new VueRouter({
routes: [
{ path: '/', component: vIndex },
{ path: '/about', component: () => import(/* webpackChunkName: "about" */ '../components/v-about.vue') },
]
});
// 提供接口给外面使用
export default routes;

在项目中应用

在上面,我们已经将路由配置写完了,那么接下来就是用到项目中去了。在前面的章节中,我们已经知道有main.js这么一个文件了,它的作用是程序入口文件,加载各种公共组件,那么我们接下来就更新下这一个文件吧。

main.js 完整代码:

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

// 引入我们配置好的路由
import router from './router';

Vue.config.productionTip = false

new Vue({
router,
render: h => h(App),
}).$mount('#app')

接着我们再更新下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
26
27
28
29
30
<template>
<div id="app">
<v-header />
<router-view></router-view>
</div>
</template>

<script>
import vHeader from './components/v-header.vue'

export default {
name: 'App',
components: {
vHeader
}
}
</script>

<style>
* {
padding: 0;
margin: 0;
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
}
</style>

多了一个<router-view />

修改v-header文件,完整代码:

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>
<div class="v-header">
<ul class="menus">
<li
class="menu"
v-for="item in menus"
:key="item.id"
>
<router-link :to="item.path">{{ item.name }}</router-link>
</li>
</ul>
</div>
</template>

<script>
export default {
name: 'v-header',
desc: '头部信息',
data() {
return {
menus: [
{ id: 1, name: '首页', path: '/' },
{ id: 2, name: '关于', path: '/about' },
]
}
}
}
</script>

<style scoped>
.v-header {
width: 100%;
height: 70px;
background-color: #fff;
box-shadow: 3px 3px 3px #ddd;
color: #333;
}
.menus {
list-style: none;
padding: 0 20px;
overflow: hidden;
}
.menu {
float: left;
padding: 0 20px;
height: 70px;
line-height: 80px;
transition: .3s;
}
.menu:hover {
cursor: pointer;
color: #0051ff;
background-color: #efefef;
}
</style>

多了个<router-link :to="item.path">{{ item.name }}</router-link>

总结

上一章出现的问题到了这里已经解决了,大家对于有些代码可能不是很明白,比如vue-router的格式和用法,这一些都会在后面再详细说一下,在这里我们只要能达到目的就可以了。另外,也希望大家能再看几遍项目的代码和目录结构。因为,看着看着说不定就熟悉了。

在上一章中,我们已经创建了头部导航组件,那么,点击不同的导航菜单,肯定是出现不同的内容的。下面我们再来创建两个组件v-indexv-about

创建首页v-index组件

我们在 components 文件夹中创建一个v-index.vue文件,也就是v-index组件。

全部代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div class="v-index">
<h1>我是首页</h1>
</div>
</template>

<script>
export default {
name: 'v-index',
desc: '首页'
}
</script>

<style scoped>
.v-index {
padding: 20px 30px;
}
</style>

创建关于v-about组件

我们在 components 文件夹中创建一个v-about.vue文件,也就是v-about组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div class="v-about">
<h1>我是关于</h1>
</div>
</template>

<script>
export default {
name: 'v-about',
desc: '关于'
}
</script>

<style scoped>
.v-about {
padding: 20px 30px;
}
</style>

新的问题

现在我们已经把v-indexv-about两个组件都创建完了,接下来就应该引入组件了。这个时候,问题来了,按照我们理解,点击首页引入v-index组件,点击关于引入v-about组件。那么,我们应该怎么做呢?

难道是在App.vue中一起引入这两个组件,然后依靠v-if点击不同的导航菜单引入不同的组件吗?这样好像也是可以实现,但是总感觉哪里怪怪的。这个时候,就到了vue-router出场了。

看一下 HelloWorld.vue

打开 components 文件夹中的 HelloWorld.vue 文件,先简单看一下,可以看到其实和 App.vue 是差不多的。

  1. html 结构内容写在 template里面
  2. js 部分写在script里面,使用export default{}暴露出去
  3. css 部分写在style里面,不过有点区别,就是多了一个scoped

scoped 的作用

scoped 的作用是下面样式的作用域就是当前这个组件,其他组件无法使用这个组件的样式。目前我们只需要知道是这么回事就可以了。

创建组件

我们在 components 文件夹中创建一个v-header.vue文件,也就是v-header组件。在网页中,头部一般有导航菜单,比如:首页,关于等,那么我们先模仿HelloWorld.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
<template>
<div class="v-header">
<ul class="menus">
<li
class="menu"
v-for="item in menus"
:key="item.id"
>{{ item.name }}</li>
</ul>
</div>
</template>

<script>
export default {
name: 'v-header',
desc: '头部信息',
data() {
return {
menus: [
{ id: 1, name: '首页', path: '/' },
{ id: 2, name: '关于', path: '/about' },
]
}
}
}
</script>

<style scoped>
.v-header {
width: 100%;
height: 70px;
background-color: #fff;
box-shadow: 3px 3px 3px #ddd;
color: #333;
}
.menus {
list-style: none;
padding: 0 20px;
overflow: hidden;
}
.menu {
float: left;
padding: 0 20px;
height: 70px;
line-height: 80px;
transition: .3s;
}
.menu:hover {
cursor: pointer;
color: #0051ff;
background-color: #efefef;
}
</style>

引入组件

上一章说过,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
26
27
28
29
<template>
<div id="app">
<v-header />
</div>
</template>

<script>
import vHeader from './components/v-header.vue'

export default {
name: 'App',
components: {
vHeader
}
}
</script>

<style>
* {
padding: 0;
margin: 0;
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
}
</style>

运行

打开 cmd 窗口并输入指令npm run serve,然后在浏览器中运行,正常显示,相信大家此刻一定有想继续做下去的冲动,先不要急,大家再好好看一下这个项目的其他文件,冷静一下,我们需要保持清醒。

创建项目

  1. 打开cmd命令窗口
  2. 进入我们需要存放代码的目录
  3. 输入命令:vue create vue-cli
  4. 回车后,我们会看到Please pick a preset,这是让我们选择预设值,前期我们直接选择默认的就好
  5. 再次回车,创建项目开始了
  6. 过了一会,创建完成后,我们输入命令cd vue-cli进入这个项目
  7. 进入后输入命令npm run serve回车开启项目
  8. 好了,能看懂英文的同学已经看到了两个地址,我们在浏览器中打开这两个地址就可以了

项目中的文件介绍

我们在开发工具中打开vue-cli项目,看看里面都有什么文件,分别有什么作用。

项目目录如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.
|-- node_modules // 依赖包,里面有各种项目所需要的包
|-- public // 避开webpack
| |-- favicon.ico // 小图标
| |-- index.html // 模板文件,作用是生成项目的入口文件
|-- src // 源码目录,我们写的代码基本会放在这里
| |-- assets // 公共文件存放处
| |-- components // vue公共组件
| |-- App.vue // 页面入口文件
| |-- main.js // 程序入口文件,加载各种公共组件
|-- .gitignore // 上传github的配置,前期先不管
|-- babel.config.js // babel配置,前期先不管
|-- package-lock.json // 在 `npm install`时候生成一份文件,用以记录当前状态下实际安装的各个npm package的具体来源和版本号
|-- package.json // 包信息,项目所需的包信息都在里面,指向`npm install`下载依赖包
|-- README.md // 项目说明,暂时不管
.

看一下 App.vue

我们先打开看一下App.vue文件,可以发现里面的代码既熟悉又陌生。比如 html 中的内容为:

1
2
3
4
5
6
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>

template标签以前讲过,这里不再讲解。

我们再看:

1
<HelloWorld msg="Welcome to Your Vue.js App"/>

是不是很熟悉?这个就是组件的使用,只是HelloWorld组件被单独放在了components文件夹里面,需要使用import引入,而以前我们都是在同一个文件中使用Vue.component()写的,这里有点小区别。

还有:

1
2
3
4
5
6
export default {
name: 'App',
components: {
HelloWorld
}
}

以前我们是直接使用new Vue(),这里使用的是export default {},相当于提供一个接口给外界,让其他文件通过import来引入使用。

总结

当我们创建完项目再打开这些文件来看时,对于初次接触vue-cli的小伙伴来说一定会感觉很陌生,甚至会有很多疑问,这些都是正常的。我们可以打开vue-cli文档看看,慢慢了解,千万不要急躁,也可以在里面先随便尝试下,不要怕出错,如果错了实在不知道怎么改了,就把这个项目删掉,然后重新再创建项目,继续试,实在不行了,大家可以私信我,把疑问的地方提出来,我给大家讲解。

注意:import/export是ES6引入的新规范,因为浏览器引擎兼容问题,需要在 node中 用 babel 将 ES6语法 编译成 ES5语法,初学者可能有点不太明白,这里大家先这样用就可以了,用多了就明白了。

学习准备

  1. HTML的基础知识,比如基本元素的使用和结构代码的编写
  2. CSS的基础知识,比如常见样式的编写和页面的布局
  3. Javascript的基础知识,比如常见函数的使用
  4. 已经看完了前面所发布的文章vue基础学习vue全局API学习vue选项

关于 vue-cli

vue-cli 是一个基于 Vue.js 进行快速开发的完整系统,简单理解就是快速构建单页应用的脚手架。如果小伙伴们对 vue 还是不太熟悉,建议再看一遍以前的文章加深一下理解。因为在学 vue-cli 时,我们可能会遇到一些以前从来没有见过的东西,比如webpackbabelvuexvue-router等,在刚开始的学习中,我会尽量减少接触这几个,到中期时再慢慢讲解。这个过程可能会有点痛苦,让人产生一些放弃的想法,但是一定要坚持住,坚持完了,那么小伙伴们就可以试着开始实战了。

安装 vue-cli

虽然目前还有一些公司用的是vue-cli 2.0,不过既然已经有了最新的,那么我们就直接安装最新的吧。

  1. 打开cmd命令窗口,输入命令:npm install -g @vue/cli
  2. 等到执行完后,我们可以使用vue --versionvue -V来检验是否安装成功。
    如果有显示类似@vue/cli 4.3.1的内容,说明我们已经安装成功了,下面就可以直接创建个项目开搞了。

注意

有些小伙伴喜欢用yarn安装依赖,其实只要能达到目的,用哪一个都是可以的,选择自己喜欢的就好。要是实在太慢了,大家也可以用cnpm试试,方法挺多,问题不大。关于yarn的用法,有兴趣的小伙伴可以直接在网上搜索,其实跟npm的用法差不多,区别不大。

关于 keep-alive 组件

在开发时,经常会出现页签之类的组件,当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。为了解决这个问题,我们可以用一个<keep-alive>元素将其动态组件包裹起来,从而在它们第一次被创建的时候缓存下来。

<keep-alive>包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。<keep-alive>是一个抽象组件:它自身不会渲染一个DOM元素,也不会出现在组件的父组件链中。

属性介绍

钩子在服务器端渲染期间不被调用。

当组件在<keep-alive>内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。

属性 说明
include 字符串或正则表达式。只有名称匹配的组件会被缓存。
exclude 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
max 数字。最多可以缓存多少组件实例。

模版代码:

1
2
3
<keep-alive :include="include" :exclude="exclude" :max="max">
<component :is="currentTab"></component>
</keep-alive>

属性的使用方法后期遇到了再讲解,在本章中主要看下生命周期钩子。

生命周期钩子函数的用法

周期 说明
activated 被 keep-alive 缓存的组件激活时调用
deactivated 被 keep-alive 缓存的组件停用时调用。

示例代码:

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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
.btn {
padding: 5px 10px;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
border: 1px solid #ddd;
cursor: pointer;
background-color: #fff;
margin-bottom: -1px;
margin-right: -1px;
}
.currentTab,
.btn:hover {
background-color: #ddd;
}
.btn:focus{
outline: 0;
}
.component {
padding: 10px 15px;
border: 1px solid #ddd;
width: 300px;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<div class="btns">
<button
class="btn"
:class="currentTab === item ? 'currentTab' : ''"
type="button"
v-for="(item, index) in tabs"
:key="index"
@click="currentTab = item"
>{{ item }}</button>
</div>
<keep-alive>
<component :is="currentTab"></component>
</keep-alive>
</div>
<script type="text/javascript">
Vue.component('tab01', {
template: `<div class="component">tab01<div>`,
activated() {
alert('activated = tab01');
},
deactivated() {
alert('deactivated = tab01');
}
})
Vue.component('tab02', {
template: `<div class="component">tab02<div>`,
activated() {
alert('activated = tab01');
},
deactivated() {
alert('deactivated = tab01');
}
})
var vm = new Vue({
el: '#app',
data() {
return {
tabs: ['tab01', 'tab02'],
currentTab: 'tab01'
}
}
})
</script>
</body>
</html>

运行上面的代码,过程就不再一一讲解了,都是一样的套路。

什么是生命周期

vue 的每个组件就像一个生命体,有出生,有成长,有死亡等阶段,它们会在不同的阶段做不同的事,这个过程就是生命周期。

vue组件生命周期有以下几个:

周期 说明
beforeCreate 创建前
created 创建后
beforeMount 挂载前
mounted 挂载后
beforeUpdate 更新前
updated 更新后
beforeDestroy 销毁前
destroyed 销毁后

用法

示例代码:

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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<v-demo v-if="!isDestroy"></v-demo>
<button @click="isDestroy = true">销毁组件</button>
</div>
<script type="text/javascript">
// 先来定义一个组件
Vue.component('v-demo', {
data() {
return {
msg: '更新前'
}
},
template: `
<div>
<div>{{ msg }}</div>
<button @click="msg='更新后'">更新</button>
</div>
`,
// 以下是该组件的生命周期
beforeCreate() {
alert('创建前:beforeCreate');
},
created() {
alert('创建后:created');
},
beforeMount() {
alert('挂载前:beforeMount');
},
mounted() {
alert('挂载后:mounted');
},
beforeUpdate() {
alert('更新前:beforeUpdate');
},
updated() {
alert('更新后:updated');
},
beforeDestroy() {
alert('销毁前:beforeDestroy');
},
destroyed() {
alert('销毁后:destroyed');
}
})
var vm = new Vue({
el: '#app',
data() {
return {
isDestroy: false
}
}
})
</script>
</body>
</html>
  1. 在浏览器中运行上面的代码,页面一次弹出创建前:beforeCreate->创建后:created->挂载前:beforeMount->挂载后:mounted,这个就是vue组件其中一部分生命周期,那么怎么触发剩下的生命周期呢?
  2. 我们接着点击更新按钮,可以看到弹出更新前:beforeUpdate->更新后:updated,这个就像一个人遇到了某些事,然后作出某些反应一样。
  3. 最后来触发下销毁的生命周期,怎么触发呢?这个就好比人,生命走到尽头就什么都没有了,也就是说,我们只要把这个组件去掉就可以了,点击销毁组件按钮后依次弹出销毁前:beforeDestroy->销毁后:destroyed

总结

通过示例代码,是不是对生命周期有了比较直观的了解?其实在日常中,不论是开发还是面试,生命周期都是避不开的一个坎,大家一定要多敲几次熟悉一下。如果想具体了解,可以到官网选项 / 生命周期钩子查看,里面已经有详细的解释了,这里就不再多说。

调用方式

示例代码:

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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<div>methodsMsg = {{ methodsMsg() }}</div>
<div>computedMsg = {{ computedMsg }}</div>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data() {
return {
msg: '我是信息'
}
},
computed: {
computedMsg() {
return this.msg;
}
},
methods: {
methodsMsg() {
return this.msg;
}
}
})
</script>
</body>
</html>

通过上面的代码可以发现,computed 定义的方法是以属性访问的形式调用的,methods 定义的方法是以函数调用的形式调用的。

computed 的缓存功能

上一章有提到过,computed 是支持缓存的,这要怎么证明它是支持缓存的呢?先来看一下下面的示例代码:

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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
<body>
<div id="app">
<div>computedMsg = {{ computedMsg }}</div>
<div>computedMsg = {{ computedMsg }}</div>
<div>computedMsg = {{ computedMsg }}</div>
<div>methodsMsg = {{ methodsMsg() }}</div>
<div>methodsMsg = {{ methodsMsg() }}</div>
<div>methodsMsg = {{ methodsMsg() }}</div>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data() {
return {
msg: '我是信息'
}
},
computed: {
computedMsg() {
alert('computedMsg');
return this.msg;
}
},
methods: {
methodsMsg() {
alert('methodsMsg');
return this.msg;
}
}
})
</script>
</body>
</html>

在浏览器中运行上面的代码,我们可以看到:computedMsg出现了一次弹出框,而methodsMsg出现了三次弹出框。

发现:
1,computed 定义的 computedMsg 方法只会做一次计算,返回一个值;
2, methods 定义的 methodsMsg 方法只要是遇见methodsMsg()都会运行。

结论:
computed 更适合复杂逻辑,避免重复调用造成的浪费,从而提高性能,优化用户体验。

总结

1,computed 是属性调用,而 methods 是函数调用;
2,computed 带有缓存功能,而 methods 没有;
3,computed 依赖于data中的数据,只有在它的相关依赖数据发生改变时才会重新求值,而 methods 需要我们调用方法时才会执行;
4,computed 也可以防止文本插值中逻辑过重而导致不易维护的情况出现。