一. 模板语法
1.1. 事件绑定 v-on用法
1.1.1. v-on各种写法
- v-on:click="counter++"
- v-on:click
- @click
- 别的事件
- v-on="{click: xxxx}"
- 修饰符stop
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<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">
<!-- 1.完整的写法 -->
<div class="box" v-on:click="divClick"></div>
<!-- 2.语法糖写法 -->
<div class="box" @click="divClick"></div>
<!-- 3.绑定的方法位置,也可以写成一个表达式(不常用,不推荐) -->
<h2>{{counter}}</h2>
<button @click="increment">+1</button>
<button @click="counter++">+1</button>
<!-- 4.绑定其他方法 -->
<div class="box" @mousemove="divMouseMove"></div>
<!-- 5.元素绑定多个事件 -->
<div class="box" @click="divClick" @mousemove="divMouseMove"></div>
<!-- <div class="box" v-on="{click:divClick,mousemove:divMouseMove}"></div> -->
<!-- <div class="box" @="{click:divClick,mousemove:divMouseMove}"></div> -->
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data:option api
data: function () {
return {
message: "Hello Vue",
counter: 0,
};
},
methods: {
divClick() {
console.log("divClick");
},
increment() {
this.counter++;
},
divMouseMove() {
console.log("divMouseMove")
}
},
});
// 2.挂载app
app.mount("#app");
</script>
</body>
</html>
1.1.2. 各种参数方式
-
默认传递 event
-
自定义参数:
- name, age, $event
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 1.默认传递event对象 -->
<button @click="btn1Click">按钮1</button>
<!-- 2.只有自己的参数 -->
<button @click="btn2Click('why',age)">按钮2</button>
<!-- 3. 自己的参数和event对象-->
<!-- 在模板中想要明确的获取event对象:$event -->
<button @click="btn3Click('why',age,$event)">按钮3</button>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data:option api
data: function () {
return {
message: "Hello Vue",
age: 18,
};
},
methods: {
// 1.默认参数:event对象
// 总结:如果在绑定事件的时候,没有传递任何的参数,那么event对象会被默认传递进来
btn1Click(event) {
console.log("btn1Click", event);
},
// 2.明确参数:
btn2Click(name, age) {
console.log("btn2Click", name, age);
},
// 3.明确参数+event对象
btn3Click(name, age, event) {
console.log("btn3Click", name, age, event);
},
},
});
// 2.挂载app
app.mount("#app");
</script>
</body>
</html>
1.1.3. 修饰符
-
v-on支持修饰符,修饰符相当于对事件进行了一些特殊的处理:
- .stop - 调用 event.stopPropagation()
- .prevent - 调用 event.preventDefault()
- .capture - 添加事件侦听器时使用 capture 模式
- .capture - 添加事件侦听器时使用 capture 模式
- .self - 只当事件是从侦听器绑定的元素本身触发时才触发回调
- .{keyAlias} - 仅当事件是从特定键触发时才触发回调
- .once - 只触发一次回调
- .left - 只当点击鼠标左键时触发
- .right - 只当点击鼠标右键时触发
- .middle - 只当点击鼠标中键时触发
- .passive - { passive: true } 模式添加侦听器
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> .box { width: 100px; height: 100px; background-color: orange; } </style> </head> <body> <div id="app"> <div class="box" @click="divClick"> <button @click.stop="btnClick">按钮</button> </div> </div> <script src="../lib/vue.js"></script> <script> // 1.创建app const app = Vue.createApp({ // data:option api data: function () { return { message:"Hello Vue" }; }, methods: { btnClick(event) { // event.stopPropagation(); console.log("btnClick"); }, divClick() { console.log("divClick"); } } }); // 2.挂载app app.mount("#app"); </script> </body> </html>
1.2. 条件渲染
1.2.1. v-if/else/else-if
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<h1 v-if="score > 90">优秀</h1>
<h1 v-else-if="score > 80">良好</h1>
<h1 v-else-if="score >= 60">及格</h1>
<h1 v-else>不及格</h1>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data:option api
data: function () {
return {
score: 40,
};
},
});
// 2.挂载app
app.mount("#app");
</script>
</body>
</html>
1.2.2. template元素
- v-if
- v-for
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<!-- v-if="条件" -->
<template v-if="Object.keys(info).length">
<h2>个人信息</h2>
<ul>
<li>姓名: {{info.name}}</li>
<li>年龄: {{info.age}}</li>
</ul>
</template>
<!-- v-else -->
<template v-else>
<h2>没有输入个人信息</h2>
<p>请输入个人信息后,再进行展示</p>
</template>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data:option api
data() {
return {
info: { name: "why", age: 18 },
};
},
});
// 2.挂载app
app.mount("#app");
</script>
</body>
</html>
1.2.3. v-show
-
if用法区别:
- v-show不能和template结合
- v-else不能结合
-
if的本质区别:
- v-if为false元素会销毁/不存在
- v-show为false元素的display none
-
选择:
- 切换非常频繁使用v-show
- 不频繁 v-if
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
img {
width: 200px;
height: 200px;
}
</style>
</head>
<body>
<div id="app">
<div>
<button @click="toggle">切换</button>
</div>
<div v-show="isShowCode">
<img
src="https://game.gtimg.cn/images/yxzj/web201706/images/comm/floatwindow/wzry_qrcode.jpg"
alt=""
/>
</div>
<div v-if="isShowCode">
<img
src="https://game.gtimg.cn/images/yxzj/web201706/images/comm/floatwindow/wzry_qrcode.jpg"
alt=""
/>
</div>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data:option api
data: function () {
return {
isShowCode: true,
};
},
methods: {
toggle() {
this.isShowCode = !this.isShowCode;
},
},
});
// 2.挂载app
app.mount("#app");
</script>
</body>
</html>
二. 列表渲染
1.1. v-for的基本使用
- item in 数组
- (item, index) in 数组
- (item, index) of 数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.item {
margin-top: 5px;
background-color: orange;
}
.item .title {
color:red
}
</style>
</head>
<body>
<div id="app">
<!-- 1.电影列表进行渲染 -->
<h2>电影列表</h2>
<ul>
<li v-for="movie in movies">{{movie}}</li>
</ul>
<!-- 2.电影列表同时有索引 -->
<ul>
<li v-for="(movie,index) in movies">{{index + 1}} --- {{movie}}</li>
</ul>
<!-- 3.遍历数组复杂数据 -->
<h2>商品列表</h2>
<div class="item" v-for="item in products">
<h3 class="title">商品:{{item.name}}</h3>
<span>价格:{{item.price}}</span>
<span>秒杀:{{item.desc}}</span>
</div>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data:option api
data: function () {
return {
// 1.movies
movies: ["星际穿越", "少年派", "大话西游", "哆啦A梦"],
// 2.数组:存放的时对象
products:[
{id:110,name:"Mackbook",price:9.9,desc:"9.9秒杀,快来抢购!"},
{id:111,name:"iPhone",price:8.8,desc:"9.9秒杀,快来抢购!"},
{id:112,name:"小米电脑",price:9.9,desc:"9.9秒杀,快来抢购!"}
]
};
},
});
// 2.挂载app
app.mount("#app");
</script>
</body>
</html>
1.2. v-for其他的类型
-
对象
- (value, key, index) in obj
-
数字
- item in 10
-
可迭代对象(字符串)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 1.遍历数组 -->
<!-- 2.遍历对象 -->
<ul>
<li v-for="(value,key,index) in info">
{{value}} --- {{key}} --- {{index}}
</li>
</ul>
<!-- 3.遍历字符串(iterable) -->
<ul>
<li v-for="item in message">{{item}}</li>
</ul>
<!-- 4.遍历数字 -->
<ul>
<li v-for="item in 10">{{item}}</li>
</ul>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data:option api
data: function () {
return {
message: "Hello Vue",
movies: [],
info: { name: "why", age: 18, height: 1.88 },
};
},
});
// 2.挂载app
app.mount("#app");
</script>
</body>
</html>
1.3. v-for绑定key属性
1.3.1. VNode/虚拟DOM
-
template元素 -> VNode
-
虚拟DOM作用之一:
- 跨平台
1.3.2. key的作用
-
有key的操作:
- 根据key找到之前的VNode进行复用;
- 没有VNode可以复用, 再创建新的VNode
-
没有key的操作:
- diff算法, 后续VNode复用性就不强
1.3.3. key绑定id
三. Options API
3.1. 计算属性 computed
3.1.1. 复杂数据的处理方式
-
mustache插值语法自己写逻辑
- 缺点一:模板中存在大量的复杂逻辑,不便于维护(模板中表达式的初衷是用于简单的计算)
- 缺点二:当有多次一样的逻辑时,存在重复的代码
- 缺点三:多次使用的时候,很多运算也需要多次执行,没有缓存
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div id="app"> <!-- 插值语法表达式直接进行拼接 --> <!-- 1.拼接名字 --> <h2>{{firstName + " " + lastName}}</h2> <h2>{{firstName}} {{lastName}}</h2> <!-- 2.显示分数等级 --> <h2>{{ score >= 60 ? "及格":"不及格" }}</h2> <!-- 3.反转单词显示文本 --> <h2>{{ message.split(" ").reverse().join(" ") }}</h2> </div> <script src="../lib/vue.js"></script> <script> // 1.创建app const app = Vue.createApp({ // data:option api data: function () { return { // 1.姓名 firstName: "kobe", lastName: "bryant", // 2.分数:及格/不及格 score: 80, // 3.一串文本:对文本中的单词进行反转显示 message:"my name is why" }; }, }); // 2.挂载app app.mount("#app"); </script> </body> </html> -
methods完成逻辑
- 我们事实上先显示的是一个结果,但是都变成了一种方法的调用
- 多次使用方法的时候,没有缓存,也需要多次计算
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div id="app"> <!-- 插值语法表达式直接进行拼接 --> <!-- 1.拼接名字 --> <h2>{{getFullname()}}</h2> <h2>{{getFullname()}}</h2> <h2>{{getFullname()}}</h2> <!-- 2.显示分数等级 --> <h2>{{ getScoreLevel() }}</h2> <!-- 3.反转单词显示文本 --> <h2>{{ reverseMessage() }}</h2> </div> <script src="../lib/vue.js"></script> <script> // 1.创建app const app = Vue.createApp({ // data:option api data: function () { return { // 1.姓名 firstName: "kobe", lastName: "bryant", // 2.分数:及格/不及格 score: 80, // 3.一串文本:对文本中的单词进行反转显示 message: "my name is why", }; }, methods: { getFullname() { return this.firstName + " " + this.lastName; }, getScoreLevel() { return this.score >= 60 ? "及格" : "不及格"; }, reverseMessage() { return this.message.split(" ").reverse().join(" "); }, }, }); // 2.挂载app app.mount("#app"); </script> </body> </html>
3.1.2. 计算属性用法
-
computed
- 注意:计算属性看起来像是一个函数,但是我们在使用的时候不需要加()
- 我们会发现无论是直观上,还是效果上计算属性都是更好的选择
- 并且计算属性是有缓存的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 插值语法表达式直接进行拼接 -->
<!-- 1.拼接名字 -->
<h2>{{ fullname }}</h2>
<h2>{{ fullname }}</h2>
<h2>{{ fullname }}</h2>
<!-- 2.显示分数等级 -->
<h2>{{ scoreLevel }}</h2>
<!-- 3.反转单词显示文本 -->
<h2>{{ reverseMessage }}</h2>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data:option api
data: function () {
return {
// 1.姓名
firstName: "kobe",
lastName: "bryant",
// 2.分数:及格/不及格
score: 80,
// 3.一串文本:对文本中的单词进行反转显示
message: "my name is why",
};
},
computed: {
fullname() {
// 1.计算属性默认对应的是一个函数
return this.firstName + " " + this.lastName;
},
scoreLevel() {
return this.score >= 60 ? "及格" : "不及格";
},
reverseMessage() {
return this.message.split(" ").reverse().join(" ");
},
},
});
// 2.挂载app
app.mount("#app");
</script>
</body>
</html>
3.1.3. computed和methods区别
- computed底层会缓存, 性能更高
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 1.methods -->
<h2>{{getFullname()}}</h2>
<h2>{{getFullname()}}</h2>
<h2>{{getFullname()}}</h2>
<!-- 2.computed -->
<h2>{{fullname}}</h2>
<h2>{{fullname}}</h2>
<h2>{{fullname}}</h2>
<!-- 3.修改name值 -->
<button @click="changeLastName()">修改lastname</button>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data:option api
data: function () {
return {
firstName: "kobe",
lastName: "bryant",
};
},
methods: {
getFullname() {
console.log("getFullname------");
return this.firstName + " " + this.lastName;
},
changeLastName() {
this.lastName = "why";
},
},
computed: {
fullname() {
console.log("computed fullname-----");
return this.firstName + " " + this.lastName;
},
},
});
// 2.挂载app
app.mount("#app");
</script>
</body>
</html>
3.1.4. computed的完整写法
- set
- get
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<h2>{{fullname}}</h2>
<button @click="setFullname">设置fullname</button>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data:option api
data: function () {
return {
firstname: "coder",
lastname: "why",
};
},
computed: {
// 语法糖的写法
// fullname() {
// return this.firstname + " " + this.lastname
// },
// 完整的写法
fullname: {
get: function () {
return this.firstname + " " + this.lastname;
},
set: function (value) {
const names = value.split(" ");
this.firstname = names[0];
this.lastname = names[1];
},
},
},
methods: {
setFullname() {
this.fullname = "kobe bryant";
},
},
});
// 2.挂载app
app.mount("#app");
</script>
</body>
</html>
3.2. 侦听器 watch
3.2.1. 基本侦听watch
-
watch: { message(newValue, oldValue) {} }
-
注意: 对象类型
- Proxy对象 -> Vue.toRaw(newValue)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<h2>{{message}}</h2>
<button @click="changeMessage">修改message</button>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data:option api
data: function () {
return {
message: "Hello Vue",
info: { name: "why", age: 18 },
};
},
methods: {
changeMessage() {
this.message = "你好啊,李银河";
this.info = { name: "kobe" };
},
},
watch: {
// 1.默认有两个参数:newValue/oldValue
message(newValue, oldValue) {
console.log("message数据发生了变化", newValue, oldValue);
},
info(newValue, oldValue) {
// 2.如果是对象类型,那么拿到的是代理对象
// console.log("info数据发生了变化", newValue, oldValue);
// console.log(newValue.name, oldValue.name);
// 3.获取原生对象
// console.log({ ...newValue });
console.log(Vue.toRaw(newValue));
},
},
});
// 2.挂载app
app.mount("#app");
</script>
</body>
</html>
3.2.2. 侦听的选项
- deep
- immediate
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<h2>{{info.name}}</h2>
<button @click="changeInfo">修改info</button>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data:option api
data: function () {
return {
info: { name: "why", age: 18 },
};
},
methods: {
changeInfo() {
// 1.创建一个新对象,赋值给info
// this.info = {name:"kobe"}
// 2.直接修改原对象某一个属性
this.info.name = "kobe";
},
},
watch: {
// 默认watch监听不会进行深度监听
// info(newValue, oldValue) {
// console.log("侦听到info的改变:", newValue, oldValue);
// },
info: {
handler(newValue, oldValue) {
console.log("侦听到info的改变:", newValue, oldValue);
console.log(newValue === oldValue);
},
// 监听器选项
// info进行深度监听
deep: true,
// 第一次渲染直接执行一次监听器
immediate: true,
},
"info.name": function (newValue, oldValue) {
console.log("name发生了改变:", newValue, oldValue);
},
},
});
// 2.挂载app
app.mount("#app");
</script>
</body>
</html>
3.2.3. 其他的写法
- "info.name"
- 别的写法
- created -> this.$watch()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<h2>{{message}}</h2>
<button @click="changeMessage">修改message</button>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data:option api
data: function () {
return {
message: "Hello Vue",
};
},
methods: {
changeMessage() {
this.message = "你好啊,李银河";
},
},
// 生命周期回调函数:当前的组件被创建时自动执行
// 一般在该函数中,会进行网络请求
created() {
// ajax/fetch/axios
console.log("created");
this.$watch(
"message",
(newValue, oldValue) => {
console.log("message数据变化:", newValue, oldValue);
},
{ deep: true }
);
},
});
// 2.挂载app
app.mount("#app");
</script>
</body>
</html>