Vue基础语法(二)
1.v-on绑定事件
v-on的使用
缩写:@
参数:event
用法:绑定事件回调函数
1.1 基本使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.box{
width: 100px;
height: 100px;
background-color: orange;
margin-top: 10px;
}
</style>
</head>
<body>
<div id="app">
<!-- 基本使用 -->
<div class="box" v-on:click="divClick"></div>
<!-- 简写 重点掌握 -->
<div class="box" @click="divClick"></div>
<!-- 不推荐 绑定方法的时候也可以写一个表达式 -->
<h2>{{counter}}</h2>
<button @click="add">+1</button>
<button @click="counter--">-1</button>
<!-- 绑定其他事件 -->
<div class="box" @mousemove="divMouse">66</div>
<!-- 绑定多个事件 -->
<div class="box" @mousemove="divMouse" @click="divClick">66</div>
<!-- 一个事件类型绑定多个事件处理函数 -->
<div class="box" @mousemove="divMouse" @click="[divClick,divMouse]">66</div>
<!-- 多个事件2 -->
<div class="box" v-on="{click:divClick,mousemove:divMouse}">88</div>
<div class="box" @="{click:divClick,mousemove:divMouse}">88</div>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data(){
return {
counter:0,
}
},
methods: {
divClick(){
console.log("6666");
},
add(){
this.counter++;
},
divMouse(){
console.log("鼠标移动了");
}
}
});
app.mount("#app");
</script>
</body>
</html>
1.2 参数的传递
你要传递的参数就得在@绑定的时候加上(),就可以将需要传递的参数写入小括号中,不需要就不加
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 默认传递 event -->
<button @click="btn1">按钮1</button>
<!-- 有自己的参数 -->
<button @click="btn2(username,age)">按钮2</button>
<!-- 自己的参数和event我都要 -->
<!-- 明确告知vue需要事件对象 -->
<!-- 手动在添加一个$event -->
<button @click = btn3()(username,age)>按钮3</button>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data(){
return {
username:"alex",
age:18
}
},
methods: {
// 绑定时没有传递任何参数的情况下,event对象会被默认传递进来
btn1(event){
console.log(event.target);
},
btn2(name,age,event){
console.log(name,age);
console.log(event);
},
btn3(){
// console.log(a,b,c);
console.log(e);
return function name(username,age) {
console.log(username,age);
console.log(e);
}
}
}
});
app.mount("#app");
</script>
</body>
</html>
1.3 修饰符
常用的是stop阻止冒泡,剩下的看看文档就好:
地址:https://cn.vuejs.org/guide/essentials/event-handling.html#key-modifiers
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.box{
width: 100px;
height: 100px;
background-color: aquamarine;
}
</style>
</head>
<body>
<div id="app">
<div class="box" @click.self="haha">
<button @click="btn">按钮</button>
</div>
<form action="" @submit.prevent>
<input type="submit" value="提交">
</form>
<a href="https://www.baidu.com" @click.prevent>去往百度</a>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data(){
return {
}
},
methods: {
btn(e){
// e.stopPropagation();
console.log("按钮被点击了");
},
haha(){
console.log("盒子被点击了");
}
}
});
app.mount("#app");
</script>
</body>
</html>
2.Vue的条件渲染
根据某些条件来渲染某些元素/组件
v-if
v-else
v-else if
v-show
2.1 demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 如果cars 为空,整个ul不显示 -->
<ul v-if="cars.length > 0">
<li v-for="good in cars">{{good}}</liv-for>
</ul>
<!-- if不展示的时候展示else的东西 -->
<h2 v-else>🛒为空,快点去淘点东西~~~~</h2>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data(){
return {
cars:["xx","qt","zd","rb"],
}
},
methods: {
}
});
app.mount("#app");
</script>
</body>
</html>
v-if,v-else,v-else if用于根据条件来渲染某一块内容,只有条件为true时才会渲染,这三个指令与if、else、else if类似
2.2 v-if、v-else使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<!-- person 由内容的时候展示人的信息 姓名+年龄 -->
<!-- 为空对象时,不展示 -->
<div v-if="Object.keys(person).length">
<h2>个人信息</h2>
<p>姓名:{{person.name}}</p>
<p>年龄:{{person.age}}</p>
</div>
<div v-else>
<h2>没有输入信息</h2>
<p>请输入个人信息</p>
</div>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
person: {
// name: "lyan",
// age: 18,
},
};
},
methods: {
a(){
JSON.stringify
}
},
});
app.mount("#app");
</script>
</body>
</html>
2.3 v-else-if
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 90分及以上为 1 80分及以上为 2 60及以上80以下为 3 60以下 4-->
<h2 v-if="result >= 90">男神</h2>
<h2 v-else-if="result >= 80">好人</h2>
<h2 v-else-if="result >= 60">正常人</h2>
<h2 v-else>ZD</h2>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
result: 59,
};
},
methods: {},
});
app.mount("#app");
</script>
</body>
</html>
2.4 template
用于控制一组元素,但是又不想使用没有意义的div或者其他元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 我们加了一个div的目的是为了控制多个元素的渲染与否,但是这个div是一个多余的dom结构,这个时候我们呢可以选择使用template来优化 -->
<template v-if="Object.keys(person).length">
<h2 @click="a">个人信息</h2>
<p>姓名:{{person.name}}</p>
<p>年龄:{{person.age}}</p>
</template>
<div v-else>
<h2>没有输入信息</h2>
<p>请输入个人信息</p>
</div>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
person: {
name: "lyan",
age: 18,
},
};
},
methods: {
a(){
console.dir(document.createElement("div"));
}
},
});
app.mount("#app");
</script>
</body>
</html>
2.5 v-show
v-show后面的条件为true的时候展示,为false不展示和if看起来一样
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<button @click="toggle">切换</button>
<img v-show="isShow" src="https://t7.baidu.com/it/u=727460147,2222092211&fm=193&f=GIF" alt="">
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data(){
return {
isShow:true,
}
},
methods: {
toggle(){
this.isShow = !this.isShow;
}
}
});
app.mount("#app");
</script>
</body>
</html>
v-show和v-if的区别:
本质区别:v-show是通过display:none来让元素消失,而v-if直接把整个don结构都干掉了,v-if=-“false”的时候,其对应的原声dom压根都不会渲染
用法的区别:v-show不支持template,v-show不能v-else一起用
实际开发中:如果是频繁切换那就用v-show,如果不频繁就是用v-if
3.列表渲染(掌握)
1.v-for的时候 给它绑定唯一的key
2.v-for的时候不要使用数组的index作为key
真实开发中,拿到一组数据并且需要渲染,这个时候我们不会一个个的去写,而是使用v-for来完成,v-for类似于js的循环,可以遍历一组数据
比如说京东的商品列表
使用v-for来渲染一组数据,就是我们常说的列表渲染
3.1 v-for的基本使用
v-for的基本格式:“item in 数组”
数组通常是来自于data或者prop,或者请求
item是我们起的一个别名,这个别名可以自定义
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<h2>电影列表</h2>
<ul>
<li v-for="moive in moives">
电影名称:{{moive}}
</li>
</ul>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data(){
return {
moives:[' 哆啦A梦','复仇者联盟','蜘蛛侠']
}
},
methods: {
}
});
app.mount("#app");
</script>
</body>
</html>
在我们遍历一个数组的时候,往往可能需要拿到索引来做一些操作
如果在v-for中你需要用到索引的话,v-for=“(item,index) in arr”
注意:item和index的顺序
<ul>
<li v-for="(moive,index) in moives">
电影名称:{{index+1}}-----{{moive}}
</li>
</ul>
实际工作中,我们需要渲染的数组往往会比较复杂
渲染稍微复杂一点的数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<h2>电影列表</h2>
<ul>
<li v-for="(moive,index) in moives">
电影名称:{{index+1}}-----{{moive}}
</li>
</ul>
<h2>商品列表</h2>
<ul>
<li v-for="good in goods">
<p>商品名称:{{good.name}}</p>
<p>商品价格:{{good.price}}</p>
</li>
</ul>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data(){
return {
moives:[' 哆啦A梦','复仇者联盟','蜘蛛侠'],
goods:[
{name:'mbp',price:1999},
{name:'imac',price:2999},
{name:'iphone',price:3999},
]
}
},
methods: {
}
});
app.mount("#app");
</script>
</body>
</html>
3.2 v-for的其他类型
v-for还支持
对象
数字
字符串(所有的可迭代对象)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<ul>
<!-- 只要你写了两个或以上的别名,就必须要小括号 -->
<li v-for="(value,key,index) in info">
{{value}}----{{key}}----{{index}}
</li>
</ul>
<ul>
<!-- num就是每个数字,从1 开始 -->
<li v-for="(num,index) in 10">{{num}}---{{index}}</li>
</ul>
<!-- 遍历字符串,str就是单个字符 -->
<ul>
<li v-for="str in msg">{{str}}</li>
</ul>
// 类似v-if,如果外层的div没有实际的意义,可以用template替换
<template v-for="item in persons">
<p>{{item.name}}</p>
<p>{{item.age}}</p>
<p>{{item.height}}</p>
</template>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
info: {
name: "jack",
age: 18,
height: 1.88,
},
msg: "hello vue",
persons: [
{
name: "jack",
age: 18,
height: 1.88,
},
{
name: "alex",
age: 18,
height: 1.88,
},
{
name: "hy",
age: 18,
height: 1.88,
},
],
};
},
methods: {},
});
app.mount("#app");
</script>
</body>
</html>
3.3 数组的更新检测
vue2和vue3区别,vue2使用类似this.arr[0]="123"是不能触发视图的更新的,因为Object.defineproperty并不能监听一个对象0的访问和修改,但是vue3可以,因为他用的proxy。vue2的解决方案是采用Vue.set()
其实Vue内部将所有的变更数组的方法进行了重写,保证他们对数组的操作能够被监听到。能够触发视图更新
push:尾部添加
pop:尾部删除
unshift:头部添加
shift:头部删除
splice:任意位置 添加/删除/替换
sort:排序
reverse:反转
slice:截取
3.4 v-for中key的作用
官方:
key属性主要用于Vue的虚拟DOM算法,在新旧Vnode对比辨识Vnode;
如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能地尝试就地修改/复用相同类型的元素的算法
使用key时,他会基于key的变化重新排列元素的顺序,并且移除/销毁key不存在的元素。
个人:差量更新,就地复用
认识Vnode
因为还没有学习组件,所以我们暂时理解html元素创建出来的vnode
Virtual Node:虚拟节点:无论是组件还是html元素,在vue中都会解析成一个个的虚拟的节点
Vnode本质就是一个js对象
<div class="title" style="font-size: 30px; color: red">哈哈哈</div>
<script>
const vnode = {
type:"div",
props:{
class:"title",
style:{
'font-size':'30px',
color:"red"
}
},
children:"哈哈哈哈"
}
</script>
vue在解析模版的时候,会把里面的一个个标签都抽象成一个个vnode,实际上我们的页面不只是上述简单的一个div构成,而是许多元素,这些元素被抽象后,最终会形成像真实DOM一样的树结构,这个结构就是VDOM
也就是多个vnode构成的一个相对较大的js对象而已。
插入f案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<button @click="inserFn">插入f</button>
<ul>
<li v-for="item in letters" :key="item">{{item}}</li>
</ul>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data(){
return {
letters:['a','b','c','d','e']
}
},
methods: {
inserFn(){
this.letters.splice(2,0,'f')
}
}
});
app.mount("#app");
</script>
</body>
</html>
我们确定一件事,在本次更新中,button和ul都不用更新,需要更新只是li,但是观察控制台的element会发现点击以后闪烁的li有四个,从插入f开始后续的li都会销毁然后重新创建,如果我们绑定了一个唯一的key,就能够更新过程中只更改一个li
<li v-for="item in letters" :key="item">{{item}}</li>
vue的diff算法对于有key和没有key的虚拟dom到底是这么做的
源码
先来看没有key的操作
有key的时候
有key的diff操作如下
第一步从头开始遍历,调用patch比较,c和f的key不一致会跳出
第二步从尾部开始遍历,比较b和f的key不一致,会跳出
第三步:如果旧节点遍历完了,依然有新节点,那就新增
第四步:如果新节点遍历完毕以后,依然有旧的节点,移除旧节点
第五步:最特殊的情况,中间还有很多未知或者乱序的节点
为什么vue3比vue2还慢,其实就是diff的时候有key的情况下,第五种情况,他会重新遍历一遍旧节点,用map建立一个索引的图结构,目的是为了最大程度的复用旧的节点。