8、class与style绑定
8.1、理解
(1)在应用界面中,某个(些)元素的样式是变化的 (2)class/style绑定就是专门用来实现动态样式效果的技术
8.2、绑定class
(1)绑定class样式–字符串写法,适用于:样式的类名不确定,需要动态指定 (2)绑定class样式–数组写法,适用于:要绑定的样式个数不确定、名字也不确定 (3)绑定class样式–对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用
例如:
<style>
.basic {
width: 300px;
height: 60px;
margin: 10px;
border: 1px solid black;
}
.happy {
background-color: pink;
border: 1px solid black;
}
.sad {
background-color: rgb(70, 67, 68);
border: 1px solid black;
}
.wu {
background-color: rgb(236, 3, 42);
border: 1px solid black;
}
.at1 {
background-color: green;
border: 4px solid black;
}
.at2 {
background-color: green;
border: 4px dashed black;
}
.at3 {
background-color: green;
border: 4px solid blue;
}
</style>
<div id="root">
<!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
<div class="basic" :class="classStr">{{name}}</div>
<!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
<div class="basic" :class="classArr">{{name}}</div>
<!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
<div class="basic" :class="classObj">{{name}}</div>
</div>
Vue.config.productionTip = false; //设置为 false 以阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el: "#root",
data: {
name: "test",
classStr: "happy",
classArr: ['at1', 'at2', 'at3'],
classObj: {
at1: true,
at2: false
}
}
})
8.3、style绑定
(1)绑定style样式–对象写法 ,属性名改为小驼峰命名 (2)绑定style样式–数组写法(不常用),数组其中是对象 例如:
<style>
.basic {
width: 300px;
height: 60px;
margin: 10px;
border: 1px solid black;
}
</style>
<div id="root">
<!-- 绑定style样式--对象写法 -->
<div class="basic" :style="styleObj">{{name}}</div>
<!-- 绑定style样式--数组写法(不常用) -->
<div class="basic" :style="styleArr">{{name}}</div>
</div>
const vm = new Vue({
el: "#root",
data: {
name: "test",
styleObj: {
fontSize: '40px',
color: "red",
backgroudColor: "yellow"
},
styleArr: [{
fontSize: '40px',
color: "red"
}, {
backgroudColor: "yellow"
}
]
}
})
9、条件渲染
9.1、v-if
写法:
-
v-if=“表达式”
-
v-else-if=“表达式”
-
v-else 适用于:切换频率较低的场景 特点:不展示的DOM元素直接被删除 注意:v-if可以和v-else-if、v-else一起使用,但要求结构不能被打断
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>条件渲染</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
<h2 v-show="true">Hello,{{name}}!</h2>
<div v-if="n === 1">Angular</div>
<div v-else-if="n === 2">React</div>
<div v-else>Vue</div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data:{
name:'jojo',
n:0
}
})
</script>
</html>
< template>
配合v-if使用:
<template v-if="true">
<h3>你好,{{name}}</h3>
<h3>你好,{{name}}</h3>
<h3>你好,{{name}}</h3>
</template>
< template>
不会渲染到DOM结构中,且只有和v-if
配合使用才可以起到整套删除整套显示的效果。
9.2、v-show
写法:v-show=“表达式” 适用于:切换频率较高的场景 特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉了
使用
v-if
的时,元素可能无法获取到,而使用v-show
一定可以获取到
10、列表渲染
10.1、基本列表
v-for指令:
-
用于展示列表数据
-
语法:
v-for="(item,index) in xxx" :key="xxx"
举例:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>基本列表</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>人员列表(遍历数组)</h2>
<ul>
<li v-for="(p,index) in persons" :key="index">
{{p.name}}-{{p.age}}
</li>
</ul>
<h2>汽车信息(遍历对象)</h2>
<ul>
<li v-for="(value,k) in car" :key="k">
{{k}}-{{value}}
</li>
</ul>
<h2>遍历字符串</h2>
<ul>
<li v-for="(char,index) in str" :key="index">
{{char}}-{{index}}
</li>
</ul>
<h2>遍历指定次数</h2>
<ul>
<li v-for="(number,index) in 5" :key="index">
{{index}}-{{number}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20}
],
car:{
name:'奥迪A8',
price:'70万',
color:'黑色'
},
str:'hello'
}
})
</script>
</body>
</html>
总结:
v-for
指令:
-
用于展示列表数据
-
语法:
<li v-for="(item, index) in xxx" :key="yyy">
,其中key可以是index,也可以是遍历对象的唯一标识 -
可遍历:数组、对象、字符串(用的少)、指定次数(用的少)
10.2、key的原理
我们在上面遍历数组的代码基础上,增加一个需求:点击一个按钮在张三的上面增加一行老刘的信息,并在每一行后面增加一个input框,用这个需求来分析key的原理:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>key的原理</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>人员列表</h2>
<button @click.once="add">添加老刘</button>
<ul>
<li v-for="(p,index) in persons" :key="index">
{{p.name}} - {{p.age}}
<input type="text">
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20}
]
},
methods: {
add(){
const p = {id:'004',name:'老刘',age:40}
this.persons.unshift(p)
}
},
})
</script>
</html>
原理:
-
虚拟DOM中key的作用:key是虚拟DOM中对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
-
对比规则:
-
旧虚拟DOM中找到了与新虚拟DOM相同的key:
-
若虚拟DOM中内容没变, 直接使用之前的真实DOM
-
若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
-
-
旧虚拟DOM中未找到与新虚拟DOM相同的key:创建新的真实DOM,随后渲染到到页面
-
-
用index作为key可能会引发的问题:
若对数据进行逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低 若结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题
-
开发中如何选择key?
最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值 如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表,使用index作为key是没有问题的
10.3、列表过滤
下面用两个例子(分别是用监视和计算属性)来实现列表过滤:
例子一(监视):
<div id="root">
<input type="text" placeholder="请输入名字" v-model="keyword">
<ul>
<li v-for="p in person1" :key="p.id">{{p.name}}-{{p.age}}-{{p.sex}}</li>
</ul>
</div>
const vm = new Vue({
el: "#root",
data: {
keyword: "",
person: [{
id: '001',
name: "马冬梅",
age: 18,
sex: '女'
}, {
id: '002',
name: "周冬雨",
age: 28,
sex: '女'
}, {
id: '003',
name: "周杰伦",
age: 13,
sex: '男'
}, {
id: '004',
name: "温兆伦",
age: 45,
sex: '男'
}, ],
person1: []
},
watch: {
keyword: {
//初始状态页面没有数据显示,即初始状态person1为空,此时让handler先执行一遍,让person1得到数据
immediate: true,
handler(val) {
//filter过滤器 遍历旧数组中的元素后,返回过滤后的新数组
this.person1 = this.person.filter((p) => {
//indexOf 前一个字符串中是否包含有括号里的字符串,不包含返回-1
return p.name.indexOf(val) !== -1
})
}
}
}
})
例子二(计算属性):
<div id="root">
<input type="text" placeholder="请输入名字" v-model="keyword">
<ul>
<li v-for="p in person1" :key="p.id">{{p.name}}-{{p.age}}-{{p.sex}}</li>
</ul>
</div>
const vm = new Vue({
el: "#root",
data: {
keyword: "",
person: [{
id: '001',
name: "马冬梅",
age: 18,
sex: '女'
}, {
id: '002',
name: "周冬雨",
age: 28,
sex: '女'
}, {
id: '003',
name: "周杰伦",
age: 13,
sex: '男'
}, {
id: '004',
name: "温兆伦",
age: 45,
sex: '男'
}, ]
},
computed: {
person1() {
return this.person.filter((p) => {
return p.name.indexOf(this.keyword) !== -1
})
}
}
})
10.4、列表排序
在上面用计算属性的列表过滤的基础上增加一个排序功能:
<div id="root">
<input type="text" placeholder="请输入名字" v-model="keyword">
<button @click="sortType=2">年龄升序</button>
<button @click="sortType=1">年龄降序</button>
<button @click="sortType=0">原顺序</button>
<ul>
<li v-for="p in person1" :key="p.id">{{p.name}}-{{p.age}}-{{p.sex}}</li>
</ul>
</div>
const vm = new Vue({
el: "#root",
data: {
keyword: "",
sortType: 0, //0原顺序,1降序,2升序
person: [{
id: '001',
name: "马冬梅",
age: 18,
sex: '女'
}, {
id: '002',
name: "周冬雨",
age: 28,
sex: '女'
}, {
id: '003',
name: "周杰伦",
age: 13,
sex: '男'
}, {
id: '004',
name: "温兆伦",
age: 45,
sex: '男'
}, ]
},
computed: {
person1() {
const arr = this.person.filter((p) => {
return p.name.indexOf(this.keyword) !== -1
})
//判断是否排序
if (this.sortType) {
arr.sort((a, b) => {
return this.sortType === 1 ? b.age - a.age : a.age - b.age
})
}
return arr
}
}
})
穿插:Vue监测数据的原理
1、更新时的一个问题
<div id="root">
<ul>
<button @click="mdm">更新马冬梅的信息</button>
<li v-for="p in person" :key="p.id">{{p.name}}-{{p.age}}</li>
</ul>
</div>
const vm = new Vue({
el: "#root",
data: {
person: [{
id: '001',
name: "马冬梅",
age: 18,
sex: '女'
}, {
id: '002',
name: "周冬雨",
age: 28,
sex: '女'
}, {
id: '003',
name: "周杰伦",
age: 13,
sex: '男'
}, {
id: '004',
name: "温兆伦",
age: 45,
sex: '男'
}, ]
},
methods: {
mdm() {
this.person[0].name = '马老师'
this.person[0].age = 50
this.person[0].sex = '男'
}
}
})
上述代码实现更新马冬梅的信息:
点击按钮后成功更新:
但是将methods中的方法改为直接修改整个对象元素:
methods: {
mdm() {
this.person[0] = {
id: '001',
name: "马老师",
age: 50,
sex: "男"
}
}
}
这样点击按钮,信息就没有更新,其实Vue无法监测到直接给数组元素赋的值,下面说说Vue监测数据的原理。
2、监测数据的原理
①Vue会监测data中所有层次的数据,就是即使对象中的对象的属性也能递归监测
②如何监测对象中的数据 通过setter实现监测,且要在new Vue时就传入要监测的数据
(1)对象中后追加的属性,Vue默认不做响应式处理
(2)如需要给后添加的属性做响应式,请使用如下API:Vue.set(target,propertyName/index,value)或vm.$set(target,propertyName/index,value)
③如何监测数组中的数据 通过包裹数组更新的方法实现,本质就是做了两件事:
(1)调用原生对应的方法对数组进行更新
(2)重写解析模板,进行更新页面
④在Vue修改数组中的某一个元素不能直接赋值,即不能直接带下标赋值,Vue监测不到,就像上面的问题一样,一定要用如下方法:
(1)使用数组的一些API:push()/pop()/shift()/unshift()/splice()/sort()/reverse()
(2)Vue.set()或vm.$set()
当然可以直接改变整个数组。
特别注意:Vue.set()和vm.$set()不能给vm或vm的根数据对象 添加属性
例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Vue数据监视</title>
<style>
button{
margin-top: 10px;
}
</style>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>学生信息</h1>
<button @click="student.age++">年龄+1岁</button><br/>
<button @click="addSex">添加性别属性,默认值:男</button> <br/>
<button @click="addFriend">在列表首位添加一个朋友</button> <br/>
<button @click="updateFirstFriendName">修改第一个朋友的名字为:张三</button><br/>
<button @click="addHobby">添加一个爱好</button> <br/>
<button @click="updateHobby">修改第一个爱好为:开车</button><br/>
<button @click="removeSmoke">过滤掉爱好中的抽烟</button> <br/>
<h3>姓名:{{student.name}}</h3>
<h3>年龄:{{student.age}}</h3>
<h3 v-if="student.sex">性别:{{student.sex}}</h3>
<h3>爱好:</h3>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">
{{h}}
</li>
</ul>
<h3>朋友们:</h3>
<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 //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
student:{
name:'tom',
age:18,
hobby:['抽烟','喝酒','烫头'],
friends:[
{name:'jerry',age:35},
{name:'tony',age:36}
]
}
},
methods: {
addSex(){
//Vue.set(this.student,'sex','男')
this.$set(this.student,'sex','男')
},
addFriend(){
this.student.friends.unshift({name:'jack',age:70})
},
updateFirstFriendName(){
this.student.friends[0].name = '张三'
},
addHobby(){
this.student.hobby.push('学习')
},
updateHobby(){
this.student.hobby.splice(0,1,'开车')
},
removeSmoke(){
this.student.hobby = this.student.hobby.filter((h)=>{
return h !== '抽烟'
})
}
}
})
</script>
</html>
效果:
每个点击之后
11、收集表单数据
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>收集表单数据</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<form @submit.prevent="demo">
账号:<input type="text" v-model.trim="userInfo.account"> <br/><br/>
密码:<input type="password" v-model="userInfo.password"> <br/><br/>
年龄:<input type="number" v-model.number="userInfo.age"> <br/><br/>
性别:
男<input type="radio" name="sex" v-model="userInfo.sex" value="male">
女<input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br/><br/>
爱好:
学习<input type="checkbox" v-model="userInfo.hobby" value="study">
打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">
<br/><br/>
所属校区:
<select v-model="userInfo.city">
<option value="">请选择校区</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzhen">深圳</option>
<option value="wuhan">武汉</option>
</select>
<br/><br/>
其他信息:
<textarea v-model.lazy="userInfo.other"></textarea> <br/><br/>
<input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.atguigu.com">《用户协议》</a>
<button>提交</button>
</form>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
userInfo:{
account:'',
password:'',
age:0,
sex:'female',
hobby:[],
city:'beijing',
other:'',
agree:''
}
},
methods: {
demo(){
console.log(JSON.stringify(this.userInfo))
}
}
})
</script>
</html>
总结:
-
收集表单数据:
-
若:<input type="text"/>,则v-model收集的是value值,用户输入的内容就是value值
-
若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value属性
-
若:<input type="checkbox"/>
-
没有配置value属性,那么收集的是checked属性(勾选 or 未勾选,是布尔值)
-
配置了value属性:
-
v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
-
v-model的初始值是数组,那么收集的就是value组成的数组
-
-
-
v-model的三个修饰符:
-
lazy:失去焦点后再收集数据
-
number:输入字符串转为有效的数字
-
trim:输入首尾空格过滤