列表渲染
列表渲染
v-for指令写在循环项上:v-for=“(一个参数或者两个参数) in/of 要遍历的数组、对象、字符串、指定次数”
- 遍历数组时参数分别是数组中元素(可以是一个对象)和元素所在下标(从0开始)
- 遍历对象时参数分别是属性值和属性名
- 遍历字符串时参数分别是单个字符和字符所在下标(从0开始)
- 遍历次数时参数分别是当前遍历的次数和遍历次数所在的下标(从0开始)
<body>
<div id="app">
<h1>{{msg}}</h1>
<h2>遍历数组</h2>
<!-- 静态列表 -->
<ul>
<li>张三</li>
<li>李四</li>
<li>王五</li>
</ul>
<!--动态列表-->
<ul>
<li v-for="name of names">{{name}}</li>
</ul>
<ul>
<li v-for="(name,index) of names"> {{name}}-{{index}}</li>
</ul>
<h2>遍历对象的属性</h2>
<ul>
<li v-for="(value, propertyName) of user">{{propertyName}},{{value}}</li>
</ul>
<h2>遍历字符串</h2>
<ul>
<li v-for="(c,index) of str">{{index}},{{c}}</li>
</ul>
<h2>遍历指定的次数</h2>
<ul>
<li v-for="(num,index) of counter">{{index}}, {{num}}</li>
</ul>
<!--遍历数组中的对象-->
<table>
<tr>
<th>序号</th>
<th>会员名</th>
<th>年龄</th>
<th>选择</th>
</tr>
<tr v-for="(vip,index) in vips">
<td>{{index+1}}</td>
<td>{{vip.name}}</td>
<td>{{vip.age}}</td>
<td><input type="checkbox"></td>
</tr>
</table>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '列表渲染',
names : ['jack','lucy','james'],
vips : [
{id:'111',name:'jack',age:20},
{id:'222',name:'lucy',age:30},
{id:'333',name:'james',age:40}
],
user : {
id : '111',
name : '张三',
gender : '男'
},
str : '动力节点',
counter : 10
}
})
</script>
v-for的key的作用以及实现原理
Vue框架采用了虚拟Dom机制+diff算法来提高渲染效率, 只有真正改变的dom元素才会重新渲染
- 虚拟dom就是在内存当中的dom对象
- diff算法是一种能够快速的比较两个事物不同之处的算法
新的虚拟dom和旧的虚拟dom比较原则: 根据v-for指令所在的标签的key属性值(key存在于虚拟dom元素中是其的唯一标识)
- 先拿key的属性值进行比较, 如果属性值相同则继续比较子元素(key属性是dom元素的唯一标识)
- 子元素不同:直接将新的虚拟dom元素渲染到页面生成新的真实dom元素
- 子元素相同:直接复用之前的真实dom
- 如果key不同直接将新的虚拟dom元素渲染到页面生成新的真实dom元素
如果没有指定标签的key属性,会自动拿index作为key属性的值, 这种方式效率低复用性差, 另外操作数组当中的非末尾元素时容易发生错乱
- 选中页面上全部的复选框, 此时的真实DOM就是选中时的状态,所以复用时添加的tom也是选中的状态
<body>
<div id="app">
<h1>{{msg}}</h1>
<button @click="addFirst">在数组第一个位置添加 tom</button>
<button @click="addLast">在数组最后位置添加 vue</button>
<table>
<tr>
<th>序号</th>
<th>姓名</th>
<th>邮箱</th>
<th>选择</th>
</tr>
<tr v-for="(vip,index) of vips" :key="index">
<td>{{index + 1}}</td>
<td>{{vip.name}}</td>
<td>{{vip.email}}</td>
<td><input type="checkbox"></td>
</tr>
</table>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'key原理(虚拟dom与diff算法)',
vips : [
{id:'100',name:'jack',email:'jack@123.com'},
{id:'200',name:'lucy',email:'lucy@123.com'},
{id:'300',name:'james',email:'james@123.com'}
]
},
methods : {
addFirst(){
this.vips.unshift({id:'400',name:'tom',email:'tom@123.com'})
},
addLast(){
this.vips.push({id:'500',name:'vue',email:'vue@123.com'})
}
}
})
</script>
</body>
指定对象的id作为key属性的值 , 因为id是唯一的所以不会出现错乱问题
列表过滤和排序
列表过滤
监视文本框中输入的数据,根据用户输入的关键字对原数组进行过滤,将新数组渲染到页面
- 使用watch配置项:需要定义一个数组用来接收过滤后的数组
- 使用computed配置项:不需要定义数组,直接返回过滤后的数组就是计算属性的值
let arr = [1,2,3,4,5,6,7,8,9]
// filter不会破坏原数组的结构,会生成一个全新的数组
let newArr = arr.filter((num) => {
//return 过滤规则
return num < 5
})
console.log(newArr)
<body>
<div id="app">
<h1>{{msg}}</h1>
<input type="text" placeholder="请输入搜索关键字" v-model="keyword">
<table>
<tr>
<th>序号</th>
<th>英雄</th>
<th>能量值</th>
<th>选择</th>
</tr>
<tr v-for="(hero,index) in filteredHeros" :key="hero.id">
<td>{{index+1}}</td>
<td>{{hero.name}}</td>
<td>{{hero.power}}</td>
<td><input type="checkbox"></td>
</tr>
</table>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
keyword : '',
msg : '列表过滤',
heros : [
{id:'101',name:'艾格文',power:10000},
{id:'102',name:'麦迪文',power:9000},
{id:'103',name:'古尔丹',power:8000},
{id:'104',name:'萨尔',power:6000}
],
filteredHeros : []
},
watch : {
// 页面初次加载时就调用handler函数
keyword : {
immediate : true,
handler(val){
this.filteredHeros = this.heros.filter((hero) => {
// 执行过滤规则
return hero.name.indexOf(val) >= 0
})
}
}
}
computed : {
filteredHeros(){
// 返回数组作为计算属性的值
return this.heros.filter((hero) => {
// 执行过滤规则
return hero.name.indexOf(this.keyword) >= 0
})
}
}
})
</script>
</body>
列表排序
let arr = [8,9,5,4,1,2,3]
// sort方法排序之后,不会生成一个新的数组,是在原数组的基础之上进行排序,会影响原数组的结构
arr.sort((a, b) => {
return b - a
})
console.log(arr)
<body>
<div id="app">
<h1>{{msg}}</h1>
<input type="text" placeholder="请输入搜索关键字" v-model="keyword">
<button @click="type = 1">升序</button>
<button @click="type = 2">降序</button>
<button @click="type = 0">原序</button>
<table>
<tr>
<th>序号</th>
<th>英雄</th>
<th>能量值</th>
<th>选择</th>
</tr>
<tr v-for="(hero,index) in filteredHeros" :key="hero.id">
<td>{{index+1}}</td>
<td>{{hero.name}}</td>
<td>{{hero.power}}</td>
<td><input type="checkbox"></td>
</tr>
</table>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
type : 0,
keyword : '',
msg : '列表排序',
heros : [
{id:'101',name:'艾格文',power:10000},
{id:'102',name:'麦迪文',power:9000},
{id:'103',name:'古尔丹',power:8000},
{id:'104',name:'萨尔',power:11000}
]
},
computed : {
filteredHeros(){
// 排序会影响原数组的结构
const arr = this.heros.filter((hero) => {
// 执行过滤规则
return hero.name.indexOf(this.keyword) >= 0
})
// 排序
if(this.type === 1){
// a和b是一个对象
arr.sort((a, b) => {
return a.power - b.power
})
}else if(this.type == 2){
arr.sort((a, b) => {
return b.power - a.power
})
}
// 返回新数组作为计算属性的值
return arr
}
}
})
</script>
</body>
收集表单数据
收集表单数据
阻止表单的默认提交行为的方式
- 给form标签添加@submit.prevent
- 给button按钮添加@click.prevent
<body>
<div id="app">
<h1>{{msg}}</h1>
<!--阻止表单的默认提交行为-->
<form @submit.prevent="send">
<!--v-model.trim去除字符串的前后空白-->
用户名:<input type="text" v-model.trim="user.username"><br><br>
密码:<input type="password" v-model="user.password"><br><br>
<!--v-model.number可以将收集到的字符串转化为数字保存-->
年龄:<input type="number" v-model.number="user.age"><br><br>
性别:
<!--同一组(name属性值相同)的单选按钮才只能选一个-->
男<input type="radio" name="gender" value="1" v-model="user.gender">
女<input type="radio" name="gender" value="0" v-model="user.gender"><br><br>
爱好:
<!--对于checkbox来说,如果没有手动指定value,那么会拿这个标签的checked属性的值作为value(选中为true,未选中为false)-->
旅游<input type="checkbox" v-model="user.interest" value="travel">
运动<input type="checkbox" v-model="user.interest" value="sport">
唱歌<input type="checkbox" v-model="user.interest" value="sing"><br><br>
学历:
<select v-model="user.grade">
<option value="">请选择学历</option>
<option value="zk">专科</option>
<option value="bk">本科</option>
<option value="ss">硕士</option>
</select><br><br>
简介:
<!--标签体当中的内容就是文本域的value-->
<!--v-model.lazy失去焦点后才开始收集表单数据-->
<textarea cols="50" rows="15" v-model.lazy="user.introduce"></textarea><br><br>
<input type="checkbox" v-model="user.accept">阅读并接受协议<br><br>
<!--<button @click.prevent="send">注册</button>-->
<button>注册</button>
</form>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
user : {
username : '',
password : '',
age : '',
// 默认选中指定表单的value值
gender : '1',
// 复选框的value采用数组接收
interest : ['travel'],
grade : 'ss',
introduce : '',
accept : ''
},
msg : '表单数据的收集'
},
methods : {
send(){
alert('ajax...!!!!')
// 将数据收集好,发送给服务器
console.log(JSON.stringify(this.user))
}
}
})
</script>
</body>
过滤器filters
过滤器filters
过滤器适用于简单的逻辑处理,可以进行全局配置或局部配置
- 全局配置:在构建任何Vue实例之前使用Vue.filter(‘过滤器名称’, callback)进行配置,可以在任何一个容器中使用
- 局部配置:在构建Vue实例的配置项中使用filters 进行局部配置,只能在当前绑定的容器中使用
过滤器可以用在插值语法和v-bind指令中,可以对一些数据进行格式化显示
- 过滤器也可以接收额外的参数,但过滤器的第一个参数永远接收的都是前一个过滤器的返回值
- 多个过滤器可以串联{{msg | filterA | filterB | filterC}}
- 由于过滤器的功能完全可以使用methods,computed 来实现,在Vue3当中已经将过滤器语法废弃了
<body>
<!--从服务器端返回一个商品的价格price,如果price的值是''、null、undefined页面上统一显示为 - , 反之显示真实的数字-->
<div id="app">
<h1>{{msg}}</h1>
<h2>商品价格:{{formatPrice}}</h2>
<h2>商品价格:{{formatPrice2()}}</h2>
<h2>商品价格:{{price | filterA | filterB(3)}}</h2>
<input type="text" :value="price | filterA | filterB(3)">
</div>
<!--在另一个容器中使用全局的过滤器-->
<div id="app2">
<h2>商品价格:{{price | filterA | filterB(3)}}</h2>
</div>
<script>
// 配置全局的过滤器可以在另一个容器中使用
Vue.filter('filterA', function(val){
if(val === null || val === undefined || val === ''){
return '-'
}
return val
})
Vue.filter('filterB', function(val, number){
return val.toFixed(number)
})
const vm2 = new Vue({
el : '#app2',
data : {
price : 20.3
}
})
const vm = new Vue({
el : '#app',
data : {
msg : '过滤器',
price : 50.6
},
methods: {
formatPrice2(){
if(this.price === '' || this.price === undefined || this.price === null){
return '-'
}
return this.price
}
},
computed : {
formatPrice(){
if(this.price === '' || this.price === undefined || this.price === null){
return '-'
}
return this.price
}
},
// 局部过滤器
filters : {
filterA(val){
if(val === null || val === undefined || val === ''){
return '-'
}
return val
},
filterB(val, number){
// 确保传递过来的数据val,保留number位小数
return val.toFixed(number)
}
}
})
</script>
</body>