10_列表渲染

87 阅读2分钟

列表渲染

基本列表

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>

image.png

key 的作用与原理

image.png

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>

image.png

列表排序

<!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>

image.png

数据监视

更新时的一个问题

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等)添加属性