过滤器
过滤器,就是vue允许开发者自定义的文本格式化函数,可以使用在两个地方:输出内容和操作数据中。
定义过滤器的方式有两种。
使用 Vue.filter()
进行全局定义
这种定义方式对当前文档中所有的 Vue 对象都生效,其基本写法为:
1 2 3 4 5 6 7 8
| <p>{{ value|过滤器名 }}</p>
...
Vue.filter("过滤器名", function(value, 参数){ ... return '返回值' })
|
在 vue 对象中通过 filters 属性来定义
这种定义方式只对当前对象 vm 生效,基本写法为
1 2 3 4 5 6 7 8 9
| var vm = new Vue({ el:"#app", data:{}, filters:{ 过滤器名:function(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 30 31 32 33 34 35 36 37 38 39
| <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <p>{{ price }}</p> <p>{{ price.toFixed(2) }}</p> <p>{{ price|RMB }}</p> <!-- 过滤器可以接收参数 --> <p>{{ price|keepdot(3) }}</p> <!-- 可以同时使用多个过滤器 --> <p>{{ price|keepdot(2)|RMB }}</p> <p></p> </div> </body> <script> // 全局过滤器,注意这里的filter后面没有s // Vue.filter("过滤器名称","调用过滤器时执行的函数") Vue.filter('RMB', function (value) { return value + '元'; }); let vm = new Vue({ el: '#app', data: { price: 20.3 }, // 普通过滤器(局部过滤器) filters: { // 注意这里是filters,别忘了加s keepdot(value, num){ return value.toFixed(num); } } }) </script> </html>
|
上面代码执行后的效果为:
过滤器总结
一个数据可以调用多个过滤器,每个过滤器之间使用”|”竖杠隔开。注意过滤器的执行顺序是从左往右执行,所以有可能产生冲突问题。这时候可以尝试调整调用过滤器之间的顺序解决冲突;
过滤器本质上就是一个函数,必须有返回值。否则数据调用了过滤器以后,无法得到处理后的数据结果;
vue1.x 版本时,有内置的过滤器。但是官方认为,过多地封装工具给开发者使用会造成框架本身的臃肿。所以在 vue2.x 版本以后把内置的过滤器废除了;
过滤器本质上来说就是函数,而函数不仅只有一个参数。过滤器也支持多个参数的写法。
计算和侦听属性
计算属性
我们之前学习过字符串反转。如果直接把反转的代码写在元素中,则会使得其他同事在开发时时不易发现数据被调整。vue 提供了一个计算属性(computed),可以让我们把调整 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
| <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <p>{{ str1 }}</p> <p>{{ strRevs }}</p> <input type="text" v-model="str1"> </div> </body> <script> let vm = new Vue({ el: '#app', data: { str1: 'hello' }, // 通过计算属性computed产生一个新的变量给模板使用 computed: { // 计算属性:里面的函数都必须有返回值 strRevs(){ // 这是strRevs: function(){}的简写形式 return this.str1.split('').reverse().join('') } } }) </script> </html>
|
上面代码的运行效果为:
计算属性和过滤器都可以对 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 36 37
| <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> 原价格:{{ price|k(2) }}<br/> 折扣价:{{ sale_price }} </div> </body> <script> let vm = new Vue({ el: '#app', data: { price: 20.3, sale: 0.6, }, // 过滤器 filters: { k(value, num){ // 过滤器是方法,可以任意使用参数 return value.toFixed(num) } }, // 计算属性 computed: { sale_price(){ // 计算属性是属性,不能有参数 let s_price = this.price * this.sale; return s_price.toFixed(2) } }
}) </script> </html>
|
监听属性
侦听属性(watch),也叫监听属性,主要的作用就是为了实时监听 data 中某个数据变量的变化。在监听的变量发生改变时候,执行其他的代码操作。
侦听属性是一个对象,它的键是要监听的对象或者变量,值一般是函数。当侦听的 data 数据发生变化时,会自定执行的对应函数。这个函数在被调用时,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
| <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <p>{{ num }}</p> <button @click="num++">按钮</button> </div> </body> <script> let vm = new Vue({ el: '#app', data: { num: 10 }, watch: { // 函数名必须和要修改的变量同名 num(newval, oldval){ // newval为变化后的新值,oldval为变化前的旧值 // num 发生变化的时候,要执行的代码 // 一般这里写的要么是ajax,要么就是改变其他相关变量的代码 console.log(`num 的数值发生了变化:从 ${oldval} 变成了 ${newval}`) } } }) </script> </html>
|
上面的代码执行的效果为:
在用户输入 url 地址以后,浏览器都干了什么?
- 拼接 url 地址
- 把 url 地址发送到 dns 域名解析服务器(网络世界中,不是通过域名识别计算机,而是 IP)
- 完成 tcp/udp 的程序,把网页内容发送过来
- 读取网页的原始内容
- 把网页的 HTML 代码转换成浏览器识别的 HTML DOM 结构对象
- 把标签的属性逐一完成渲染
- 把 CSS 的代码读取
- 把 CSS 的样式添加到 DOM 结构对象中(DOM树)
- 页面渲染(执行 js 操作)
vue 对象的生命周期
每个 Vue 对象在创建时都要经过一系列的初始化过程。在这个过程中 Vue.js
会自动运行一些叫做生命周期的的钩子函数,我们可以使用这些函数,在对象创建的不同阶段加上我们需要的代码,实现特定的功能。
在这些钩子函数中,需要重点掌握,日后使用较多的有两个:created 和 mounted 方法。
关于 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 52 53 54
| <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <p>{{ msg }}</p> <button @click="msg += 'a'">按钮</button> </div> </body> <script> let vm = new Vue({ el: '#app', data: { msg: '无人修改我', }, beforeCreate () { console.log('beforeCreate,vm 对象尚未创建,msg 的值为:' + this.msg); //undefined,就是说data属性中的值还没有放 this.msg = 'beforeCreate 修改了 msg'; // 这个值会在一会儿对象创建时被覆盖掉 console.log(this.$el.innerHTML); // 因为对象还未创建,所以找不到而报错 }, // 用的居多,一般在这里使用ajax去后端获取数据,然后交给data属性 created () { console.log('created,vm 对象已经创建,设置好对象的控制范围,msg 的值为:' + this.msg); // 无人修改我,也就是data属性中的值已经初始化放到vm对象中 this.msg = 'created 修改了 msg'; console.log(this.$el.innerHTML); // HTML 标签尚未创建,所以依然找不到,报错 }, beforeMount () { console.log('beforeMount,vm 对象尚未把数据加载到页面中,msg 的值为:' + this.msg); console.log(this.$el.innerHTML); // <p>{{ msg }}</p>,HTML标签已经生成,只是尚未载入数据 this.msg = 'beforeMount 修改了 msg' }, // 用的居多,一般在这里使用ajax去后端获取数据然后通过js代码对页面中原来的内容进行更改 mounted () { console.log('mounted,vm 对象已经把数据加载到页面中,msg 的值为:' + this.msg); // this.$el 就是我们创建vm对象时指定的el属性,$el表示当前vue.js所控制的元素#app console.log(this.$el.innerHTML); // <p>beforeMount 修改了 msg</p>,数据已经加载进来 this.msg = 'mounted 修改了 msg' }, // 后面两个简单作为了解吧。需要注意的是,对象初始化创建时,这两个函数也会连通上面的函数一起被执行 beforeUpdate () { console.log('beforeUpdate,vm 对象尚未将更新好的数据显示出来,msg 的值为:' + this.msg); console.log(this.$el.innerHTML); }, updated () { console.log('updated,vm 对象已经更新好的数据显示出来,msg 的值为:' + this.msg); console.log(this.$el.innerHTML); }, }) </script> </html>
|
上面代码执行后,浏览器终端中显示的内容为:
根据上面代码运行结果得出的结论,我们可以在官方的 vue 生命周期图中,加上备注,从而更加清晰了解 vm 对象的创建过程:
vue 的生命周期钩子函数总结:
- 在 vue 使用的过程中,如果要初始化操作,把初始化操作的代码放在 mounted 中执行;
- mounted 阶段就是在 vm 对象已经把 data 数据实现到页面以后。一般页面初始化使用。例如,用户访问页面加载成功以后,就要执行的 ajax 请求;
- 另一个就是 created,这个阶段就是在 vue对象创建以后,把 ajax 请求后端数据的代码放进 created。
阻止事件冒泡和刷新页面
阻止事件冒泡
js 中,子标签事件触发的同时也会导致父标签时间的触发,这种现象被称为事件冒泡。要了解事件冒泡的更多内容,可以参见 JavaScript 的事件冒泡和事件委托。
vue 中,阻止事件冒泡的写法很简单,只需要在标签中绑定的事件后面,加上 .prevent
即可,比如可以这样写:
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="zh-CN"> <head> <meta charset="UTF-8"> <title>vue阻止事件冒泡</title> <script src="js/vue.js"></script> <style> .c1 { height: 400px; width: 800px; background-color: greenyellow; } .c2 { margin: auto; height: 100px; width: 100px; background-color: orange; } .c3 { margin: 50px auto; height: 100px; width: 100px; background-color: deeppink; } </style> </head> <body> <div class="c1" id="app" @click="alert('这里是外层标签 c1')"> <div class="c2" @click="alert('这里是未阻止事件冒泡的内层标签 c2')">未阻止事件冒泡</div> <div class="c3" @click.stop="alert('这里是阻止事件冒泡的内层标签 c3')">阻止事件冒泡</div> </div> </body> <script> let vm = new Vue({ el: '#app', methods: { alert (msg) { alert(msg); } }, }) </script> </html>
|
代码执行后的效果为:
阻止刷新页面
button 按钮和 a 标签点击后可能会刷新页面。但是 vue 是不喜欢经常刷新页面的,所以有时需要阻止这些后续步骤的执行。
在 vue 中,可以简单地在标签事件的后面加上 .prevent
阻止刷新页面等后续步骤。
通过代码来表示就是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>vue阻止刷新页面</title> <script src="js/vue.js"></script> </head> <body> <div id="app"> <form action=""> <div><input type="text"></div> <div><input type="submit" value="页面会刷新" @click=""></div> <div><input type="submit" value="阻止页面刷新" @click.prevent=""></div> </form> </div> </body> <script> let vm = new Vue({ el: '#app', }) </script> </html>
|
上面代码运行的效果如下:
综合案例:todolist
我的计划列表。功能:可以对数据中的任务列表进行增删改查操作,可以调整任务的排列顺序。
可以基于下面已经写好样式的 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 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
| <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>todolist</title> <script src="js/vue.js"></script> <style type="text/css"> .list_con{ width:600px; margin:50px auto 0; } .inputtxt{ width:550px; height:30px; border:1px solid #ccc; padding:0px; text-indent:10px; } .inputbtn{ width:40px; height:32px; padding:0px; border:1px solid #ccc; } .list{ margin:0; padding:0; list-style:none; margin-top:20px; } .list li{ height:40px; line-height:40px; border-bottom:1px solid #ccc; }
.list li span{ float:left; }
.list li a{ float:right; text-decoration:none; margin:0 10px; } </style> </head> <body> <div class="list_con"> <h2>To do list</h2> <input type="text" name="" id="txt1" class="inputtxt"> <input type="button" name="" value="增加" id="btn1" class="inputbtn">
<ul id="list" class="list"> <li> <span>学习html</span> <a href="javascript:;" class="up"> ↑ </a> <a href="javascript:;" class="down"> ↓ </a> <a href="javascript:;" class="del">删除</a> </li> <li> <span>学习css</span> <a href="javascript:;" class="up"> ↑ </a> <a href="javascript:;" class="down"> ↓ </a> <a href="javascript:;" class="del">删除</a> </li> <li> <span>学习javascript</span> <a href="javascript:;" class="up"> ↑ </a> <a href="javascript:;" class="down"> ↓ </a> <a href="javascript:;" class="del">删除</a> </li> </ul> </div> </body> </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 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
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>todolist</title> <style type="text/css"> .list_con{ width:600px; margin:50px auto 0; } .inputtxt{ width:550px; height:30px; border:1px solid #ccc; padding:0px; text-indent:10px; } .inputbtn{ width:40px; height:32px; padding:0px; border:1px solid #ccc; } .list{ margin:0; padding:0; list-style:none; margin-top:20px; } .list li{ height:40px; line-height:40px; border-bottom:1px solid #ccc; }
.list li span{ float:left; }
.list li a{ float:right; text-decoration:none; margin:0 10px; } </style> <script src="js/vue.js"></script> </head> <body> <div id="todolist" class="list_con"> <h2>To do list</h2> <input type="text" v-model="message" class="inputtxt"> <input type="button" @click="addItem" value="增加" class="inputbtn"> <ul id="list" class="list"> <li v-for="item,key in dolist"> <span>{{item}}</span> <a @click="upItem(key)" class="up" > ↑ </a> <a @click="downItem(key)" class="down"> ↓ </a> <a @click="delItem(key)" class="del">删除</a> </li> </ul> </div> <script> let vm = new Vue({ el:"#todolist", data:{ message:"", dolist:[ "学习html", "学习css", "学习javascript", ] }, methods:{ addItem(){ if(this.messsage==""){ return false; }
this.dolist.push(this.message); this.message = "" }, delItem(key){ this.dolist.splice(key, 1); }, upItem(key){ if(key==0){ return false; } let result = this.dolist.splice(key,1); this.dolist.splice(key-1,0,result[0]); }, downItem(key){ let result = this.dolist.splice(key, 1); console.log(result); this.dolist.splice(key+1,0,result[0]); } } }) </script> </body> </html>
|
最终的页面效果为: