大纲
监听器(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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <!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>Document</title> </head> <body> <div id="app"> <ul> <li>西游记:价格 {{xyjPrice}},数量: <input type="number" v-model="xyjNum"> </li> <li>水浒传:价格 {{shzPrice}},数量: <input type="number" v-model="shzNum"> </li> <li>提示信息:{{msg}}</li> </ul> </div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
<script> let app = new Vue({ el: "#app", data: { xyjPrice: 56, shzPrice: 47, xyjNum: 1, shzNum: 1, msg: '' }, watch: { xyjNum(newVal, oldVal) { if (newVal >= 3) { this.msg = '库存不足'; this.xyjNum = 3; } else { this.msg = ''; } } } }); </script> </body> </html>
|
代码运行效果
上述 HTML 代码的运行效果如下,当输入的西游记下单数量大于等于 3 时,页面会显示 库存不足
的提示信息。
过滤器(filters)
过滤器介绍
过滤器不会改变真正的 data
,而只是改变渲染的结果,并返回过滤后的内容。在很多不同的业务场景下,过滤器都是有用的,比如尽可能保持 API 响应结果的干净,并在前端处理数据的格式。
提示
- 过滤器常用来处理文本格式化的操作
- 过滤器可以用在两个地方:双花括号插值
{{ }}
和 v-bind
指令中
过滤器使用
局部过滤器使用
局部过滤器注册在当前的 Vue 实例中,只有当前 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
| <!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>Document</title> </head> <body> <div id="app"> <table border="1px" cellspacing="0" width="200px"> <tr align="center"> <th>ID</th> <th>姓名</th> <th>性别</th> </tr> <tr align="center" v-for="user in userList"> <td>{{user.id}}</td> <td>{{user.name}}</td> <td>{{user.gender | genderFilters}}</td> </tr> </table> </div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
<script> let app = new Vue({ el: "#app", data: { userList: [ { id: 1, name: 'Jack', gender: 1 }, { id: 2, name: 'Amy', gender: 0 } ] }, filters: { genderFilters(val) { if (val === 1) { return "男"; } else { return "女"; } } } }); </script> </body> </html>
|
代码运行效果
上述 HTML 代码的运行效果如下,当使用局部定义的性别过滤器后,页面渲染后会显示过滤后得到的性别。
全局过滤器使用
全局过滤器注册在全局,可以在当前 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
| <!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>Document</title> </head> <body style="margin: 100px;"> <div id="app"> <table border="1px" cellspacing="0" width="200px"> <tr align="center"> <th>ID</th> <th>姓名</th> <th>性别</th> </tr> <tr align="center" v-for="user in userList"> <td>{{user.id}}</td> <td>{{user.name}}</td> <td>{{user.gender | genderFilters}}</td> </tr> </table> </div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
<script>
Vue.filter('genderFilters', function (val) { if (val === 1) { return "男"; } else { return "女"; } });
let app = new Vue({ el: "#app", data: { userList: [ { id: 1, name: 'Jack', gender: 1 }, { id: 2, name: 'Amy', gender: 0 } ] } }); </script> </body> </html>
|
代码运行效果
上述 HTML 代码的运行效果如下,当使用全局定义的性别过滤器后,页面渲染后会显示过滤后得到的性别。
计算属性(computed)
计算属性介绍
若某些渲染结果是基于已有数据实时计算出来的,那么可以利用 Vue 的计算属性(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 41 42
| <!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>Document</title> </head> <body> <div id="app"> <ul> <li>西游记:价格 {{xyjPrice}},数量: <input type="number" v-model="xyjNum"> </li> <li>水浒传:价格 {{shzPrice}},数量: <input type="number" v-model="shzNum"> </li> <li>总价:{{totalPrice}}</li> </ul> </div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
<script> let app = new Vue({ el: "#app", data: { xyjPrice: 56, shzPrice: 47, xyjNum: 1, shzNum: 1 }, computed: { totalPrice() { return this.xyjPrice * this.xyjNum + this.shzPrice * this.shzNum; } } }); </script> </body> </html>
|
代码运行效果
上述 HTML 代码的运行效果如下,只要 totalPrice
依赖的属性发生了变化,就会重新计算 totalPrice
的值。
组件化
在大型应用开发的时候,页面可以划分成很多部分。往往不同的页面,也会有相同的部分,例如可能会有相同的头部导航。但是如果每个页面都独自开发,这无疑增加了开发的成本。所以一般会把页面的不同部分拆分成独立的组件,然后在不同页面就可以共享这些组件,避免重复开发。在 Vue 里,所有的 Vue 实例都是组件,通常一个应用会以一棵嵌套的组件树的形式来组织(如下图)。例如,可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
全局组件
通过 Vue 的 component()
函数可以定义一个全局组件,component()
函数的第一个参数是组件名称,第二个参数是组件的参数。
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
| <!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>Document</title> </head> <body> <div id="app"> <counter></counter> </div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
<script> Vue.component("counter", { template: `<button v-on:click="count++">点击了 {{count}} 次</button>`, data() { return { count: 1 } } });
let app = new Vue({ el: "#app" }); </script> </body> </html>
|
代码运行效果
上述 HTML 代码的运行效果如下,每点击一次按钮,都会记录显示点击的总次数。
- 组件其实也是一个 Vue 实例,因此它在定义时也会接收
data
、methods
、生命周期函数等 - 与普通 Vue 实例不同的是,组件不会与页面的元素绑定,否则就无法复用了,因此没有
el
属性 - 由于组件渲染需要 HTML 模板,所以增加了
template
属性,值就是 HTML 模板的内容 - 全局组件定义完成后,任何 Vue 实例都可以直接在 HTML 中通过组件名称来使用组件
data
必须是一个函数,不再是一个对象,因此每个实例可以维护一份被返回对象的独立的拷贝。否则重复引用同一个组件时,数据会互相影响
局部组件
通过 Vue 的 components
属性可以定义一个局部组件,components
就是当前 Vue 实例子组件的集合。特别注意,局部组件只能在当前 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
| <!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>Document</title> </head> <body> <div id="app"> <counter></counter> </div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
<script> let app = new Vue({ el: "#app", components: { 'counter': { template: `<button v-on:click="count++">点击了 {{count}} 次</button>`, data() { return { count: 1 } } } } }); </script> </body> </html>
|
代码运行效果
上述 HTML 代码的运行效果如下,每点击一次按钮,都会记录显示点击的总次数。
组件复用
定义好全局组件或局部组件后,在同一个 Vue 实例(页面)中可以任意重复使用多次。
特别注意
注册全局组件或局部组件时,data
必须是一个函数,不再是一个对象,因此每个实例可以维护一份被返回对象的独立的拷贝。否则重复引用同一个组件时,数据会互相影响。
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
| <!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>Document</title> </head> <body> <div id="app"> <counter></counter> <counter></counter> <counter></counter> </div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
<script> Vue.component("counter", { template: `<button v-on:click="count++">点击了 {{count}} 次</button>`, data() { return { count: 1 } } });
let app = new Vue({ el: "#app" }); </script> </body> </html>
|
生命周期和钩子函数
每个 Vue 实例在被创建时都要经过一系列的初始化过程:创建实例,装载模板,渲染模板等等。Vue 为生命周期中的每个状态都设置了钩子函数(监听函数)。每当 Vue 实例处于不同的生命周期时,对应的钩子函数就会被触发调用。
生命周期
生命周期图示
下图展示了 Vue 实例的生命周期。开发者不需要立马弄明白所有的东西,不过随着不断学习和使用,它的参考价值会越来越高。
钩子函数
钩子函数介绍
beforeCreated
:在使用 Vue 时都要进行实例化,因此该函数就是在 Vue 实例化时调用,也可以将它理解为初始化函数比较方便一点,在 Vue 1.0 版本时,这个函数的名字就是 init
created
:在创建 Vue 实例之后进行调用beforeMount
:页面加载完成,没有渲染,例如此时页面还是会显示类似 {{name}}
的内容mounted
:可以将它理解为原生 JS 中的 window.onload=function(){}
,或许也可以理解为 JQuery 中的 $(document).ready(function(){})
,它就是在 DOM 文档渲染完毕之后将要执行的函数,该函数在 Vue 1.0 版本中名字为 compiled
,此时页面中的 {{name}}
已被渲染成 张三
beforeDestroy
:该函数将在销毁实例前进行调用destroyed
:该函数将在销毁实例后进行调用beforeUpdate
:该函数将在组件更新之前调用updated
:该函数将在组件更新之后调用
钩子函数使用
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
| <!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>Document</title> </head> <body> <div id="app"> <span id="num">{{num}}</span> <button v-on:click="num++">点赞</button> <h2> {{name}},有 {{num}} 个人点赞。 </h2> </div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
<script> let app = new Vue({ el: "#app", data: { name: "张三", num: 10 }, methods: { show() { return this.name; }, add() { this.num++; } }, beforeCreate() { console.log("=========beforeCreate============="); console.log("数据模型未加载: " + this.name, this.num); console.log("方法未加载: " + this.show); console.log("html 模板未加载: " + document.getElementById("num")); }, created: function () { console.log("=========created============="); console.log("数据模型已加载: " + this.name, this.num); console.log("方法已加载: " + this.show()); console.log("html 模板已加载: " + document.getElementById("num")); console.log("html 模板未渲染: " + document.getElementById("num").innerText); }, beforeMount() { console.log("=========beforeMount============="); console.log("html 模板未渲染: " + document.getElementById("num").innerText); }, mounted() { console.log("=========mounted============="); console.log("html 模板已渲染: " + document.getElementById("num").innerText); }, beforeUpdate() { console.log("=========beforeUpdate============="); console.log("数据模型已更新: " + this.num); console.log("html 模板未更新: " + document.getElementById("num").innerText); }, updated() { console.log("=========updated============="); console.log("数据模型已更新: " + this.num); console.log("html 模板已更新: " + document.getElementById("num").innerText); } }); </script> </body> </html>
|
代码运行效果
上述 HTML 代码运行后,浏览器控制台的日志输出如下,在页面中每点赞一次,都会记录显示点赞的总次数。