01-Vue2.0

image-20200723170734421

参考资料:

1. 框架

1.1 引入

Vue是一套用于构建用户界面的渐进式框架。架构模式是 mvvm(双向数据绑定)

引入:(vue2)

1
2
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

或者下载vue.js文件(浏览器打开http…js文件,Ctrl +S保存),放在本地路径。

引入:(vue3)

1
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

每个 Vue 应用都需要通过实例化 Vue来实现。

语法格式如下:

1
2
3
var vm = new Vue({
// 选项
})

1.2 原理

  1. 通过 Object.defineProperty 把对象的 property 属性/方法 都转为 getter/setter方法
  2. 如果值有变动,则会通知 Wather 监听器,监听器会触发一系列操作:组件、渲染、函数 操作
  3. 底层原理是通过 虚拟dom树进行diff算法比较,标记不同的dom节点进行最小补丁更新

当把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter

Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

注意:vue3的变化

Object.defineProperty 有以下缺点(无法监听以下内容):

  1. ES6的 Set、Map 变化
  2. Class类型数据
  3. 属性的新加和删除
  4. 数组元素的增加和删除

针对这些缺点,ES6 Proxy都能完美解决,唯一缺点就是对IE不友好,所以vue3如果检测到使用IE(包括IE11都不支持)时,会自动降级为 Object.defineProperty 的数据监听系统。

img

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="box"></div>
<script>
var obj = {}
var box = document.getElementById("box")
// dom拦截, obj.myname 的获取和赋值 都能够拦截到
Object.defineProperty(obj, "myname", {
get() {
console.log("get");
return box.innerHTML
},
set(val) {
console.log("set", val);
box.innerHTML = val
}
})
</script>

image-20251217155425506

2. 响应式渲染

2.1 模版语法

插值

  • {{...}} 双大括号,绑定文本插值,中间也支持三目表达式
  • v-html 渲染html代码,有XSS/CSRF攻击风险,尽可能不要给用户提供该能力使用。
    • 防止XSS,CSRF攻击:①前端过滤 ②后端转义(< > &lt; &gt;) ③给cookie加上属性http
  • 表达式

指令

  • v-bind / :动态绑定属性。简写 :,如 :class 绑定类名,**:src** 绑定路径,支持三目表达式
  • v-if 动态创建/删除。
  • v-show 动态显示/隐藏。(会提前创建出来,并添加样式display:none)
  • v-on / @ 绑定事件。简写 @ ,如 @click=”handleClick()”
  • v-for 遍历
  • v-model 双向绑定表单 input

案例:待办事项

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./lib/vue.js"></script>
</head>
<body>
<div id="box">
<!-- v-model 双向绑定了input框的值 -->
<input type="text" v-model="mytext">{{mytext}}
<button @click="handleAdd()">添加</button>
<ul v-show="dataList.length">
<li v-for="(item, index) in dataList">
{{item}}
<button @click="handleDel(index)">删除</button>
</li>
</ul>
<!-- 数组长度为0时显示 -->
<div v-show="!dataList.length">待办事项空空如也</div>
</div>
<script>
var vm = new Vue({
el: "#box",
data: {
dataList: ["111", "222", "333", "444"],
mytext: ""
},
methods: {
handleAdd() {
console.log("添加值:", this.mytext)
this.dataList.push(this.mytext)
this.mytext = ""
},
handleDel(index) {
console.log("删除索引:", index)
this.dataList.splice(index, 1)
}
}
})
</script>
</body>
</html>

效果:

chrome-capture-2025-12-17

2.2 style | class

vue2与vue3中这两个属性的动态拦截区别:

  • vue3默认就能拦截到属性和值。
  • vue2中需要使用 Vue.set() 去拦截,或者使用 数组的方式去操作。

示例:

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./lib/vue.js"></script>
<style>
.aa {
background: red;
}
.bb {
background: yellow;
}
.cc {
background: green;
}
.dd {
background: blue;
}
</style>
</head>

<body>
<!-- vue3默认就拦截到到属性,如dd样式的动态生效 -->
<div id="box">
<div :class="classObj">动态切换class-1:对象方式</div>
<button @click="handleClick1()">Vue2的后补方案1-对象</button>
<div :class="classArr">动态切换class-1:数组方式</div>
<button @click="handleClick2()">Vue2的后补方案2-数组</button>
<hr>
<div :style="styleObj">动态切换style-1:对象方式</div>
<button @click="handleClick3()">Vue2的后补方案1-对象</button>
<div :style="styleArr">动态切换style-1:数组方式</div>
<button @click="handleClick4()">Vue2的后补方案2-数组</button>
</div>
<script>
var vm = new Vue({
el: "#box",
data: {
classObj: {
aa: true,
bb: true,
cc: false
},
classArr: ["aa", "bb"],
styleObj: {
backgroundColor: "orange"
},
styleArr: [{backgroundColor: "orange"}]
},
methods: {
handleClick1() {
//vue2解决方案:Vue.set(对象, 属性, true) - 即动态拦截属性
Vue.set(this.classObj, "dd", true)
},
handleClick2() {
//vue2解决方案:操作数组
this.classArr.push("dd")
},
handleClick3() {
//vue2解决方案:Vue.set(对象, 属性, true)
Vue.set(this.styleObj, "fontSize", "32px")
},
handleClick4() {
//vue2解决方案:操作数组
this.styleArr.push({fontSize: "40px"})
}
}
})

</script>
</body>

</html>

vue3 书写方式和默认解决属性拦截问题:

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
<!-- vue3 -->
<script src="./lib/vue.global.js"></script>
<div id="box">
{{10+20}}-{{myname}}
<input type="text" v-model="mytext">
<button @click="handleAdd()">添加</button>
<div>{{mytext}}</div>
</div>
<script>
// vue2 初始化
// new Vue({
// el: "#box"
// })

// vue3 初始化
var obj = {
data() { //②函数式 data() {}
return { //③返回对象{}
myname: "jerry",
mytext: "aaa"
}
},
methods: {
handleAdd() {
console.log("add", this.mytext)
}
}
}
Vue.createApp(obj).mount("#box") //①创建并绑定节点
</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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./lib/vue.js"></script>
<style>
*{
margin: 0;
padding: 0;
}
ul{
list-style: none;
display: flex;
}
ul li{
flex: 1;
text-align: center;
}
ul .active{
background: green;
}
</style>
</head>
<body>
<div id="box">
<ul>
<li v-for="(item, index) in dataList" :class="current === index ? 'active' : ''" @click="handleClick(index)">
{{item}}
</li>
</ul>
</div>

<script>
new Vue({
el: "#box",
data: {
dataList: ["首页", "列表", "我的"],
current: 0
},
methods: {
handleClick(index) {
console.log(index)
this.current = index
}
}
})
</script>
</body>
</html>

效果:

chrome-capture-2025-12-17 (1)

2.3 条件渲染 v-if

v-if 后面跟条件表达式

v-else-if 后面跟条件表达式

v-else 无需条件表达式

<template> 包装元素,标签自己本身默认不会创建在页面上,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
<div id="box">
<div v-if="isCreated">111111</div>
<!-- <div v-if="!isCreated">222222</div> -->
<div v-else>222222</div>

<ul>
<li v-for="item in dataList">
{{item.status}}-
<span v-if="item.status === 0">未付款</span>
<span v-else-if="item.status === 1">已付款</span>
<span v-else-if="item.status === 2">已发货</span>
<span v-else>已完成</span>
</li>
</ul>
</div>
<script>
var vm = new Vue({
el: "#box",
data: {
isCreated: true,
dataList: [
{
title: "111",
status: 0
},
{
title: "222",
status: 1
},
{
title: "333",
status: 2
}
]
}
})
</script>

template示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="box">
<template v-if="isCreated">
<div>111111</div>
<div>222222</div>
<div>333333</div>
</template>
</div>
<script>
var vm = new Vue({
el: "#box",
data: {
isCreated: true,
},
})
</script>

2.4 列表渲染

v-for 遍历使用,如 v-for="(item, index) in dataList"

  • in 与 of 没有区别,key / index 键 或 索引没有区别
  • 特殊 v-for="n in 10"

key 属性,用法:key="唯一标识"在v-for遍历时必须给 key 添加一个唯一id字段值,vue diff算法可提升性能

  • 跟踪每个节点的身份,从而复用和重新排序现有元素,利用vue的虚拟dom进行对比和标记和最小补丁更新
  • 理想的key值是每项都有的且唯一的id,如item.id

image-20251219161815156

数组更新检测 - 用于页面动态渲染

  • 使用以下方法修改原数组,即可以检测变动
    • push() / pop() / shift() / unshift() / splice() / sort() / reverse()
  • 使用以下方法不会修改原数组,所以需要重新赋值给原数组,就可以被检测到
    • filter() / concat() / slice() / map()
  • 不能检测以下变动的数组:
    • vm.items[index] = newValue,检测变动解决方案 - vue2中可用(vue3无需解决):
      1. Vue.set(items, index, newValue)
      2. splice(index, 1, newValue)

示例:

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
<div id="box">
<!-- 遍历数组,in与of没有区别 -->
<ul>
<li v-for="(item, index) in dataList">
{{index}}-{{item}}
</li>
</ul>
<!-- 遍历对象,key与index没有区别 -->
<ul>
<li v-for="(item, key) of obj">
{{key}}-{{item}}
</li>
</ul>
<!-- 快速造假数据-临时自用 -->
<ul>
<li v-for="item in 10">{{item}}</li>
</ul>
</div>
<script>
var vm = new Vue({
el: "#box",
data: {
dataList: ["111", "222", "333"],
obj: {
name: "jerry",
age: 20,
location: "china"
}
}
})
</script>

案例:模糊搜索 ×2

解决回删不生效方式一:es5 filter

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
<div id="box">
<input type="text" @input="handleInput()" v-model="inputText">
<ul>
<li v-for="item in dataList" :key="item">
{{item}}
</li>
</ul>
</div>
<script>
new Vue({
el: "#box",
data: {
dataList: ["aaa", "bbb", "ccc", "ddd", "eee", "abc"],
// 配合 es5 filter:为了不破坏原始数据,解决回删时不搜索的问题
sourceList: ["aaa", "bbb", "ccc", "ddd", "eee", "abc"],
inputText: ""
},
methods: {
handleInput() {
console.log("input", this.inputText)
// es5 filter
this.dataList = this.sourceList.filter(item => item.includes(this.inputText))
}
}
})
</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
<div id="box">
<input type="text" v-model="inputText">
<ul>
<!-- -->
<li v-for="item in getList()" :key="item">
{{item}}
</li>
</ul>
<!-- 可以放函数执行,拿到的是函数的返回值 -->
<!-- {{ getList() }} -->
</div>
<script>
new Vue({
el: "#box",
data: {
dataList: ["aaa", "bbb", "ccc", "ddd", "eee", "abc"],
inputText: ""
},
methods: {
getList() {
// filter 不会改变原数组 dataList,所以 v-for 中拿到的数据临时变化,但不影响原数组,因此能够支持回删继续展示原数组内容
return this.dataList.filter(item => item.includes(this.inputText))
}
}
})
</script>

效果:

chrome-capture-2025-12-17 (2)

2.5 事件处理

2.5.1 函数表达式

三种方式:

  1. 监听事件-直接写触发代码
  2. 方法事件处理器-写函数名,不带小括号会默认带 event 事件对象参数,如 handleClick
  3. 内联处理器方法-执行函数表达式,如 handleClick($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
31
<div id="box">
{{count}}
<button @click="handleAdd1($event, 1, 2, 3)">add-1-函数表达式</button>
<button @click="handleAdd2">add-2-函数名</button>
<button @click="count++">add-3-表达式</button>
<br>
<input type="text" @input="handleInput">
</div>
<script>
var vm = new Vue({
el: "#box",
data: {
count: 1
},
methods: {
// 带小括号时,没有默认形参,如果要拿到 evt 事件对象,固定第一个传 $event
handleAdd1(evt, a, b, c) {
this.count++
console.log(evt.target, a, b, c);
},
// 不带小括号时,可以得到一个默认形参 evt 事件对象
handleAdd2(evt) {
this.count++
console.log(evt.target); //事件源
},
handleInput(evt) {
console.log("input:", evt.target.value)
}
}
})
</script>

2.5.2 事件修饰符

Vue 通过由点 . 符号表示的指令后缀来调用修饰符。

  • .stop - 阻止冒泡
  • .self - 只监听触发元素的事件
  • .prevent - 阻止默认事件,更多用于表单中如提交前校验
  • .capture - 阻止捕获
  • .once - 只触发一次
  • .left - 左键事件
  • .right - 右键事件
  • .middle - 中键滚轮事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 阻止单击事件冒泡 -->
<a @click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form @submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a @click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form @submit.prevent></form>
<!-- 添加事件侦听器时使用事件捕获模式 -->
<div @click.capture="doThis">...</div>
<!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 -->
<div @click.self="doThat">...</div>
<!-- click 事件只能点击一次,2.1.4版本新增 -->
<a @click.once="doThis"></a>

案例:模态框-阻止冒泡

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./lib/vue.js"></script>
<style>
#overlay{
background: rgba(0, 0, 0, 0.6);
width: 100%;
margin: auto;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
#center{
background: #ffff;
border-radius: 5px;
padding-top: 15px;
padding-left: 30px;
padding-bottom: 15px;
width: 290px;
height: 160px;
position: fixed;
margin: auto;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
</style>
</head>
<body>
<div id="box">
<button @click="isShow=true">显示</button>
<!-- 阻止冒泡方案1 overlay: @click.self="isShow=false" -->
<div id="overlay" v-show="isShow" @click.self="isShow=false">
<!-- 阻止冒泡方案2 center: @click.stop -->
<div id="center">
<div>用户名:<input type="text"></div>
<div>密码:<input type="text"></div>
<div><button>登陆</button></div>
</div>
</div>
</div>
<script>
new Vue({
el: "#box",
data: {
isShow: false
}
})
</script>
</body>
</html>

效果:(点击半透明的蒙版区域,窗口才会消失)

image-20251218101723326

2.5.3 按键修饰符

@keyup / @click Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:

1
2
<!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
<input @keyup.13="submit">

记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名:

1
2
<!-- 缩写语法【常用】按下回车键时调用 vm.submit() -->
<input @keyup.enter="submit">

全部常用的按键别名:(没有按键别名的使用 keyCode

  • .enter 回车键
  • .tab TAB切换键
  • .delete (捕获 “删除” 和 “退格” 键)
  • .esc ESC键
  • .space 空格键
  • .up 上箭头
  • .down 下箭头
  • .left 左箭头
  • .right 右箭头
  • .ctrl
  • .alt
  • .shift
  • .meta

组合按键:

1
2
3
4
<p><!-- Alt + C -->
<input @keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>

2.6 表单控件 | 双向绑定

2.6.1 v-model

v-model 指令在表单控件元素上创建双向数据绑定。

image-20251218110521167

  • text / textarea 文本框,绑定字符串
  • checkbox 复选框,单个绑定布尔值,多个绑定数组
  • radio 单选框,绑定字符串类型
  • select 下拉列表框,绑定字符串类型
  • 参考:https://www.runoob.com/vue2/vue-forms.html
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
<div id="box">
{{mytext}}<br>
<textarea v-model="mytext"></textarea><br>
<div>用户名:<input type="text" v-model="username" @input="inputHandle()"></div>
<input type="checkbox" v-model="isRemember">记住用户名 <br>
<input type="submit" value="登陆" @click="submit()">

<div>
<h3>兴趣爱好:</h3>
<label><input type="checkbox" v-model="checkList" value="vue">Vue</label>
<label><input type="checkbox" v-model="checkList" value="react">react</label>
<label><input type="checkbox" v-model="checkList" value="wx">小程序</label>
</div>
{{checkList}}

<div>
<h3>性别:</h3>
<input type="radio" v-model="select" value="man">男
<input type="radio" v-model="select" value="woman">女
</div>
{{select}}
</div>
<script>
var vm = new Vue({
el: "#box",
data: {
mytext: "",
username: localStorage.getItem("username"), // 取出记住的用户名
isRemember: false,
checkList: [], // 双向绑定:记录checkbox的value值到数组中,提交给后端
select: "man"
},
methods: {
inputHandle() {
console.log("有内容输入:", this.username)
this.isRemember = this.username ? true : false
console.log(this.isRemember)
},
submit() {
if (this.isRemember) {
// 存储记住的用户名
localStorage.setItem("username", this.username)
}
}
}
})
</script>

效果:

image-20251218111112041

案例:购物车

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./lib/vue.global.js"></script>
<style>
ul {
list-style: none;
}
li {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 5px;
}
ul img {
width: 100px;
}
</style>
</head>

<body>
<div id="box">
<input type="checkbox" v-model="isAll" @change="handAllClicked">全选
<ul>
<li v-for="(item, index) in datalist" :key="item.id">
<!-- 绑定变量,value要使用 :value -->
<input type="checkbox" v-model="checkList" :value="item" @change="handleItemChecked" :id=`checkbox-${item.id}`>
<!-- 点击图片也可以选中checkbox,将图片包label通过for指向id即可 -->
<label :for=`checkbox-${item.id}`><img :src="item.pic" alt=""></label>

<div>
<div>{{item.name}}</div>
<div style="color:red">¥{{item.price}}</div>
</div>
<div>
<button @click="item.number--" :disabled="item.number == 1">-</button>
<span>{{item.number}}</span>
<button @click="item.number++" :disabled="item.number == item.limit">+</button>
</div>

<div>
<button @click="handelDeleteClick(index, item.id)">删除</button>
</div>
</li>
总金额:<strong style="color: red;">¥{{totalPrice()}}</strong> <br>
{{checkList}}
</ul>
</div>
<script>
// 基于 vue3
var obj = {
data() {
return {
isAll: false,
checkList: [], // 勾选的购物车的数据
datalist: [ // 所有购物车的数据-后端给
{
id: 1,
name: "商品1",
price: 10,
number: 1,
limit: 5, //限购
pic: "https://static.maizuo.com/pc/v5/usr/movie/7a9184547e88c6f6fe1fd0b01eb7d841.jpg?x-oss-process=image/quality,Q_70"
},
{
id: 2,
name: "商品2",
price: 20,
number: 1,
limit: 10, //限购
pic: "https://static.maizuo.com/pc/v5/usr/movie/7a9184547e88c6f6fe1fd0b01eb7d841.jpg?x-oss-process=image/quality,Q_70"
},
{
id: 3,
name: "商品3",
price: 30,
number: 1,
limit: 15, //限购
pic: "https://static.maizuo.com/pc/v5/usr/movie/7a9184547e88c6f6fe1fd0b01eb7d841.jpg?x-oss-process=image/quality,Q_70"
}
]
}
},
methods: {
totalPrice() {
let total = 0
// 累加计算 checkList 数组每一项的价格*数量
this.checkList.forEach(item => {
total += item.price * item.number
});
return total
},
handelDeleteClick(index, id) {
// 关联删除选中的 checkList: 找到唯一id,过滤出不包含id的数组
// var id = this.datalist[index].id // 如果不传id也可以找到id
this.checkList = this.checkList.filter(item => item.id !== id)
// 删除datalist,靠索引 index
this.datalist.splice(index, 1)

// 删除时同步全选与否的状态
this.handleItemChecked()
},
handAllClicked() {
console.log("isAll=", this.isAll)
this.checkList = this.isAll ? this.datalist : []
},
handleItemChecked() {
if (this.checkList.length === this.datalist.length) {
// console.log("全选")
this.isAll = true
} else {
// console.log("全不选")
this.isAll = false
}
}
}
}

var vm = Vue.createApp(obj).mount("#box")
</script>
</body>

</html>

效果:

image-20251218125738841

2.6.2 表单修饰符

v-model.lazy 让input失去焦点才更新数据

v-model.number 让input输入的内容转换为数字类型

v-model.trim 让input输入的内容去除首尾空格

也可以连着用,比如 v-model.number.lazy=”xxx”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div id="box">
<!-- .lazy 让input失去焦点才更新数据 -->
<input type="text" v-model.lazy="mytext">{{mytext}}
<br>
<!-- .number 让input输入的内容转为数字类型 -->
<input type="number" v-model.number="myAge">{{myAge}}
<br>
<!-- .trim 让input输入的内容去除首尾空格 -->
<input type="username" v-model.trim="myUsername">{{myUsername}}
</div>
<script>
var vm = new Vue({
el: "#box",
data: {
mytext: "",
myAge: 0,
myUsername: ""
}
})
</script>

2.7 计算属性 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
<div id="box">
<!-- 此行方式,不推荐,难以维护 -->
<p>{{ myname.substring(0,1).toUpperCase() + myname.substring(1) }}</p>
<!-- 计算属性【只计算一次可重复使用】-有缓存 -->
<p>{{ myComputedName }}</p>
<p>{{ myComputedName }}</p>
<p>{{ myComputedName }}</p>
<!-- 方法会根据调用次数而执行多次 -->
<p>{{ myMethodName() }}</p>
<p>{{ myMethodName() }}</p>
<p>{{ myMethodName() }}</p>
</div>
<script>
var vm = new Vue({
el: "#box",
data: {
myname: "jerry"
},
computed: { // 计算的属性,属性像方法一样写,但是必须要有返回值
myComputedName() {
console.log("computed")
return this.myname.substring(0,1).toUpperCase() + this.myname.substring(1)
}
},
methods: {
myMethodName() {
console.log("method")
return this.myname.substring(0,1).toUpperCase() + this.myname.substring(1)
}
}
})
</script>

案例:模糊搜索-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
<div id="box">
<input type="text" v-model="inputText">
<ul>
<li v-for="item in computedList" :key="item">
{{item}}
</li>
</ul>
</div>
<script>
new Vue({
el: "#box",
data: {
dataList: ["aaa", "bbb", "ccc", "ddd", "eee", "abc"],
inputText: ""
},
computed: {
computedList() {
//console.log("computed")
//如果数据dataList是从ajax获取的,则不好用了 - 只能转为method函数
return this.dataList.filter(item => item.includes(this.inputText))
}
}
})
</script>

效果一样。

购物车实现,使用计算属性:

image-20251218132903771

2.8 监听属性 watch

watch 会实时监听数据变化并改变自身的值

示例:

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
<div id="box">
<input type="text" v-model="inputText">
<ul>
<li v-for="item in dataList" :key="item">
{{item}}
</li>
</ul>
</div>
<script>
new Vue({
el: "#box",
data: {
dataList: ["aaa", "bbb", "ccc", "ddd", "eee", "abc"],
// 配合 es5 filter:为了不破坏原始数据,解决回删时不搜索的问题
sourceList: ["aaa", "bbb", "ccc", "ddd", "eee", "abc"],
inputText: ""
},
watch: {
// inputText 值改变了,就会去做相应的逻辑处理
inputText(newVal) {
console.log("改变了", newVal)
this.dataList = this.sourceList.filter(item => item.includes(this.inputText))
}
}
})
</script>

2.9 区别:methods|computed|watch

methods 方法:事件绑定,逻辑计算,可以不return,没有缓存

computed 计算属性:重视结果,解决模版过重的问题,必须有return,只求结果,有缓存,同步

watch 监听属性:重视过程,监听一个值的改变,不用返回值,异步同步

3. fetch | axios

fetch: 如果项目对浏览器兼容性要求不高,且希望减少依赖,可以选择Fetch API;

axios: 如果需要处理复杂的请求和响应,或者需要支持旧版本的浏览器,Axios是更好的选择。

3.1 fetch

兼容性不好,IE6-11/Edge12-13都不支持,兼容性查询:https://caniuse.com/?search=fetch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// get
fetch("/api").then(res => res.json()).then(data => console.log(data)).catch(err => console.error(err))
// post
fetch("/api", {
method: "post",
headers: {
//"Content-Type": "application/x-www-form-urlencoded" // body: {name=jerry&age=18}
"Content-Type": "application/json" // body: JSON.stringify(data)
},
body: JSON.stringify({
name: "jerry",
age: 18
})
}).then(res => res.json()).then(data => console.log(data)).catch(err => console.error(err))

注意:fetch 请求默认不带cookie,需要设置 fetch(url, {credentials: 'include'})

3.2 axios

axios 一个基于 Promise 的 HTTP 库,可以用在浏览器和 node.js 中(vue2中推荐使用)。

官网:https://www.axios-http.cn/

1
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
1
<script src="./lib/axios.min.js"></script>

处理普通请求:

1
2
3
4
5
6
7
axios.get(url[, config])
axios.post(url[, data[, config]])
axios.delete(url[, config])
axios.head(url[, config])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
axios.request(config)

3.2.1 get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
new Vue({
el: '#app',
data () {
return {
info: null
}
},
mounted () {
axios.get('https://www.xxx.com/try/ajax/json_demo.json')
.then(res => (this.info = res)) // 最终数据在 res.data 中
.catch(err => { // 请求失败处理
console.log(err);
});
}
})

get传参:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 直接在 URL 上添加参数 ID=12345
axios.get('/user?ID=12345')
.then(res => {
console.log(res.data);
})
.catch(err => {
console.log(err);
});

// 也可以通过 params 设置参数:
axios.get('/user', {
params: {
ID: 12345
}
})
.then(res => {
console.log(res.data);
})
.catch(err => {
console.log(err);
});

3.2.2 post

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
new Vue({
el: '#app',
data () {
return {
info: null
}
},
mounted () {
axios.post('https://www.xxx.com/try/ajax/demo_axios_post.php')
.then(res => (this.info = res)) // 最终数据在 res.data 中
.catch(err => { // 请求失败处理
console.log(err);
});
}
})

post传参:

1
2
3
4
5
6
7
8
9
10
axios.post('/user', {
firstName: 'Fred', // 参数 firstName
lastName: 'Flintstone' // 参数 lastName
})
.then(res => {
console.log(res.data);
})
.catch(err => {
console.log(err);
});

3.2.3 并发请求

功能上等价于 Promise.all(),并行执行多个请求。

1
2
axios.all(iterable)
axios.spread(callback)

示例:

1
2
3
// 1. 定义两个待执行的axios请求
const request1 = axios.get('https://jsonplaceholder.typicode.com/todos/1');
const request2 = axios.get('https://jsonplaceholder.typicode.com/todos/2');
1
2
3
4
5
6
7
8
9
10
// 2. 并行执行请求 + 解析结果
axios.all([request1, request2])
.then(axios.spread((res1, res2) => {
// res1 是request1的结果,res2是request2的结果
console.log('请求1结果:', res1.data);
console.log('请求2结果:', res2.data);
}))
.catch(error => {
console.log('请求失败:', error);
});

数组结构 简写,可省略 axios.spread():

1
2
3
4
5
// 2. 并行执行请求 + 数组结构
axios.all([request1, request2])
.then(([res1, res2]) => { // 数组解构,效果和spread一致
console.log(res1.data, res2.data);
});

4. 过滤器(vue2)

| 管道符号将内容传送给过滤器,可以有多个过滤器进行多道工序处理。

  • vue3 中已移除,不支持过滤器。

示例:

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
<div id="box">
<button @click="handleAjax">click-ajax</button>
<ul>
<li v-for="item in datalist" :key="item.filmId">
<!-- 管道符号 | 传送给过滤器 imgFilter -->
<img :src="item.poster | imgFilter1 | imgFilter2" alt="">
{{item.name}}
</li>
</ul>
</div>
<script>
/* 定义过滤器工序1 */
Vue.filter("imgFilter1", (url) => {
url = url.replace(".jpg", ".png")
console.log("filter:", url)
return url
})
/* 定义过滤器工序2 */
Vue.filter("imgFilter2", (url) => {
url = url.replace(".png", ".jpg")
console.log("filter:", url)
return url
})

var vm = new Vue({
el: "#box",
data: {
datalist: []
},
methods: {
handleAjax() {
console.log("handleAjax")
axios.get("http://127.0.0.1:5500/json/maizuo.json")
.then(res => {
console.log(res.data.data.films)
this.datalist = res.data.data.films
}).catch(err => {
console.log(err)
})
}
}
})
</script>

01-Vue2.0
https://janycode.github.io/2018/05/22/04_大前端/04_Vue/01-Vue2.0/
作者
Jerry(姜源)
发布于
2018年5月22日
许可协议