
参考资料:
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 原理
- 通过
Object.defineProperty 把对象的 property 属性/方法 都转为 getter/setter方法
- 如果值有变动,则会通知
Wather 监听器,监听器会触发一系列操作:组件、渲染、函数 操作
- 底层原理是通过
虚拟dom树进行diff算法比较,标记不同的dom节点进行最小补丁更新
当把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。
Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。
注意:vue3的变化
Object.defineProperty 有以下缺点(无法监听以下内容):
- ES6的 Set、Map 变化
- Class类型数据
- 属性的新加和删除
- 数组元素的增加和删除
针对这些缺点,ES6 Proxy都能完美解决,唯一缺点就是对IE不友好,所以vue3如果检测到使用IE(包括IE11都不支持)时,会自动降级为 Object.defineProperty 的数据监听系统。

示例:
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") Object.defineProperty(obj, "myname", { get() { console.log("get"); return box.innerHTML }, set(val) { console.log("set", val); box.innerHTML = val } }) </script>
|

2. 响应式渲染
2.1 模版语法
插值:
{{...}} 双大括号,绑定文本插值,中间也支持三目表达式
v-html 渲染html代码,有XSS/CSRF攻击风险,尽可能不要给用户提供该能力使用。
- 防止XSS,CSRF攻击:①前端过滤 ②后端转义(
< > < >) ③给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>
|
效果:

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>
|
效果:

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

数组更新检测 - 用于页面动态渲染
- 使用以下方法
会修改原数组,即可以检测变动
- push() / pop() / shift() / unshift() / splice() / sort() / reverse()
- 使用以下方法
不会修改原数组,所以需要重新赋值给原数组,就可以被检测到
- filter() / concat() / slice() / map()
- 不能检测以下变动的数组:
- vm.items[index] = newValue,检测变动解决方案 - vue2中可用(vue3无需解决):
- Vue.set(items, index, newValue)
- 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>
|
效果:

2.5 事件处理
2.5.1 函数表达式
三种方式:
- 监听事件-直接写触发代码
- 方法事件处理器-写函数名,不带小括号会默认带 event 事件对象参数,如
handleClick
- 内联处理器方法-执行函数表达式,如
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>
|
效果:(点击半透明的蒙版区域,窗口才会消失)

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 指令在表单控件元素上创建双向数据绑定。

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>
|
效果:

案例:购物车
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>
|
效果:

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>
|
效果一样。
购物车实现,使用计算属性:

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
| fetch("/api").then(res => res.json()).then(data => console.log(data)).catch(err => console.error(err))
fetch("/api", { method: "post", headers: { "Content-Type": "application/json" }, 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)) .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
| axios.get('/user?ID=12345') .then(res => { console.log(res.data); }) .catch(err => { console.log(err); });
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)) .catch(err => { console.log(err); }); } })
|
post传参:
1 2 3 4 5 6 7 8 9 10
| axios.post('/user', { firstName: 'Fred', lastName: 'Flintstone' }) .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
| 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
| axios.all([request1, request2]) .then(axios.spread((res1, res2) => { console.log('请求1结果:', res1.data); console.log('请求2结果:', res2.data); })) .catch(error => { console.log('请求失败:', error); });
|
数组结构 简写,可省略 axios.spread():
1 2 3 4 5
| axios.all([request1, request2]) .then(([res1, res2]) => { console.log(res1.data, res2.data); });
|
4. 过滤器(vue2)
| 管道符号将内容传送给过滤器,可以有多个过滤器进行多道工序处理。
示例:
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>
|