计算属性与监视姓名案例
插值语法实现
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>姓名案例</title><script type="text/javascript" src="../js/vue.js"></script>
</head>
<body><div id="root">姓:<input type="text" v-model="firstName">名:<input type="text" v-model="lastName">姓名:<span>{{firstName}}-{{lastName}}</span></div>
</body>
<script type="text/javascript">Vue.config.productionTip = false;new Vue({el:'#root',data:{firstName:'zhang',lastName:'san'} })
</script>
</html>
methods实现
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>姓名案例</title><script type="text/javascript" src="../js/vue.js"></script>
</head>
<body><div id="root">姓:<input type="text" v-model="firstName">名:<input type="text" v-model="lastName">姓名:<span>{{fullName()}}</span></div>
</body>
<script type="text/javascript">Vue.config.productionTip = false;new Vue({el:'#root',data:{firstName:'zhang',lastName:'san'},methods:{fullName(){return this.firstName+'-'+this.lastName}}})
</script>
</html>
计算属性
定义
要用的属性不存在,要通过已有的属性计算得来
原理
底层借助了Object.defineProperty方法提供的getter和setter
执行
- 初次读取时会执行一次
- 当依赖的数据发生改变时会再次被调用
<body><div id="root">姓:<input type="text" v-model="firstName">名:<input type="text" v-model="lastName">姓名:<span>{{fullName}}</span></div>
</body>
<script type="text/javascript">Vue.config.productionTip = false;Vue.config.devtools = true;const vm = new Vue({el:'#root',// 属性data:{firstName:'zhang',lastName:'san'},// 计算属性// 只调用一次,剩下的根据缓存computed:{fullName:{// get什么时候调用// 1.初次读取fullName时// 2.依赖的数据(firstName,lastName)发生变化时get(){console.log('get方法被调用了');return this.firstName+'-'+this.lastName},set(value){const arr = value.split('-')this.firstName=arr[0]this.lastName=arr[1]}}}})
</script>
优势
与methods实现相比,内部有缓存机制(复用),效率更高,调试方便
其他
- 计算属性最终会出现在vm上,直接读取使用即可
- 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变
简写
// 简写
fullName(){return this.firstName+'-'+this.lastName
}
监视属性watch
- 当监视属性变化时,回调函数自动调用,进行相关操作
- 监视的属性必须存在,才能进行监视
- 监视的两种写法:
(1)new Vue时传入watch配置
(2)通过vm.$watch监视
简写
实例
<script type="text/javascript">Vue.config.productionTip = false;Vue.config.devtools = true;new Vue({el:'#root',data:{isHot:true},computed:{info(){return this.isHot?'炎热':'凉爽'}},methods:{changeWeather(){this.isHot = !this.isHot}},// 配置监听watch:{isHot:{// 初始化时让handler执行一次// immediate:true,// 当isHot发生变化时执行handlerhandler(newValue,oldValue){console.log(newValue,oldValue)}}}})
</script>
<script type="text/javascript">Vue.config.productionTip = false;Vue.config.devtools = true;const vm =new Vue({el:'#root',data:{isHot:true},computed:{info(){return this.isHot?'炎热':'凉爽'}},methods:{changeWeather(){this.isHot = !this.isHot}},// 配置监听// watch:{// isHot:{// // 初始化时让handler执行一次// // immediate:true,// // 当isHot发生变化时执行handler// handler(newValue,oldValue){// console.log(newValue,oldValue)// }// }// }})vm.$watch('isHot',{// 初始化时让handler执行一次// immediate:true,// 当isHot发生变化时执行handlerhandler(newValue,oldValue){console.log(newValue,oldValue)}})
</script>
深度监视
- Vue中的watch默认不检测对象内部值的改变(一层)
- 配置deep:true;可以监测对象内布值改变(多层)
其他
- Vue自身可以监测对象内部值得改变,但Vue提供的watch默认不可以
- 使用watch时根据数据的具体结构,决定是否采用深度监视
watch和computed
区别
- computed完成的功能,watch都能完成
- watch能完成的功能,computed不一定能完成
原则
- 所被Vue管理的函数,最好写成普通函数,这样this指向才是vm或组件实例对象
- 所有不被vue管理的函数(定时器的回调函数,ajax的回调函数等),最好写成回调函数,这样this的指向才是vm或组件实例对象
绑定样式
绑定class样式
写法:class=“xxx” xxx可以是字符串,对象,数组
字符串写法适用于:类名不确定,要动态获取
对象写法适用于:要绑定多个样式,个数确定,名字也不确定
数组写法适用于:要绑定多个样式,个数确定,名字也确定
绑定style样式
:style="{fontSize:xxx}"其中xxx是动态值
:style=“a,b”,其中a,b是样式对象
条件渲染
v-if
适用于:切换频率较低的场景
特点:不展示的DOM元素直接被移除
注意:v-if可以和v-else-if,v-else一起使用,但要求结构不能被打断
<h2 v-if="false">欢迎来到{{name}}</h2>
写法
- v-if=“表达式”
- v-else-if=“表达式”
- v-else=“表达式”
<div v-if="num === 1">As</div><div v-else-if="num === 2">Bs</div><div v-else-if="num === 3">Cs</div>
v-show
适用于:切换频率较高的场景
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
<div id="root"><h2 v-show="false">欢迎来到{{name}}</h2>
</div>
写法
v-show=“表达式”
其他
使用v-if时,元素可能无法获取到,而使用v-show一定可以获取到
template不显示在页面上,只能配合v-if使用
列表渲染
v-for指令
- 用于列表展示数据
- 语法:v-for=“(item,index) in xxx” :key=“yyy”
- 可遍历:数组,对象,字符串(用的很少),指定次数(用的很少)
<body><div id="root"><!-- 遍历数组 --><ul><li v-for="(p,index) in persons" :key="index">{{p.id}}-{{p.name}}-{{p.age}}</li></ul><!-- 遍历对象 --><ul><li v-for="(value,key) of car" :key="key">{{key}}-{{value}}</li></ul><!-- 遍历字符串 --><ul><li v-for="(char,index) of str" :key="index">{{char}}-{{index}}</li></ul><!-- 遍历指定次数 --><ul><li v-for="(number,index) of 5" :key="index">{{index}}-{{number}}</li></ul></div>
</body>
<script type="text/javascript">Vue.config.productionTip = false;new Vue({el:'#root',data:{persons:[{id:'001',name:'aaa',age:18},{id:'002',name:'bbb',age:19},{id:'003',name:'ccc',age:20}],car:{name:'奔驰',price:'100000',color:"black"},str:'hello'}})
</script>
key的原理
<body><div id="root"><!-- 遍历数组 --><button @click.once="add">添加一个</button><ul><li v-for="(p,index) in persons" :key="index">{{p.id}}-{{p.name}}-{{p.age}}</li></ul></div>
</body>
<script type="text/javascript">Vue.config.productionTip = false;new Vue({el:'#root',data:{persons:[{id:'001',name:'aaa',age:18},{id:'002',name:'bbb',age:19},{id:'003',name:'ccc',age:20}],},methods:{add(){const p ={id:'004',name:'ddd',age:21}this.persons.unshift(p)}}})
</script>
遍历列表时key的作用(index作为key)
遍历列表时key的作用(id作为key)
虚拟DOM中key的作用
key是虚拟DOM对象的标识,当状态中的数据发生变化时 ,Vue会根据新数据生成新虚拟DOM,随后Vue进行新虚拟DOM与旧虚拟DOM的差异比较,比较规则如下:
列表过滤
<body><div id="root"><h2>人员列表</h2><input type="text" placeholder="请输入名字" v-model="keyword"><ul><li v-for="(p,index) in persons" :key="index">{{p.id}}-{{p.name}}-{{p.age}}</li></ul></div>
</body>
<script type="text/javascript">Vue.config.productionTip = false;//用watch实现// new Vue({// el:'#root',// data:{// keyword:'',// persons:[// {id:'001',name:'aaa',age:18},// {id:'002',name:'bbb',age:19},// {id:'003',name:'ccc',age:20}// ],// filPerson:[]// },// watch:{// keyword:{// //immediate:true,立即执行handler// immediate:true,// handler(val){// this.filPerson=this.persons.filter((p)=>{// return p.name.indexOf(val)!== -1// })// }// }// }// })//用computed实现new Vue({el:'#root',data:{keyword:'',persons:[{id:'001',name:'aaa',age:18},{id:'002',name:'bbb',age:19},{id:'003',name:'ccc',age:20}],},computed:{filPerson(){return this.persons.filter((p)=>{return p.name.indexOf(this.keyword)!== -1})}}})</script>
列表排序
new Vue({el:'#root',data:{keyword:'',sortType:0,//0:原顺序,1:升序,2:降序persons:[{id:'001',name:'aaa',age:18},{id:'002',name:'bbb',age:19},{id:'003',name:'ccc',age:20}],},computed:{filPerson(){const arr = this.persons.filter((p)=>{return p.name.indexOf(this.keyword)!== -1})//判断下一个是否需要排序if(this.sortType){arr.sort((p1,p2)=>{return this.sortType==1?p1.age-p2.age:p2.age-p1.age})}return arr}}})
监测数据
原理
-
Vue会监视data中所有层次的数据
-
如何监测对象中的数据
(1)对象中后追加的属性,Vue默认不做响应式处理
(2)如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value)
Vue.$set(target,propertyName/index,value -
如何监测数组中的数据
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1)调用原生对应的方法对数组进行更新
(2)重新解析模板,进而更新页面 -
在Vue中修改数组中的某个元素一定要用如下方法:
(1)使用这些API:push(),shift(),unshift(),splice(),sort(),reverse()
(2)Vue.set()或vm. s e t ( ) 特别注意: V u e . s e t ( ) h 和 v m . set() 特别注意:Vue.set()h和vm. set()特别注意:Vue.set()h和vm.set()不能给vm或vm的根数据对象添加属性
对象
不用Vue来模拟一个数据监测
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>模拟数据监测</title>
</head>
<body><script type="text/javascript" >let data={names:'清华大学',address:'北京'}//创建一个监视对象用于监视data中数据的变化const obs =new Observer(data)console.log(obs);//准备一个vm实例对象let vm ={}vm._data=data=obsfunction Observer(obj){//汇总对象中所有的属性形成一个数组const keys =Object.keys(obj)//遍历keys.forEach((k)=>{Object.defineProperty(this,k,{get(){return obj[k]},set(val){console.log(`${k}被改了`);obj[k] = val}})})}</script>
</body>
</html>
有一个问题是,如果data数据里面还包含着一个对象时,这个对象并不能监测到
如果用Vue来测试
<body><div id="root"><h2>学校名称:{{names}}</h2><h2>学校地址:{{address}}</h2></div>
</body>
<script type="text/javascript">Vue.config.productionTip = false;const vm =new Vue({el:'#root',data:{names:'清华大学',address:'北京',student:{name:'tom',age:{rAge:40,sAge:29}},friends:[{name:'jerry',age:30}]}})
</script>
数组
当监测的数据是数组是,vm._data里并没有getter和setter方法,所以可以用引起数组发生变化的几个数组API方法
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Vue监测数据改变的原理</title><script type="text/javascript" src="../js/vue.js"></script>
</head>
<body><div id="root"><h2>学校名称:{{names}}</h2><h2>学校地址:{{address}}</h2><!--Vue.set()方法只能修改数组中的元素,不能添加元素,不能删除元素 --><hr/><h1>学生信息</h1><button @click="addSex">添加性别</button><h2>学生姓名:{{student.name}}</h2><h2 v-if="">性别:{{student.sex}}</h2><h2>学生年龄:{{student.age.rAge}},对外{{student.age.sAge}}</h2><h2>朋友们</h2><ul><li v-for="(f,index) in student.friends" :key="index">{{f.name}}--{{f.age}}</li></ul><h2>爱好</h2><ul><li v-for="(h,index) in student.hobby" :key="index">{{h}}</li></ul></div>
</body>
<script type="text/javascript">Vue.config.productionTip = false;const vm =new Vue({el:'#root',data:{names:'清华大学',address:'北京',student:{name:'tom',age:{rAge:40,sAge:29},friends:[{name:'jerry',age:30},{name:'lucy',age:28}],hobby:['电视','读书','玩']},},methods:{addSex(){Vue.set(this.student,'sex','男')}}})
</script>
</html>
Vue.set()
在Vue中如果是undefined,不会显示在页面上
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Vue监测数据改变的原理</title><script type="text/javascript" src="../js/vue.js"></script>
</head>
<body><div id="root"><h2>学校名称:{{names}}</h2><h2>学校地址:{{address}}</h2><!--Vue.set()方法只能修改数组中的元素,不能添加元素,不能删除元素 --><h2>校长:{{headMaster}}</h2><hr/><h1>学生信息</h1><button @click="addSex">添加性别</button><h2>学生姓名:{{student.name}}</h2><h2 v-if="">性别:{{student.sex}}</h2><h2>学生年龄:{{student.age.rAge}},对外{{student.age.sAge}}</h2><h2>朋友们</h2><ul><li v-for="(f,index) in student.friends" :key="index">{{f.name}}--{{f.age}}</li></ul></div>
</body>
<script type="text/javascript">Vue.config.productionTip = false;const vm =new Vue({el:'#root',data:{names:'清华大学',address:'北京',student:{name:'tom',age:{rAge:40,sAge:29},friends:[{name:'jerry',age:30},{name:'lucy',age:28}]},},methods:{addSex(){Vue.set(this.student,'sex','男')}}})
</script>
</html>
收集表单数据
若,则v-model收集的是value值,用户输入的就是value值
若,则v-model收集的是value值,且要给标签配置value值
若
1.没有配置input的value属性,那么收集的就是checked(勾选 or 不勾选,是布尔值)
2.配置input的value属性
(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 不勾选,是布尔值)
(2)v-model的初始值是数组,那么收集的就是value组成的数组
备注: v-model的三个修饰符:
lazy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤
<body><div id="root"><form @submit.prevent="demo">账号:<input type="text" v-model="userInfo.account"><br><br>密码:<input type="password" v-model="userInfo.password"><br><br>性别:男<input type="radio" name="sex" value="male" v-model="userInfo.sex">女<input type="radio" name="sex" value="female" v-model="userInfo.sex"><br><br>爱好:打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">看电影<input type="checkbox" v-model="userInfo.hobby" value="movie">看小说<input type="checkbox" v-model="userInfo.hobby" value="novel"><br><br>所属校区<select v-model="userInfo.city"><option value="">请选择校区</option><option value="gaungzhou">广州校区</option><option value="shenzhen">深圳校区</option><option value="shanghai">上海校区</option></select><br><br>其他信息:<textarea v-model="userInfo.other"></textarea><br><br><input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.atguigu.com">《学员协议》</a><br><br><button>提交</button></form></div>
</body>
<script type="text/javascript">Vue.config.productionTip = false;new Vue({el:'#root',data:{userInfo:{account:'',password:'',sex:'male',hobby:[],city:'guangzhou',other:'',agree:''}},methods:{demo(){console.log(JSON.stringify(this.userInfo));}}})
</script>
过滤器
<body><div id="root"><h2>显示格式化后的时间</h2><!-- 计算属性 --><h3>{{time1}}</h3><!-- methods方法 --><h3>{{time2()}}</h3><!-- 过滤器实现 --><h3>{{time3 | filterTime}}</h3><h3>{{time3 | filterTime('YYYY') | mySlice}}</h3></div>
</body>
<script type="text/javascript">Vue.config.productionTip = false;//全局过滤器Vue.filter('mySlice',function(value){return value.slice(0,4)})new Vue({el:'#root',data:{time:1621561377603},computed:{time1(){return dayjs(tgis.time).format('YYYY-MM-DD HH:mm:ss')}},methods: {time2(){return dayjs(tgis.time).format('YYYY-MM-DD HH:mm:ss')}},// 局部过滤器filters:{filterTime(value,str){return dayjs(value).format(str)},}})
</script>
指令
内置指令
v-text
把所有的内容都当成文本解析
作用:向其所在的节点中渲染文本内容
与插值语法的区别:v-text会替换节点中的内容,{{xx}}则不会
v-html
作用:向指定节点中渲染包含html的结构的内容
与插值语法的区别:
- v-html会替换掉节点中所有内容,{{xx}}则不会
- v-html可以识别html结构
注意:v-html有安全性问题 - 在网站上动态渲染任意html是非常危险的,容易导致xss攻击
- 一定要在可信的内容上使用html,永远不要在用户提交的内容
<body><div id="root"><div>{{name}}</div><div v-html="str"></div><div v-html="str1"></div></div>
</body>
<script type="text/javascript">Vue.config.productionTip = false;new Vue({el:'#root',data:{name:'我是名字',str:'我是字符串',str1:'<span style="color:red">我是字符串</span>'}})
</script>
v-cloak
该指令本身是没有值的
- 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
- 使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题
<style>/* v-cloak指令,用于解决在数据未加载完之前,页面上显示{{}} *//* [v-cloak]{display: none;} */
</style>
<body><div id="root"><h2 v-cloak>{{name}}</h2></div>
</body>
v-once
- v-once所在节点在初次动态渲染后,就视为静态内容了
- 以后结局的改变不会引起v-once所在结构的更新,可以用于优化性能
v-pre
- 跳过其所在节点的编译过程
- 可利用它跳过,没有使用指令语法,没有使用插值语法的节点,加快编译
自定义指令
<body><!-- 需求1:定义一个v-big指令,和v-text指令功能类似,但是将绑定的数据放大10倍需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让绑定的input元素默认获取焦点--><div id="root"><h2>当前的n值是:<span v-text="n"></span></h2><h2>放大10倍后的n值是:<span v-big="n"></span></h2><button @click="n++">点我n+1</button><br/><input type="text" v-fbind:value="n"></div>
</body>
<script type="text/javascript">Vue.config.productionTip = false;new Vue({el:'#root',data:{name:'hello',n:18},directives:{// 何时被调用// 1指令与元素成功绑定时// 2.指令所在的模板被重新解析时big(element,binding){element.innerText = binding.value*10},fbind:{// 指令与元素成功绑定时bind(element,binding){element.value = binding.value},//指令所在元素被插入页面时inserted(element,binding){element.focus()},// 指令所在模板被重新解析时update(element,binding){element.value = binding.value}}}})
</script>