列表渲染
基本列表
v-for指:用于展示列表数据
语法
<li v-for="(item, index) of items" :key="index">
这里key可以是index,更好的是遍历对象的唯一标识
特点: 可遍历:数组、对象、字符串(用的少)、指定次数(用的少)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>基本列表</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 遍历数组 -->
<h3>人员列表(遍历数组)</h3>
<!--v-for key:每个节点的标识,且唯一 -->
<ul v-for="p in persons" :key="p.id">{{p.name}}-{{p.age}}</ul>
<!-- 语法:(element,index) of array :key='索引值' -->
<ul v-for="(p,index) of persons" :key="index">{{index}},{{p.name}}-{{p.age}}</ul>
<!-- 遍历对象 -->
<!-- 语法:(属性值,属性名称) of 对象 :key='属性值' -->
<ul v-for="(value,k) of car" :key="k">{{k}}-{{value}}</ul>
<!-- 遍历字符串 -->
<!-- 语法:(字符,索引) of 字符串 :key='索引' -->
<ul v-for="(char,index) of str" :key="k">{{index}}-{{char}}</ul>
<!-- 遍历(数值)指定次数 -->
<!-- 语法:(字符,索引) of 字符串 :key='索引' -->
<ul v-for="(number,index) of 5" :key="index">{{index}}-{{number}}</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data:{
persons:[
{id:"001",name:"张三",age:10},
{id:"002",name:"李四",age:20},
{id:"003",name:"王吴",age:33}
],
car:{
name:"奥迪",
price:"70万",
color:"黑色"
},
str:'hello'
}
})
</script>
</html>
key 的作用与原理
reactvue中的key有什么作用?key的内部原理
1. 虚拟DOM中key的作用:key是虚拟DOM中对象的标识,当数据发生变化时,Vue会根据新数据生成新的虚拟DOM,随后Vue进行新虚拟DOM与旧虚拟DOM的差异比较,比较规则如下.
2对比规则
a.旧虚拟DOM中找到了与新虚拟DOM相同的key
ⅰ若虚拟DOM中内容没变, 直接使用之前的真实DOM
ⅱ若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
b.旧虚拟DOM中未找到与新虚拟DOM相同的key, 创建新的真实DOM,随后渲染到到页面
3.用index作为key可能会引发的问题
a若对数据进行逆序添加、逆序删除等破坏顺序操作,会产生没有必要的真实DOM更新 ==> 界面效果没问题,但效率低
b若结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题
4开发中如何选择key
a最好使用每条数据的唯一标识作为key,比如 id、手机号、身份证号、学号等唯一值
b如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表,使用index作为key是没有问题的
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>基本列表</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<button @click.once='add'>添加老刘</button>
<!-- 遍历数组,index出问题-->
<h3>人员列表(遍历数组)</h3>
<ul v-for="(p,index) of persons" :key="index">{{index}},{{p.name}}-{{p.age}}<input type="text"></ul>
<ul v-for="(p,index) of persons" :key="p.id">{{index}},{{p.name}}-{{p.age}}<input type="text"></ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
persons: [
{ id: "001", name: "张三", age: 10 },
{ id: "002", name: "李四", age: 20 },
{ id: "003", name: "王吴", age: 33 }
]
},
methods: {
add() {
const p = { id: '004', name: "老刘", age: 15 }
this.persons.unshift(p)
}
}
})
</script>
</html>
二、列表过滤
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>基本列表</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 遍历数组 -->
<h3>人员列表(遍历数组)</h3>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<ul v-for="(p,index) of filPersons" :key="index">{{p.name}},{{p.age}}-{{p.sex}}</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data:{
keyWord:"",
persons:[
{id:"001",name:"马冬梅",age:19,sex:'女'},
{id:"002",name:"周冬雨",age:20,sex:'女'},
{id:"003",name:"周杰伦",age:21,sex:'男'},
{id:"004",name:"温兆伦",age:22,sex:'男'}
]
},
computed:{
filPersons(){
return this.persons.filter((p)=>{
// indexOf: 没有匹配到返回-1,空串返回0,匹配到返回1
return p.name.indexOf(this.keyWord) !==-1
})
}
}
})
</script>
</html>
列表排序
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>基本列表</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 遍历数组 -->
<button @click="updateMei">更新</button>
<h3>人员列表(遍历数组)</h3>
<ul v-for="(p,index) of persons" :key="index">{{p.name}},{{p.age}}-{{p.sex}}</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data:{
persons:[
{id:"001",name:"马冬梅",age:30,sex:'女'},
{id:"002",name:"周冬雨",age:31,sex:'女'},
{id:"003",name:"周杰伦",age:18,sex:'男'},
{id:"004",name:"温兆伦",age:19,sex:'男'}
]
},
methods: {
updateMei(){
this.persons[0].name = '马老师',
this.persons[0].age = 50,
this.persons[0].sex = '楠'
}
},
})
</script>
</html>
数据监视
更新时的一个问题
this.persons[0] = {id:'001',name:'马老师',age:50,sex:'男'} 更改data数据,Vue不监听,模板不改变
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>基本列表</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 遍历数组 -->
<button @click="updateMei">更新</button>
<h3>人员列表(遍历数组)</h3>
<ul v-for="(p,index) of persons" :key="index">{{p.name}},{{p.age}}-{{p.sex}}</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data:{
persons:[
{id:"001",name:"马冬梅",age:30,sex:'女'},
{id:"002",name:"周冬雨",age:31,sex:'女'},
{id:"003",name:"周杰伦",age:18,sex:'男'},
{id:"004",name:"温兆伦",age:19,sex:'男'}
]
},
methods: {
updateMei(){
this.persons[0].name = '马老师',
this.persons[0].age = 50,
this.persons[0].sex = '楠'
}
},
})
</script>
</html>
模拟数据检测
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>基本列表</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
let data = {
name: "尚硅谷",
address: "北京"
}
// 创建一个监视的实例对象,用于监视data中的属性变化
const obs = new Observer(data)
let vm = {}
vm._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("修改了")
obj[k] = val
}
})
})
}
</script>
</html>
原理
1.vue会监视data中所有层次的数数据。
2.如何监测对象中的数据? 通过setter实现监视,且要在new Vue()时就传入要监测的数据
○对象创建后追加的属性,Vue默认不做响应式处理
○如需给后添加的属性做响应式,请使用如下API
Vue.set(target,propertyName/index,value)
vm.$set(target,propertyName/index,value)
3.如何监测数组中的数据? 通过包裹数组更新元素的方法实现,本质就是做了两件事
a调用原生对应的方法对数组进行更新
b重新解析模板,进而更新页面
4.在Vue修改数组中的某个元素一定要用如下方法, 这几个方法被Vue重写了
push()pop()unshift()shift()splice()sort()reverse()
Vue.set()或vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm或vm的根数据对象(data等)添加属性