安装 Chrome vue 调试工具

打开谷歌应用商店搜索 Vue.js devtools 并添加至 Chrome

在谷歌浏览器扩展程序页面找到刚才添加的扩展程序,点击详情

在下面找到 允许访问文件网址 打开这个设置

在渲染vue页面的时候,打开控制台,可以看到vue选项,就可以开始使用 Vue.js devtools


什么是Vue?

简介

vue是构建用户界面的框架

学习vue,就是在学习vue框架中规定的用法,框架是一套现成的解决方案,程序员只能遵守框架的规范去编写自己的功能。我们需要学习vue的指令,组件,路由,Vuex,vue组件库等

Vue特性

  1. 数据驱动视图

    vue会监听数据的变化,从而自动重新渲染页面的结构

    数据驱动视图是单向的数据绑定

  2. 双向数据绑定

    js数据的变换会被自动渲染到页面

    页面表单变化后不需要手动操作DOM,就可以获取表单的值

  3. MVVM

    Model:表示当前页面渲染所依赖的数据源

    View:表示当前页面所渲染的DOM结构

    ViewModel:表示vue的实例,他是MVVM的核心

    data中的所有属性,最后都出现在了vm上

    vm上所有属性,以及Vue原型上的所有属性在Vue模板中都可以直接使用。


使用Vue

快速开始

我们可以去vue官网下载vue.js 文件,并在 script 标签中导入使用

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
    <!-- 
1.想让vue工作,就必须创建一个vue实例,传入一个配置对象
2.root 容器里的代码依然符合html规范,只不过混入了一些特殊的vue语法
3.root容器里的代码称为 【vue 模板】
4.vue实例和容器是一一对应的
5.真实开发中只有一个vue实例,并且会配合组件一起使用
6.{{xxx}} 中的xxxx 要写js表达式且xxx可以自动读取到data中的所有属性
7.一旦data中的数据发生改变,那么模板中用到该数据的地方也会自动更新
-->
<body>
<div id="root">
<h1>Hello,{{name}}</h1>
<p>1+2的结果是{{1+2}}</p>
<p>{{name}} 反转后是:{{name.split('').reverse().join('')}}</p>
</div>
<!-- 导入vue的库文件 -->
<script src="./lib/vue.js" type="text/javascript"></script>
<script type="text/javascript">
Vue.config.productionTip = false //阻止Vue的生产提示
//创建Vue实例,传入配置对象
const x = new Vue({
//el 用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串
el: '#root',
//data中存储数据,数据供el所指定的容器去使用,值可以是对象也可以是函数
data: {
name: "张三是我呀"
}
})
</script>
</body>

Vue指令

指令是vue为开发者提供的模板语法,用于辅助开发者渲染页面的基本结构u

内容渲染指令

常见的内容渲染指令有

v-text:指令会覆盖元素内部的原有内容

  • 代码
  • 效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div class="ptext">
<!-- 1. 使用v-text, 属性值为要渲染的数据的键 -->
<p v-text="username"></p>
<!-- v-text会把原来的内容覆盖掉 -->
<p v-text="gender">性别</p>
</div>
<script>
const ptext = new Vue({
el:".ptext",
data:{
username:'yjr1100',
gender:'你猜'
}
})
</script>

{{}} 插值表达式:只是内容的占位符,不会覆盖原有内容

只能用在内容节点,不可以用在属性节点,在插值表达式中还可以进行javascript的操作,如调用函数,加减运算,字符串拼接等

  • 代码
  • 效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="chazhitext">
<!-- 2. 插值语法: Mustache
功能:用于解析标签体内容
写法:{{xxxx}} xxxx是js表达式,可以直接读取到data中的所有属性
-->
<p>姓名:{{username}}</p>
<p>性别:{{gender}}</p>
</div>
<script>
const chazhitext = new Vue({
el:"#chazhitext",
data:{
username:'yjr1100',
gender:'你猜'
}
})
</script>

v-html:把带有标签的字符串,渲染为html标签

  • 代码
  • 效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="htmltext">
<!--
使用v-html 可以将标签渲染成html标签,上面两个只能渲染成文本内容
-->
<p v-html="info"></p>
</div>
<script>
const htmltext = new Vue({
el:"#htmltext",
data:{
info:'<h4 style="color:red;font-weight:bold;">我是yjr1100</h4>'
}
})
</scrip>

属性绑定指令

功能:用于解析标签(包括:标签属性,标签体内容,绑定事件。。。)

例子:v-bind:placeholder="xxx" 或简写 :placeholder="xxx",xxx同样js表达式, 可以读取到data中的数据

备注:Vue中有很多的指令,且形式都是v-???,此处拿v-bind举例子

如果绑定的内容需要动态拼接,则字符串外面需要用单引号包裹,比如:
<div :title="'box'+index">我的title动态计算出是 box3</div>

  • 代码
  • 效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="app">
<!--
功能:用于解析标签(包括:标签属性,标签体内容,绑定事件。。。)
例子:v-bind:placeholder="xxx" 或简写 :placeholder="xxx",xxx同样js表达式, 可以读取到data中的数据
备注:Vue中有很多的指令,且形式都是v-???,此处我们只是拿v-bind举例子
-->
<input type="text" v-bind:placeholder="tips">
<hr>
<img :src="poto" alt="" style="width:80px">
<hr>
<div :title="'box'+index">我的title动态计算出是 box3</div>
</div>
<script>
const vm = new Vue({
el:"#app",
data:{
index:3,
tips:"请输入用户名",
poto:"https://cn.vuejs.org/images/logo.svg"
}
})
</script>

事件绑定指令

v-on 事件绑定

辅助DOM元素的绑定事件监听

v-on:click = “xxxx” xxxx是事件处理函数的名字 ,可以v-on:简写为 @

下面实现给按钮绑定一个事件,点击后 count+1

在绑定事件后,可以在小括号中进行传参

  • 代码
  • 效果
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
<!-- 事件绑定 
v-on 事件绑定
辅助DOM元素的绑定事件监听
v-on:click = "xxxx" xxxx是事件处理函数的名字
-->
<div id="root">
<p>count的值是:{{count}}</p>
<button v-on:click = "add(2)" style="background-color:#fff">+2</button>
<button @click = "sub" style="background-color:#fff">-1</button>
</div>
<script>
new Vue({
el:"#root",
data:{
name:"张三",
count:1
},
// methods的作用就是定义事件处理函数
methods:{
add:function(a){
this.count +=a;
},
sub(){
this.count -=1;
}
}
})
</script>

事件绑定对象

在我们没有给绑定函数传参时,默认有一个事件对象e,如果事件传参了,那么事件对象e就会被覆盖

想要再次获取到事件对象e,vue 提供了内置变量,名字叫做$event,就是原生对象

我们如果传参后还想拿到事件对象,那么我们需要把 $event 传入函数中

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
<!-- 事件对象$event
vue 提供了内置变量,名字叫做$event,就是原生对象
-->
<div id="root">
<p>count的值是:{{count}}</p>
<button @click = "add(1,$event)">+N</button>
</div>
<script>
console.log(this)
new Vue({
el:"#root",
data:{
count:1
},
// methods的作用就是定义事件处理函数
methods:{
add(n,e){
this.count +=n;
console.log(e)
// 判断count是奇数还是偶数
if(this.count%2===0){
e.target.style.backgroundColor = 'red';
}else{
e.target.style.backgroundColor = ' '
}
}
}
})

</script>

事件修饰符

当我们点击a链接的时候,默认会发生网页的跳转

如果我们想要阻止默认行为,除了使用 event.stopPropagation()event.preventDefault() 我们就可以使用 vue 的事件修饰符,把修饰符通过 . 夹在绑定事件的后面,方便对事件出发的控制

常用事件修饰符:
.prevent 阻止默认行为
.stop 阻止事件冒泡
.capture 以捕获模式触发当前的事件处理函数
.once 绑定的事件只触发1次
.self 只有当 event.target 是当前元素自身时出发事件处理函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

<div id="root">
<a href="http://www.baidu.com/" @click.prevent="show">跳转到百度首页</a>
</div>
<script>
console.log(this)
new Vue({
el:"#root",
data:{
count:1
},
// methods的作用就是定义事件处理函数
methods:{
show(){
document.write("点击了a连接")
}
}
})
</script>

双向绑定指令

vue 中有2种数据绑定的方式:

  1. 单向绑定(v-bind):数据只能从data流向页面。

  2. 双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data
    v-model 只能用于表单类元素的数据双向绑定,v-model 默认收集的就是value值。可以简写

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
    <!-- 双向数据绑定
vue 中有2种数据绑定的方式:
1.单向绑定(v-bind):数据只能从data流向页面。
2.双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data
v-model 只能用于表单类元素的数据双向绑定,v-model 默认收集的就是value值。可以直接简写为v-model
-->
<div id="root">
单向数据绑定:<input type="text" v-bind:value = "name"><br>
双向数据绑定:<input type="text" v-model:value = "name"><br>
<hr>
单向数据绑定简写:<input type="text" :value = "name"><br>
双向数据绑定简写:<input type="text" v-model= "name">
<hr>
<select v-model="city">
<option value="">请选择城市</option>
<option value="1">背景</option>
<option value="2">长沙</option>
<option value="3">广州</option>
</select>
</div>
<script>
new Vue({
el:"#root",
data:{
name:"张三",
city:""
}
})
</script>

v-model 有一些特定的修饰符,来方便对用户输入的内容进行处理

.number 将用户输入的值转为数值类型

.trim 将用户输入的首位空白字符过滤掉

.lazy 在“change”时而不是“input”的时候进行数据更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div id="root">
<input type="text" v-model.number="n1">+<input type="text" v-model.number="n2"> = <span>{{n1+n2}}</span>
<hr>
<input type="text" v-model.lazy.trim="name">
<button @click="showName">控制台显示用户名</button>
</div>
<script>
new Vue({
el:"#root",
data:{
n1:0,
n2:0,
name:""
},
methods:{
showName(){
console.log(`用户名是:${this.name}`);
}
}
})
</script>

条件渲染指令

条件渲染用来帮助开发者按照控制需求来控制DOM元素的隐藏和显示

v-if :每次会动态的移除和添加元素

v-show :通过display属性来控制元素隐藏和显示

如果要频繁的显示和隐藏,v-show 的性能好一些,如果刚进入页面某些元素不需要展示并且后期也可能不需要展示使用 v-if

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="root">
<!-- 如果flag为true就显示为false就隐藏 -->
<p v-if="flag">v-if的标签</p>
<!-- 如果flag为false就显示为true就隐藏 -->
<p v-show="!flag">v-show的标签</p>
</div>
<script>
new Vue({
el:"#root",
data:{
// 如果flag为true就显示为false就隐藏
flag:true,
name:"张三",
city:""
}
})
</script>

v-if 配套使用的还有 v-else-ifv-else

列表渲染指令

对于数字和对象这样的数据,需要创建重复的页面结构,我们一般使用列表渲染指令来减少代码的书写

列表数据需要在什么标签进行渲染,就把 v-for 写在哪个标签上

官方建议,只要使用到了v-for命令,那么一定绑定一个 :key 属性

而且尽量把 id 作为 key 的值,使用 index 的值当作 key 没有意义

官方对 key 的值的类型要求只能是:字符串或者数字

key 的值必须是唯一的,不可重复

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
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
</head>
<div id="root">
<table class="table table-bordered table-hover table-striped">
<thead>
<th>索引</th> <th>姓名</th> <th>手机号</th>
</thead>
<tbody>
<tr v-for="(item,index) in list" :key="item.id">
<td>{{index}}</td>
<td>{{item.name}}</td>
<td>{{item.phone}}</td>
</tr>
</tbody>
</table>
</div>
<script>
new Vue({
el:"#root",
data:{
list:[
{ id:1, name:"yjr1100", phone:"1234566" },
{ id:2, name:"yjrfirst", phone:"178984566" },
{ id:3, name:"yjrblog", phone:"1756789566" }
]
}
})
</script>

按键修饰符

按键修饰符在触发特定的按键时使用,下面使用escentera 的按键修饰符进行演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div id="root">
<input type="text" @keyup.esc="clearinput" @keyup.enter = "commitAjax" @keyup.a="aisin">
</div>
<script>
new Vue({
el:"#root",
// methods的作用就是定义事件处理函数
methods:{
clearinput(e){
e.target.value = ""
},
commitAjax(){
console.log("触发了ajax方法")
},
aisin(){
console.log("aisin")
}
}
})
</script>

一个小案例

案例来自黑马程序员《品牌列表案例》

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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>品牌列表案例</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
<style>
body {
padding: 15px;
user-select: none;
}
.card {
position: relative;
display: -ms-flexbox;
display: flex;
-ms-flex-direction: column;
flex-direction: column;
min-width: 0;
word-wrap: break-word;
background-color: #fff;
background-clip: border-box;
border: 1px solid rgba(0, 0, 0, 0.125);
border-radius: 0.25rem;
}
.card-body {
-ms-flex: 1 1 auto;
flex: 1 1 auto;
min-height: 1px;
padding: 1.25rem;
}
.card-header {
padding: 0.75rem 1.25rem;
margin-bottom: 0;
background-color: rgba(0, 0, 0, 0.03);
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
}
.mb-2{
margin-bottom: 0.5rem !important;
}
.form-row {
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -5px;
margin-left: -5px;
}
.col-auto{
position: relative;
width: 100%;
padding-right: 15px;
padding-left: 15px;
}
</style>
</head>
<body>
<div id="app">
<!-- 卡片区域 -->
<div class="card">
<div class="card-header">
添加品牌
</div>
<div class="card-body">
<!-- 添加品牌的表单区域 -->
<!-- form 表单元素有 submit 事件 -->
<form @submit.prevent="add">
<div class="form-row align-items-center">
<div class="col-auto">
<div class="input-group mb-2">
<div class="input-group-prepend">
<div class="input-group-text">品牌名称</div>
</div>
<input type="text" class="form-control" placeholder="请输入品牌名称" v-model.trim="brand">
</div>
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary mb-2">添加</button>
</div>
</div>
</form>
</div>
</div>
<!-- 表格区域 -->
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">品牌名称</th>
<th scope="col">状态</th>
<th scope="col">创建时间</th>
<th scope="col">操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in list" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>
<div class="custom-control custom-switch">
<!-- 使用 v-model 实现双向数据绑定 -->
<input type="checkbox" class="custom-control-input" :id="'cb' + item.id" v-model="item.status">
<!-- 使用 v-if 结合 v-else 实现按需渲染 -->
<label class="custom-control-label" :for="'cb' + item.id" v-if="item.status">已启用</label>
<label class="custom-control-label" :for="'cb' + item.id" v-else>已禁用</label>
</div>
</td>
<td>{{ item.time }}</td>
<td>
<a href="javascript:;" @click="remove(item.id)">删除</a>
</td>
</tr>
</tbody>
</table>
</div>
<script src="./lib/vue-2.6.12.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
// 用户输入的品牌名称
brand: '',
// nextId 是下一个,可用的 id
nextId: 4,
// 品牌的列表数据
list: [
{ id: 1, name: '宝马', status: true, time: new Date() },
{ id: 2, name: '奔驰', status: false, time: new Date() },
{ id: 3, name: '奥迪', status: true, time: new Date() },
],
},
methods: {
// 点击链接,删除对应的品牌信息
remove(id) {
this.list = this.list.filter(item => item.id !== id)
},
// 阻止表单的默认提交行为之后,触发 add 方法
add() {
// 如果判断到 brand 的值为空字符串,则 return 出去
if (this.brand === '') return alert('必须填写品牌名称!')
// 如果没有被 return 出去,应该执行添加的逻辑
// 1. 先把要添加的品牌对象,整理出来
const obj = {
id: this.nextId,
name: this.brand,
status: true,
time: new Date()
}
// 2. 往 this.list 数组中 push 步骤 1 中得到的对象
this.list.push(obj)
// 3. 清空 this.brand;让 this.nextId 自增 +1
this.brand = ''
this.nextId++
}
},
})
</script>
</body>
</html>