计算属性与监视姓名案例
插值语法实现
html
<!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实现
html
<!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
执行
- 初次读取时会执行一次
- 当依赖的数据发生改变时会再次被调用
html
<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中要引起计算时依赖的数据发生改变
简写
javascript
// 简写
fullName(){
return this.firstName+'-'+this.lastName
}
监视属性watch
- 当监视属性变化时,回调函数自动调用,进行相关操作
- 监视的属性必须存在,才能进行监视
- 监视的两种写法:
(1)new Vue时传入watch配置
(2)通过vm.$watch监视
简写
实例
html
<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发生变化时执行handler
handler(newValue,oldValue){
console.log(newValue,oldValue)
}
}
}
})
</script>
html
<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发生变化时执行handler
handler(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一起使用,但要求结构不能被打断
html
<h2 v-if="false">欢迎来到{{name}}</h2>
写法
- v-if="表达式"
- v-else-if="表达式"
- v-else="表达式"
html
<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元素未被移除,仅仅是使用样式隐藏掉
html
<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"
- 可遍历:数组,对象,字符串(用的很少),指定次数(用的很少)
html
<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的原理
html
<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的差异比较,比较规则如下:
列表过滤
html
<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>
列表排序
javascript
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来模拟一个数据监测
html
<!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=obs
function 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来测试
html
<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方法
html
<!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,不会显示在页面上
html
<!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:输入首尾空格过滤
html
<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>
过滤器
html
<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,永远不要在用户提交的内容
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}}的问题
html
<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
- 跳过其所在节点的编译过程
- 可利用它跳过,没有使用指令语法,没有使用插值语法的节点,加快编译
自定义指令
html
<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>