【VUE常用特性】(P29~52)
本文标题中的数字表示B站视频内容对应的分P数。
1.常用特性预览
- 表单操作
- 自定义指令
- 计算属性
- 侦听器
- 过滤器
- 生命周期
2.表单操作
(1)基于Vue的表单操作
之前学的v-model
指令用来实现表单输入域的双向数据绑定
- Input 单行文本
- textarea 多行文本
- select 下拉多选
- radio 单选框
- checkbox 多选框
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
| <!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> <style type="text/css"> form div { height: 40px; line-height: 40px; } form div:nth-child(4) { height: auto; } form div span:first-child { display: inline-block; width: 100px; } </style> </head> <body> <div id="app"> <form action="http://itcast.cn">
<div> <span>姓名:</span> <span> <input type="text" v-model='uname'> </span> </div>
<div> <span>性别:</span> <span> <input type="radio" id="male" value="1" v-model='gender'> <label for="male">男</label> <input type="radio" id="female" value="2" v-model='gender'> <label for="female">女</label> </span> </div>
<div> <span>爱好:</span> <input type="checkbox" id="ball" value="1" v-model='hobby'> <label for="ball">篮球</label> <input type="checkbox" id="sing" value="2" v-model='hobby'> <label for="sing">唱歌</label> <input type="checkbox" id="code" value="3" v-model='hobby'> <label for="code">写代码</label> </div>
<div> <span>职业:</span> <select v-model='occupation'> <option value="0">请选择职业...</option> <option value="1">教师</option> <option value="2">软件工程师</option> <option value="3">律师</option> </select> </div>
<div> <span>个人简介:</span> <textarea v-model='desc'></textarea> </div>
<div> <input type="submit" value="提交" @click.prevent='handle'> </div>
</form> </div>
<script type="text/javascript" src="js/vue.js"></script> <script> var vm = new Vue ({ el: '#app', data: { uname: 'Jack', gender: 1, hobby: ['2','3'], occupation: 2, desc: 'nihao' }, methods: { handle: function(){ console.log(this.uname) console.log(this.gender) console.log(this.hobby.toString()) console.log(this.occupation) console.log(this.desc)
} } }); </script>
</body> </html>
|
(2)表单域修饰符
- number:转化为数值(方便进行计算操作)
- trim:去掉开始和结尾的空格(中间的去不掉)
- lazy : 将input事件切换为change事件
input事件每次输入内容都会被触发
change事件只有当失去焦点时候才会触发(比如验证注册昵称是否重复)
1
| <input v-model.number="age" type="number">
|
3.自定义指令
(1)为何需要自定义指令
除了 Vue 一系列内置的指令 (比如 v-model
或 v-show
) 之外,Vue 还允许你注册自定义的指令去满足你特定的需求。
(2)自定义指令的语法规则
以自定义获取元素焦点
的指令为例:
1 2 3 4 5 6 7
| Vue.directive('focus' { inserted: function(el) { el.focus(); } })
|
上面定义指令的过程中,inserted
是一个钩子函数,规定自定义指令里会触发的一些动作。每个钩子函数有多个钩子函数参数
关于:指令钩子:指令的定义对象提供的几种钩子函数
(3)自定义指令用法
上方定义完指令,如何使用呢?
1
| <input type="text" v-focus>
|
(4)带参数的自定义指令
以改变背景颜色为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Vue.directive('color', { bind: function(el, binding) { el.style.backgroundColor = binding.value.color; } })
var vm = new Vue({ el: '#app', data: { msg: { color: 'orange' } }, methods: { hangle: function(){
} } })
|
其中,bind
也是一个钩子函数,但和刚刚的inserted
有点儿差别
(5)带参数自定义指令用法
1 2 3 4
| <div id="app"> <input type="text" v-color='msg'> </div>
|
(6)自定义局部指令
语法:在Vue实例(组件)的当中添加一个额外的对象directives
,用于放置我们的局部指令。也就是在el
、data
、methods
后面加上directives:
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
| var vm = new Vue({ el: '#app', data: { msg: { color: 'orange' } }, methods: { hangle: function(){
} }, directives: { color: { bind: function(el, binding) { el.style.backgroundColor = binding.value.color; } }, focus: { inserted: function(el) { el.focus(); } } } })
|
实际上这种自定义的局部指令
和上面普通的自定义指令,在功能上没有任何变化。区别有:
- 全局指令钩子函数写在Vue实例里面,局部指令写在Vue实例外面了。
- 局部指令局部生效(在本组件中使用),普通自定义指令全局生效。
1 2 3 4
| <div id="app"> <input type="text" v-color='msg'> <input type="text" v-focus> </div>
|
4.计算属性
(1)为何需要计算属性?
比如,想要把{{msg}}
运算符输出的字符串“Hello”前后反转,用原生JS方法:
1 2 3 4 5 6 7 8 9
| <div>{{msg}}</div>
<div>{{msg.split('')}}</div>
<div>{{msg.split('').reverse()}}</div>
<div>{{msg.split('').reverse().join('')}}</div>
|
可以看出来,模板中的数据展示情况相对复杂,代码可读性低
表达式的计算逻辑比较复杂时,使用计算属性可以使模板内容更加简洁
(2)计算属性的用法
在原来的Vue实例中添加一个对象computed
1 2 3 4 5 6 7 8 9 10 11 12 13
| var vm = new Vue({ el: '#app', data: { msg: 'Hello' }, computed: { reversedMessage: function () { return this.msg.split('').reverse().join('') } } })
|
页面中使用方法:
1 2 3 4 5
| <div id="app"> <div>{{msg}}</div> <div>{{reversedMessage}}</div> </div>
|
使用了计算属性,我们可以把模板中复杂的计算逻辑抽取出来,这样的话我们的模板就变得更加简单。
(3)计算属性与方法的区别
computed
计算属性:是基于它们的依赖进行缓存的(计算一次结果,利用缓存,下次无需计算)
methods
方法:不存在缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| var vm = new Vue({ el: '#app', data: { msg: 'Hello' }, methods: { reversedMessage: function () { console.log('methods执行了一次') return this.msg.split('').reverse().join('') } }, computed: { reverseString: function () { console.log('computed执行了一次') return this.msg.split('computed').reverse().join('') } } })
|
页面中调用时:
1 2 3 4 5 6 7 8 9
|
<div id="app"> <div>{{msg}}</div> <div>{{reversedMessage()}}</div> <div>{{reversedMessage()}}</div> <div>{{reverseString}}</div> <div>{{reverseString}}</div> </div>
|
控制台显示结果是:
methods:
方法中的reversedMessage()
执行了两次;
computed:
计算属性reverseString
利用缓存只执行了一次。
注意计算属性是有依赖的,如果所依赖的数据发生变化,缓存就失效了,会重新计算
5.侦听器
(1)侦听器的应用场景
用来侦听数据,如果数据变化,就通知侦听器所绑定的方法。
和刚刚的计算属性有相似的地方,就是数据的变化会触发对应的方法。
但是侦听器特有的应用场景是执行异步或开销较大的操作。
- 异步:如Ajax和定时任务
- 开销大:指比较耗时的
(2)侦听器的用法
使用方法也和计算属性差不多,但它引入的属性是watch
:
1 2 3 4 5 6 7 8 9
| watch: { firstName: function(val){ this.fullName = val + this.lastName; }, lastName: function(val) { this.fullName = this.firstName + val; } }
|
(3)案例1(拼接姓名)
- 输入名字和姓氏,显示全名:
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
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="app">
<div> <span>名:</span> <span> <input type="text" v-model='firstName'> </span> </div>
<div> <span>姓:</span> <span> <input type="text" v-model='lastName'> </span> </div>
<div>{{fullName}}</div> </div>
<script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: '#app', data: { firstName: 'Jim', lastName: 'Green', fullName: 'Jim Green' },
watch: { firstName: function(val) { this.fullName = val + ' ' + this.lastName; }, lastName: function(val) { this.fullName = this.firstName + ' ' + val; } } }); </script> </body> </html>
|
其实此例用计算属性也能做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var vm = new Vue({ el: '#app', data: { firstName: 'Jim', lastName: 'Green', }, computed: { fullName: function(){ return this.firstName + ' ' + this.lastName; } } })
|
对于这个场景,其实用计算属性更简单,用侦听器反而麻烦。
但有一些场景,用计算属性是做不到的(异步或开销比较大的操作)。
(4)案例2(验证昵称可用性)
本例展示监听器处理异步的操作,验证用户名的可用性。
一般在注册用户时使用,需求是:
- 输入用户名,
失去焦点
时才验证是否存在,(避免频繁调用接口)
- 如果已经存在,提示重新输入,
- 如果不存在,提示可以使用。
实现方法:
- 通过v-model实现数据绑定
- 需要提供提示信息
- 需要侦听器输入信息的变化
- 使用
.lazy
需要修改触发的事件(需是“失去焦点”时,而不是内容变化时)
lazy是上面刚刚学的表单操作的“表单域修饰符”,将input事件切换为change事件:
input事件每次输入内容都会被触发
change事件只有当失去焦点时候才会触发(比如验证注册昵称是否重复)
6.过滤器
(1)过滤器的作用是什么?
格式化数据,比如将字符串格式化为首字母大写,将日期格式化为指定的格式等
hello
=> Vue过滤器
=> Hello
(2)自定义过滤器
1 2 3 4
| Vue.filter('过滤器名称', function(value){
return 运算结果 })
|
案例:将输入字符串的首字母大写:
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
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="app"> <input type="text" v-model='msg'> <div>{{msg | upper}}</div> <div>{{msg | upper | lower}}</div> <div :abc='msg | upper'>测试数据</div> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript">
Vue.filter('upper', function(val) { return val.charAt(0).toUpperCase() + val.slice(1); });
var vm = new Vue({ el: '#app', data: { msg: '' },
filters: { upper: function(val) { return val.charAt(0).toUpperCase() + val.slice(1); } } }); </script> </body> </html>
|
其中,使用到的原生JS有:
.charAt(0)
可以拿到字符串第一个字符
.toUpperCase()
可以把字符串变成大写
.toLowerCase()
可以把字符串变成小写
.slice(1)
可以从第二个字符截取到最后
(3)过滤器的使用
1 2 3 4 5 6 7 8 9
| <div>{{msg | upper}}</div>
<div>{{msg | upper | lower}}</div>
<div v-bind:id=“id | formatId"></div>
|
(4)局部过滤器
过滤器也支持局部定义,刚刚的案例中已经展示过了
1 2 3 4 5 6 7 8 9 10 11 12 13
| var vm = new Vue({ el: '#app', data: { msg: '' },
filters: { upper: function(val) { return val.charAt(0).toUpperCase() + val.slice(1); } } });
|
(5)带参数的过滤器
1 2 3
| Vue.filter('format', function(value, arg1){
})
|
第一个参数默认是要处理的data数据,所以要从第二个参数开始传起
(6)带参数过滤器的使用
1
| <div>{{date | format(‘yyyy-MM-dd')}}</div>
|
(7)案例:过滤器格式化日期
2018-11-15T09:20:15.004Z
↓
2018-09-27
日期格式化规则:
y:年,
M:年中的月份(1-12),
d:月份中的天(1-31),
h:小时(0-23),
m:分(0-59),
s:秒(0-59),
S:毫秒(0-999),
q:季度(1-4)
将时间格式化为 yyyy-MM-dd
格式
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
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="app"> <div>{{date | format('yyyy-MM-dd hh:mm:ss')}}</div> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript">
Vue.filter('format', function(value, arg) { function dateFormat(date, format) { if (typeof date === "string") { var mts = date.match(/(\/Date\((\d+)\)\/)/); if (mts && mts.length >= 3) { date = parseInt(mts[2]); } } date = new Date(date); if (!date || date.toUTCString() == "Invalid Date") { return ""; } var map = { "M": date.getMonth() + 1, "d": date.getDate(), "h": date.getHours(), "m": date.getMinutes(), "s": date.getSeconds(), "q": Math.floor((date.getMonth() + 3) / 3), "S": date.getMilliseconds() };
format = format.replace(/([yMdhmsqS])+/g, function(all, t) { var v = map[t]; if (v !== undefined) { if (all.length > 1) { v = '0' + v; v = v.substr(v.length - 2); } return v; } else if (t === 'y') { return (date.getFullYear() + '').substr(4 - all.length); } return all; }); return format; } return dateFormat(value, arg); }) var vm = new Vue({ el: '#app', data: { date: new Date() } }); </script> </body> </html>
|
上方过滤器的处理过程用到了正则,可以先不用理解,会用就行
7.生命周期
Vue官网-生命周期图示
(1)主要阶段
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeDestroy
- destroyed
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
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="app"> <div>{{msg}}</div> <button @click='update'>更新</button> <button @click='destroy'>销毁</button> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript">
var vm = new Vue({ el: '#app', data: { msg: '生命周期' }, methods: { update: function(){ this.msg = 'hello'; }, destroy: function(){ this.$destroy(); } },
beforeCreate: function(){ console.log('beforeCreate'); }, created: function(){ console.log('created'); }, beforeMount: function(){ console.log('beforeMount'); }, mounted: function(){ console.log('mounted'); }, beforeUpdate: function(){ console.log('beforeUpdate'); }, updated: function(){ console.log('updated'); }, beforeDestroy: function(){ console.log('beforeDestroy'); }, destroyed: function(){ console.log('destroyed'); } }); </script> </body> </html>
|
(2)Vue实例的产生过程
Vue实例的产生需要经过一个过程,这个过程包含了一系列钩子函数的调用。
选项-生命周期钩子
- 【挂载阶段】beforeCreate :在实例初始化之后,数据观测和事件配置之前被调用。
- 【挂载阶段】created :在实例创建完成后被立即调用。
- 【挂载阶段】beforeMount :在挂载开始之前被调用。
- 【挂载阶段】mounted :el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。
- 【更新阶段】beforeUpdate :数据更新时调用,发生在虚拟DOM打补丁之前。
- 【更新阶段】updated :由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。
- 【销毁阶段】beforeDestroy :实例销毁之前调用。
- 【销毁阶段】destroyed :实例销毁后调用。
在这些钩子函数中,我们最需要关心的是mounted
。
这个函数一旦被触发,就代表着初始化已完成,
代表页面中模板内容存在了,
代表着我们就可以往里面填充数据了。
因此,mounted
最大的应用场景之一,是用于要调用后台接口获取数据,然后把数据填充到模板里边。因为要保证模板已经存在,才能往里面填充数据啊。
特别感谢: