Vue的特性

数据驱动视图

  • 当页面数据发生变化时, 页面会重新渲染
  • 单向的数据绑定

双向数据绑定

  • form表单负责采集数据, ajax负责提交数据

注: 数据驱动视图和双向数据绑定的底层原理是MVVM

MVVM

  • Model, View, ViewModel
  • Model 表示当前页面渲染时所依赖的数据源
  • View 表示当前页面所渲染的DOM结构
  • ViewModel 表示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
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
<!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">
<script src="https://cdn.jsdelivr.net/gh/Rr210/hexofixed@v0.47/js/jquery-3.5.1.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Rr210/hexofixed@frame/frame/bootstrap/3/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/gh/Rr210/hexofixed@frame/frame/bootstrap/3/js/bootstrap.min.js"></script>
<title>Document</title>
</head>
<style>
body {
padding: 0;
margin: 0;
box-sizing: border-box;
}

.tab {
margin: 100px auto;
}

.checkOn {
overflow: hidden;
width: 66px;
height: 25px;
line-height: 25px;
border-radius: 20px;
border: 1px solid #888;
font-size: 8px;
}

.check_warp {
position: relative;
}

input[id^="check"][type="checkbox"] {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
opacity: 0;
}

.on {
float: left;
width: 23px;
height: 23px;
line-height: 23px;
border-radius: 50%;
background-color: #ccc;
color: #000;
}

.isChecked {
background-color: #fff;
}

.isbg {
color: #fff;
background-color: rgb(0, 110, 255);
}

.form_data {
margin: 20px 0;
}

#btn1 {
outline: none;
}
</style>

<body>
<div class="tab container text-center" id="app">
<h2>汽车品牌列表</h2>
<form class="form-inline form_data" @submit.prevent="add">
<div class="form-group">
<label class="sr-only" for="exampleInputAmount">Amount (in dollars)</label>
<div class="input-group">
<div class="input-group-addon">请输入品牌</div>
<input type="text" class="form-control" id="exampleInputAmount" name="brand_name" placeholder="请输入要添加的品牌">
</div>
</div>
<button class="btn btn-primary" id="btn1" type="submit">添加</button>
<a class="" tabindex="0" class="btn btn-lg btn-danger" role="button" id='btn2' data-toggle="popover" data-trigger="" data-delay="show:500;hide:400" title="提示" data-content=""></a>
</form>
<table class="table table-striped table-bordered justify-content-center table-hover">
<thead>
<tr>
<th class="text-center" v-for="(item,index) in th_title">{{item}}</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in brandList" :key="item.id">
<td>{{index+1}}</td>
<td>{{item.brand_name}}</td>
<td>
<div class="check_warp">
<label :for="'check'+item.id" :class="item.status?'checkOn isbg':'checkOn'">
<div :class="item.status?'on isChecked':'on'" :style="item.status?'float:right':'float:left'"></div>
{{item.status?'已启用':'已禁用'}}
</label>
<input @change="getFloat" :data-index='index' type="checkbox" :checked="item.status" name="status" :id="'check' + item.id">
</div>
</td>
<td>{{item.time | formatDate}}</td>
<td><button @click="remove(index)">移除</button></td>
</tr>
</tbody>
</table>
</div>
</body>
<script src="../../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
th_title: ['#', '品牌名称', '状态', '创建时间', '操作'],
brandList: [{
id: 0,
brand_name: '宝马',
status: true,
time: new Date()
},
{
id: 1,
brand_name: '奔驰',
status: false,
time: new Date()
},
{
id: 2,
brand_name: '奥迪',
status: true,
time: new Date()
},
{
id: 3,
brand_name: '路虎',
status: true,
time: new Date()
}
]
},
methods: {
getFloat(e) {
let {
checked
} = e.target
let {
index
} = e.target.dataset
this.brandList[index].status = checked
},
remove(index) {
this.brandList.splice(index, 1)
},
add(e) {
let timer;
let brand_name = $('#exampleInputAmount').val().trim()
if (brand_name.length == 0) {
this.getmodal('您输入的内容为空,请重新输入!!')
} else {
this.brandList.push({
id: this.brandList.length,
brand_name,
status: false,
time: new Date()
})
this.getmodal('品牌已成功添加到列表中')
}
},
getmodal(content) {
$('.popover-content').text('') // 先清空
$('#btn2').data('content', content).popover('show')
timer = setTimeout(function() {
$('#btn2').popover('destroy')
}, 2500)
}
},
filters: {
formatDate: function(now) {
var year = now.getFullYear(); //取得4位数的年份
var month = now.getMonth() + 1; //取得日期中的月份,其中0表示1月,11表示12月
var date = now.getDate(); //返回日期月份中的天数(1到31)
var hour = now.getHours(); //返回日期中的小时数(0到23)
var minute = now.getMinutes(); //返回日期中的分钟数(0到59)
var second = now.getSeconds(); //返回日期中的秒数(0到59)
return year + "-" + month + "-" + date + " " + hour + ":" + minute + ":" + second;
}
}
})
</script>

</html>

演示效果

过滤器

  • 常用于文本的格式化, 插值表达式和v-bind属性绑定
  • 格式: 要转换的值 | 方法
  • 知识回顾: 获取字符串中第一个值得方法使用 charAt(索引号)
  • 全局过滤器 在最外面使用Vue.filter() 定义一个全局过滤器
  • 如果全局过滤器和私有过滤器得名称一致, 遵循就近原则

侦听器watch

  • 监听数据得变化, 侦听器本质上一个函数, 要监视那个数据得变化, 就要把数据作为方法名即可. 而且方法中也可以加上参数; 新值在前, 旧值在后
  • 侦听器的格式
  • 方法格式的侦听器 无法在进入页面的时候, 自动触发
  • 对象格式侦听器 在对象中加入immediate属性 将它的值改为true , handler是固定的写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const app = new Vue({
el: '#app',
data: {
username: ""
},
watch: {
// 方法格式侦听器
// username: function (newval, oldval) {
// if (newval.trim().length == 0) return
// $.get('https://www.escook.cn/api/finduser/' + newval, function (e) {
// console.log(e);
// })
// }
// 对象格式
username: {
handler(newval, oldval) {
console.log(newval, oldval);
},
immediate: true
}
}
})

深度侦听deep

  • 在对象格式的侦听器里面加入deep参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const app = new Vue({
el: '#app',
data: {
info: {
username: 'admin'
}
},
watch: {
// info: {
// handler(newval) {
// console.log(newval);
// },
// deep: true
// // 开启深度侦听只要对象中任何一个属性变化了 都会触发对象的侦听器
// }
// 如果要侦听的是对象的子属性的变化,必须包裹一层单引号
'info.username': {
handler(newval) {
console.log(newval);
}
}
}
})

axios

  • 专注于网络请求的库, 在请求到数据之后, 在真正的数据之外套了一层壳
1
config, data, headers, request, status, statusText
  • 如果调用某个方法的返回值是Promise实例则前面可以添加await
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
<div id="app">
<button id="infoGet">Get</button>
<button id="infoPost">Post</button>
</div>
<!-- <script src="../../js/vue.js"></script> -->
<script src="../../js/axios.min.js"></script>
<script>
// axios get事件
document.querySelector('#infoGet').addEventListener('click', async function() {
const {
data: res
} = await axios({
method: "get",
url: 'http://www.liulongbin.top:3006/api/getbooks/'
})
console.log(res);
})
// axios post事件
document.querySelector('#infoPost').addEventListener('click', async function() {
const {
data
} = await axios({
method: "post",
url: 'http://www.liulongbin.top:3006/api/post/',
data: {
name: 'zs',
age: 20
}
})
console.log(data.data);
})
</script>
  • 将axios请求后的返回值进行解构重命名
  • 简写axios.get()和axios.post()类似于ajax中的$.get()方法
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
<div id="app">
<button id="infoGet">Get</button>
<button id="infoPost">Post</button>
</div>
<!-- <script src="../../js/vue.js"></script> -->
<script src="../../js/axios.min.js"></script>
<script>
// axios get事件
document.querySelector('#infoGet').addEventListener('click', async function() {
const {
data: res
} = await axios.get('http://www.liulongbin.top:3006/api/getbooks/', {
params: {
id: 1
}
})
console.log(res);
})
// axios post事件
document.querySelector('#infoPost').addEventListener('click', async function() {
const {
data: res
} = await axios.post('http://www.liulongbin.top:3006/api/post/', {
name: 'zs',
age: 20
})
console.log(res);
})

vue-cli

  • 标准工具
  • 安装配置参考cli.vuejs.org
  • 运行vue create first-project
  • 有两种方法指定渲染区域 el或者使用$mount('#app')

解决样式的冲突

  • 使用属性选择器, 每一个组件中都有一个唯一的属性
  • 在样式中加入scoped属性
  • 如果想修改子组件中的样式在scoped的情况下 使用/deep/

父子组件传值

  • 不建议修改父传过来的值props
  • 子向父传值需要使用自定义事件$emit
  • 数据共享 数据发送方使用$emit, 数据接受方使用$on 构建一个实例对象

生命周期

生命周期

基础知识思维导图

基础知识思维导图

$refs引用

  • 获取DOM元素或者组件的引用
  • 也可以拿到组件调用组件中的方法

$nextTick()方法

  • 当页面重新渲染完成后执行

动态渲染组件

  • 它提供了一个内置的组件component
  • 使用动态绑定is属性, 展示自定义组件
1
2
3
<keep-alive>
<component :is="conTog"></component>
</keep-alive>

keep-alive 方法

  • 让组件保持激活状态 将要保存的组件放到keep-alive标签中, 把包裹的组件进行缓冲, 不会销毁组件
  • 在被缓存和被激活时有着不同的生命周期, 被缓存时deactivated, 被激活时activated, 当组件第一次被创建时执行created生命周期也会执行activated
  • include属性包括需要缓冲的组件, 使用逗号分隔
  • exclude属性排除不会缓冲的组件, 这两个属性不能同时使用
  • 如果组件有自己的注册名称, 在使用exclude, include属性时需要使用组件的注册名称name

插槽slot

  • 占位符, 每个插槽都有一个默认的名称, 具名插槽 name=default
  • 指定某个内容渲染到某个指定的插槽中, 使用v-slot方法, 只能在模板标签或者组件上使用, 不能直接在元素上使用, 不会被渲染到页面上只起到包裹的作用
  • v-slot 语法糖为 #
  • 如果想给插槽定义默认内容, 直接在slot标签中
  • 也可以在插槽上接受slot属性对象, 作用域插槽
  • 作用域插槽的解构赋值

自定义指令(全局和私有)

  • 私有自定义指令的节点 directives
  • 当指令被第一次被绑定到元素上的时候, 会立即触发bind函数
  • 参数为绑定的DOM元素 使用binding.value获取自定义的值
  • bind函数 第一次执行指令时
  • update函数 在DOM更新时会触发
  • bind和update方法相同时可以简写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<h1 v-color="color">App 根组件</h1>
<span v-color="'blue'">你好世界</span>
<script>
// directives: {
// color: {
// bind(el, binding) {
// console.log("打印一次自定义的指令");
// console.log(el, binding);
// el.style.color = binding.value;
// },
// update(el, binding {}
// },
// }
color: function(el, binding) {}
</script>

路由

  • 对应关系, spa单页面
  • 哈希地址和组件的关系
1
2
3
created() {
onhashchange = (res) => this.hash = location.hash.slice(2, 3).toUpperCase() + location.hash.slice(3)
}

vue-router

vue-router

  • 在进行模块化导入的时候, 如果给定的是文件夹, 则默认去调用index.js文件

声明路由链接和占位符

  • 导入Vue-router后直接在页面中加入标签router-view
  • 在routes中创建哈希地址和组件的对应对象
  • 使用router-link替换原来的链接
  • 使用路由重定向{path:"/", redirect:"/home"}

哈希和组件的对应关系

嵌套路由

  • 声明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
const router = new VueRouter({
// 定义哈希地址和组件的对应关系
routes: [{
path: "/",
redirect: '/home'
},
{
path: "/home",
component: Home
},
{
path: "/movie",
component: Movie
},
{
path: "/about",
component: About,
redirect: "/about/tab1",
children: [{
path: "tab1",
component: Tab1
},
{
path: "tab2",
component: Tab2
}
]
}
]
})

默认子路由

  • 某个子路由规则path值为空字符串

动态路由

  • 把hash地址中的可变部分定义为参数项, 使用:id
  • 使用 $route.params 路由参数对象

$route.params

  • 可以为路由规则开启props传参, 获取动态参数的值
  • 在哈希地址中/后面的参数项, 叫做路径参数, 在路由参数对象中需要使用this.$route.params方法获取路径参数, 查询参数使用query获取
  • this.$route中path只是路径部分, fullpath是完整的地址

声明式导航 编程式导航

  • 点击链接的方法叫声明式导航, 调用浏览器API叫编程式导航

vue-router方法

  • this.$router.push('哈希地址') 跳转到指定的hash地址, 并增加一条历史记录
  • this.$router.replace('hash地址') 跳转到指定的地址, 并替换当前的历史地址
  • this.$router.go(n) 实现导航历史的前进后退; $router.back(), $router.forward()分别表示后退到上一个页面, 前进到下一个页面

导航守卫

  • 控制路由的访问权限
  • 创建全局前置守卫
  • 发生了路由的跳转运行后会主动的调用beforeEach指定的回调函数
  • next有三种调用方法, 直接调用next() next(‘/admin’) next(false)

全局前置守卫