Vue01-简介与入门

Vue

01. 简介

1.1 前端三大框架

目前前端最流行的三大框架:

  • Vue
  • React
  • angular

image-20230519141258319

1.2 Vue简介

Vue (读音 /vjuː/,类似于 view) ,也可以写成Vue.js。

vue.js是目前前端web开发最流行的工具库,由尤雨溪在2014年2月发布的。

  • 一套用于构建用户界面渐进式 JavaScript框架;
  • 基于标准 HTML、CSS 和 JavaScript 构建,
  • 提供了一套声明式的、组件化的编程模型;

VUE3文档https://v3.cn.vuejs.org/guide/introduction.html

什么是渐进式框架呢?

表示我们可以在项目中一点点来引入和使用Vue,而不一定需要全部使用Vue来开发整个项目。

02. 快速上手

Vue的本质,就是一个JavaScript的库,对于Vue的使用:

  • 在页面中通过CDN引入
  • 下载Vue的JavaScript文件,并且自己手动引入
  • 通过npm包管理工具安装并使用它
  • 直接通过Vue CLI创建项目

2.1 引入

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--创建由Vue接管的区域-->
<div id="app"></div>
<!--引入Vue:CDN引入-->
<script src="https://unpkg.com/vue@next"></script>
<!--引入Vue:本地引入,需提前下载-->
<!--<script src="../js/vue.js"></script>-->
<script>
// 创建Vue的实例
const app = Vue.createApp({
template: '<h2>Hello</h2>'
})
// 挂载
app.mount('#app')
</script>
</body>
</html>

2.2 体验插值语法

插值语法,官网中的说法是Mustache。(由于使用{{}},形似胡子,所以使用这个单词)。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--创建由Vue接管的区域-->
<div id="app">
<h1>欢迎学习Vue.js</h1>
<div>我是{{name}},微信{{wechat}}</div>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
// 使用Vue
const app = Vue.createApp({
data: function () {
return {
name: '子不语',
wechat: '188888888'
}
},
})
// 挂载
app.mount('#app')
</script>
</body>
</html>

插值语法的更多用法:

  • JavaScript表达式
  • 三元运算
  • 调用函数
<div id="app">
<!-- 1. 基本使用-->
<div>我叫{{name}},我喜欢{{hobby}}</div>
<!-- 2. JavaScript表达式-->
<div>{{ num + 1 + 1 }}</div>
<!-- 3. 三元运算-->
<div>{{ age>=18 ?"成年人":"未成年人"}}</div>
<!-- 4. 调用函数-->
<div>{{demoFunc()}}</div>
<!-- 如果函数需要参数也可传入 -->
</div>
<script>
// 使用Vue
const app = Vue.createApp({
data: function () {
return {
num: 0,
name: 子不语,
age: 22,
hobby: 羽毛球,
dataInfo: {
email: "xxxxx@163.com"
},
}
},
methods: {
demoFunc: function () {
return '2012-12-12'
},
}
})
// 挂载
app.mount('#app')
</script>

2.3 动态展示数据

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--这里使用本地引入Vue-->
<script src="./Vue.js"></script>
</head>
<body>
<!--创建由Vue接管的区域-->
<div id="app"></div>
<script>
// 使用Vue
const app = Vue.createApp({
// 将内容放入app的template中
template: `
<h1>欢迎学习Vue.js</h1>
<h2>电影列表</h2>
<ul>
<li v-for="item in movies">{{item}}</li>
</ul>
`,
data: function () {
return {
movies: ['大话西游', '盗梦空间', '流浪地球']
}
},
})
// 挂载
app.mount('#app')
</script>
</body>
</html>

2.4 案例:计数器

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./Vue.js"></script>
</head>
<body>
<!--创建由Vue接管的区域-->
<div id="app"></div>
<script>
// 使用Vue
const app = Vue.createApp({
template: `
<h1>当前计数:{{num}}</h1>
<button @click="add">+1</button>
<button @click="minus">-1</button>
`,
data: function () {
return {
num: 0
}
},
methods: {
add: function () {
this.num++ // 使用this,可以直接访问data中定义的数据
},
minus: function () {
this.num--
},
}
})
// 挂载
app.mount('#app')
</script>
</body>
</html>

当app中定义了template,Vue会用template代替接管区域的内容;

如果app没有定义template,Vue默认渲染接管区域的内容。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./Vue.js"></script>
</head>
<body>
<!--创建由Vue接管的区域-->
<div id="app">
<h1>当前计数:{{num}}</h1>
<button @click="add">+1</button>
<button @click="minus">-1</button>
</div>
<script>
// 使用Vue
const app = Vue.createApp({
data: function () {
return {
num: 0
}
},
methods: {
add: function () {
this.num++
},
minus: function () {
this.num--
},
}
})
// 挂载
app.mount('#app')
</script>
</body>
</html>

2.5 原生js实现计数器

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>当前计数:<span class="counter"></span></h2>
<button class="add"> +1</button>
<button class="minus"> -1</button>
<script>
// 1. 获取Dom
const counterEl = document.querySelector('.counter')
const addBtnEl = document.querySelector('.add')
const MinusBtnEl = document.querySelector('.minus')
// 2. 定义一个变量,用来记录数据
let counter = 100
counterEl.textContent = counter.toString()
// 3. 监听按钮的点击
addBtnEl.onclick = function () {
counter++
counterEl.textContent = counter.toString()
}
MinusBtnEl.onclick = function () {
counter--
counterEl.textContent = counter.toString()
}
</script>
</body>
</html>

2.6 声明式编程与命令式编程

2.1.42.1.5,我们可以体验到声明式编程与命令时编程的不同:

  • 命令式编程关注的是 “how to do”,需要自己完成整个过程。

  • 声明式编程关注的是 “what to do”,由框架(机器)完成整个过程。

2.1.5中,我们通过JavaScript编写每一条代码,来给浏览器传达每一个指令,这样的编写代码的过程,我们称之为命令式编程;

在早期的原生JavaScript和jQuery开发的过程中,我们都是通过这种命令式的方式在编写代码的;

而在2.1.4中,我们使用Vue,在createApp传入的对象中声明需要的内容:

  • 模板template
  • 数据data
  • 方法methods

这样编写代码的过程,我们称之为是声明式编程;

03. 基本属性-optionAPI

3.1 data

data属性是传入一个函数,并且该函数需要返回一个对象:

  • 在Vue2.x的时候,也可以传入一个对象(虽然官方推荐是一个函数);
  • 在Vue3.x的时候,必须传入一个函数,否则就会直接在浏览器中报错;

data中返回的对象会被Vue的响应式系统劫持,之后对该对象的修改或者访问都会在劫持中被处理。

所以我们在template或者app中通过 {{counter}} 访问counter,可以从对象中获取到数据;修改counter的值时,app中的 {{counter}}也会发生改变;

3.2 methods

methods属性是一个对象,通常我们会在这个对象中定义很多的方法,这些方法可以被绑定到模板中。

在这些方法中,我们可以使用this关键字来直接访问到data中返回的对象的属性。

对官方文档有这么一段描述:

image-20230519153316845

问题一:为什么不能使用箭头函数(官方文档有给出解释)?

问题二:不使用箭头函数的情况下,this到底指向的是什么?(可以作为一道面试题)

事实上Vue的源码当中就是对methods中的所有函数进行了遍历,并且通过bind绑定了this。

image-20230519154149477

3.3 computed

computed称为计算属性。

对于computed,官方并没有给出直接的概念解释。

而是说:对于任何包含响应式数据的复杂逻辑,都应该使用计算属性。

我们通过案例来理解,以下有三个案例:

案例一:我们有两个变量:firstName和lastName,希望它们拼接之后在界面上显示;

案例二:我们有一个分数:score

  • 当score大于60的时候,在界面上显示及格;

  • 当score小于60的时候,在界面上显示不及格;

案例三:我们有一个变量message,记录一段文字:比如Hello World

  • 某些情况下我们是直接显示这段文字;

  • 某些情况下我们需要对这段文字进行反转;


方案一:在模板语法中直接使用表达式;

<div id="app">
<!-- 1.拼接名字 -->
<h2>{{ firstName + " " + lastName }}</h2>
<!-- 2.显示分数等级 -->
<h2>{{ score >= 60 ? '及格': '不及格' }}</h2>
<!-- 3.反转单词显示文本 -->
<h2>{{ message.split(" ").reverse().join(" ") }}</h2>
</div>

思路一虽然直接,但是存在不可避免的缺点:

  • 模板中存在大量的复杂逻辑,不便于维护(模板中表达式的初衷是用于简单的计算);

  • 当需要多次实现同样逻辑时,存在重复的代码;

  • 多次使用时,很多运算也需要多次执行,没有缓存;


方案二:使用method对逻辑进行抽取,模板中进行函数调用;

<div id="app">
<!-- 1.拼接名字 -->
<h2>{{ getFullname() }}</h2>
<!-- 2.显示分数等级 -->
<h2>{{ getScoreLevel() }}</h2>
<!-- 3.反转单词显示文本 -->
<h2>{{ reverseMessage() }}</h2>
</div>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
// 1.姓名
firstName: "kobe",
lastName: "bryant",
// 2.分数: 及格/不及格
score: 80,
// 3.一串文本: 对文本中的单词进行反转显示
message: "Hello World!"
}
},
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>

方案二比方案一有优化,但仍然有缺点:

  • 我们想显示的是一个结果,但变成了一种方法调用;
  • 多次使用方法的时候,没有缓存,也需要多次计算;

方案三:使用计算属性computed;

<div id="app">
<!-- 1.拼接名字 -->
<h2>{{ fullname }}</h2>
<!-- 2.显示分数等级 -->
<h2>{{ scoreLevel }}</h2>
<!-- 3.反转单词显示文本 -->
<h2>{{ reverseMessage }}</h2>
</div>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
// 1.姓名
firstName: "kobe",
lastName: "bryant",
// 2.分数: 及格/不及格
score: 80,
// 3.一串文本: 对文本中的单词进行反转显示
message: "my name is why"
}
},
computed: {
// 1.计算属性默认对应的是一个函数,模板中可以直接书写该函数名称
fullname() {
return this.firstName + " " + this.lastName
},
scoreLevel() {
return this.score >= 60 ? "及格": "不及格"
},
reverseMessage() {
return this.message.split(" ").reverse().join(" ")
}
}
})
// 2.挂载app
app.mount("#app")
</script>

注意:

无论是直观上还是效果上,计算属性都是更好的选择;

计算属性看起来像是一个函数,但是我们在使用的时候不需要加();并且计算属性是有缓存的。

在computed中,对于函数的调用,运行一次之后,结果会被缓存起来,在数据不发生变化时,计算属性是不需要重新计算的。


计算属性在大多数情况下,只需要一个getter方法即可,所以我们会将计算属性直接写成一个函数。

如果我们需要设置计算属性的值,这时候我们也可以给计算属性设置一个setter的方法。

<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() {
return {
firstname: "coder",
lastname: "easy"
}
},
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中可以直接访问到computed中的值。
this.fullname = "kobe bryant"
}
}
})
// 2.挂载app
app.mount("#app")
</script>

3.4 watch

监听器watch。

在某些情况下,我们希望在代码逻辑中监听某个数据的变化,当该数据发生变化时,执行某一些代码。

这个时候就需要用到监听器watch了。

以下案例:

data中的message发生变化时,watch中的message函数就会执行。

watch中函数的名称必须是数据名称。

<div id="app">
<h2>{{message}}</h2>
<button @click="changeMessage">修改message</button>
</div>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
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>

image-20230522183606076

watch中监听的如果是对象类型, 那么数据变化后,拿到的是代理对象。

如果要获取原生对象,有两种方法:

console.log({ ...newValue })
console.log(Vue.toRaw(newValue))
深度监听

默认情况下,watch只是在侦听info的引用变化,对于内部属性的变化是不会做出响应的。

下面的例子中,changeInfo函数如果是直接赋予了一个新对象,所以,watch可以监听到,

但是,如果是info属性发生变化,默认是监听不到的。

methods: {
changeInfo() {
// 1.创建一个新对象, 赋值给info
// this.info = { name: "kobe" }
// 2.直接修改原对象某一个属性,这种情况监听不到。
this.info.name = "kobe"
}

watch的深度监听:

watch: {
// 进行深度监听
info: {
// 以下为完整写法,handler函数用于处理数据变化逻辑。之前是语法糖写法。
handler(newValue, oldValue) {
console.log("侦听到info改变:", newValue, oldValue)
console.log(newValue === oldValue) // True
},
// 监听器选项:
deep: true, // info进行深度监听
// 第一次渲染直接执行一次监听器
immediate: true
},
// Vue2的语法(了解即可)
"info.name": function(newValue, oldValue) {
console.log("name发生改变:", newValue, oldValue)
}
}
watch的其他写法

但组件被建立时,系统会自动执行created函数,

我们可以在created函数中,使用 $watch 来监听。需要传入三个参数:

  • 第一个参数是要侦听的源;

  • 第二个参数是侦听的回调函数callback;

  • 第三个参数是额外的其他选项,比如deep、immediate;

<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
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>

3.5 其他属性

当然,这里还可以定义很多其他的属性,比如props、emits、setup、components等等;也包括很多的生命周期函数。

04. v-once

当某些值只需要被渲染一次时,可以使用v-once。

需要注意的是,一旦某个标签使用了v-once指令,其后代标签都只渲染一次。

<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-once -->
<h2 v-once>
{{ message }}
<span>数字: {{counter}}</span>
</h2>
<h1>{{message}}</h1>
<button @click="changeMessage">改变message</button>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
data: function() {
return {
message: "Hello Vue",
counter: 100
}
},
methods: {
changeMessage: function() {
this.message = "你好啊, 李银河"
this.counter += 100
console.log(this.message, this.counter)
}
}
})
// 2.挂载app
app.mount("#app")
</script>
</body>
</html>

05. v-text

v-text指令并不常用,了解即可。

其以指令的形式来渲染内容,比如插值语法灵活。

<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>
<h2 v-text="message"></h2>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
data: function() {
return {
message: "Hello Vue"
}
},
})
// 2.挂载app
app.mount("#app")
</script>
</body>
</html>

效果:

image-20230519162320395

06. v-html

如果data中的数据是标签语法,v-text并不会渲染它。

使用v-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>{{ content }}</h2>
<h2 v-html="content"></h2>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data: function() {
return {
content: `<span style="color: red; font-size: 30px;">你好,Vue</span>`
}
},
})
// 2.挂载app
app.mount("#app")
</script>
</body>
</html>

效果:

image-20230519162338545

07. v-pre

v-pre用于跳过元素和它的子元素的编译过程,显示原始的内容。

<div id="app">
<div v-pre>
<h2>{{ message }}</h2>
<p>当前计数: {{ counter }}</p>
<p>{{}}</p>
</div>
</div>

效果:

image-20230519162801407

08. v-cloak

浏览器的加载原则是:更快地让用户看到内容。

浏览器在渲染页面的时候,实际上是先展示类似下面的内容:

image-20230519163516930

当执行到Vue.createApp时,再根据Vue的语法去渲染页面,显示:

image-20230519163617717

在某些极端的情况下,Vue的渲染过程太慢,用户会看到类似{{message}} 的内容。

如果不希望让用户看到这些内容,则可以使用v-cloak指令。

cloak是斗篷的意思。

v-cloak指令需要搭配css语法使用,

通过css语法,指定在未渲染完全时的显示样式。

<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>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app">
<h2 v-cloak>{{message}}</h2>
</div>
<script src="../lib/vue.js"></script>
<script>
setTimeout(() => {
// 1.创建app
const app = Vue.createApp({
// data: option api
data: function() {
return {
message: "Hello Vue"
}
},
})
// 2.挂载app
app.mount("#app")
}, 3000)
</script>
</body>
</html>

09. v-memo

使用v-memo时,只有规定的值发生变化,才会重新渲染。

<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">
<div v-memo="[name, age]">
<h2>姓名: {{ name }}</h2>
<h2>年龄: {{ age }}</h2>
<h2>身高: {{ height }}</h2>
</div>
<button @click="updateInfo">改变信息</button>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data: function() {
return {
name: "why",
age: 18,
height: 1.88
}
},
methods: {
updateInfo: function() {
// this.name = "kobe"
this.age = 20
}
}
})
// 2.挂载app
app.mount("#app")
</script>
</body>
</html>

10. v-bind

前面的指令都是用来渲染标签内容的,如果需要动态渲染标签的属性,则需要使用到v-bind。

10.1 绑定属性

<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">
<div>
<button @click="switchImage">切换图片</button>
</div>
<!-- 1.绑定img的src属性 -->
<img v-bind:src="showImgUrl" alt="">
<!-- 语法糖: v-bind -> : -->
<img :src="showImgUrl" alt="">
<!-- 2.绑定a的href属性 -->
<a :href="href">百度一下</a>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data: function() {
return {
imgUrl1: "http://p1.music.126.net/agGc1qkogHtJQzjjyS-kAA==/109951167643767467.jpg",
imgUrl2: "http://p1.music.126.net/_Q2zGH5wNR9xmY1aY7VmUw==/109951167643791745.jpg",
showImgUrl: "http://p1.music.126.net/_Q2zGH5wNR9xmY1aY7VmUw==/109951167643791745.jpg",
href: "http://www.baidu.com"
}
},
methods: {
switchImage: function() {
this.showImgUrl = this.showImgUrl === this.imgUrl1 ? this.imgUrl2: this.imgUrl1
}
}
})
// 2.挂载app
app.mount("#app")
</script>
</body>
</html>

10.2 绑定class

<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>
.active {
color: red;
}
</style>
</head>
<body>
<div id="app">
<!-- 1.基本绑定class -->
<h2 :class="classes">Hello World</h2>
<!-- 2.动态class可以写三元运算 -->
<button :class=" isActive ? 'active': '' " @click="btnClick">我是按钮</button>
<!-- 3.class的绑定内容可以传入对象,值必须为布尔值 -->
<button :class="{ active: isActive }" @click="btnClick">我是按钮</button>
<!-- 4.对象语法:传入多个键值对 -->
<button :class="{ active: isActive, why: true, kobe: false }" @click="btnClick">我是按钮</button>
<!-- 5.动态绑定的class是可以和普通的class同时的使用 -->
<button class="abc cba" :class="{ active: isActive, why: true, kobe: false }" @click="btnClick">我是按钮</button>
<!-- 6.通过函数的方式获取对象 -->
<button class="abc cba" :class="getDynamicClasses()" @click="btnClick">我是按钮</button>
<!-- 7.动态class可以写数组语法(了解) -->
<h2 :class="['abc', 'cba']">Hello Array</h2>
<h2 :class="['abc', className]">Hello Array</h2>
<h2 :class="['abc', className, isActive? 'active': '']">Hello Array</h2>
<h2 :class="['abc', className, { active: isActive }]">Hello Array</h2>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data: function() {
return {
classes: "abc cba nba",
isActive: false,
className: "why"
}
},
methods: {
btnClick: function() {
this.isActive = !this.isActive
},
getDynamicClasses: function() {
return { active: this.isActive, why: true, kobe: false }
}
}
})
// 2.挂载app
app.mount("#app")
</script>
</body>
</html>

10.3 绑定style

使用v-bind绑定style,可以传入对象类型,但要注意,以下的写法是不对的。

<h2 v-bind:style="{ color: red, font-size: 30px }">你好,Vue!</h2>

因为-与30px这种写法是不能被检测的。

CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名。

<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.普通的html写法 -->
<h2 style="color: red; font-size: 30px;">你好,Vue!</h2>
<!-- 2.style中的某些值, 来自data中 -->
<!-- 2.1.动态绑定style, 在后面跟上 对象类型 (重要)-->
<h2 v-bind:style="{ color: fontColor, fontSize: fontSize + 'px' }">你好,Vue!</h2>
<!-- 2.2.动态的绑定属性, 这个属性是一个对象 -->
<h2 :style="objStyle">你好,Vue!</h2>
<!-- 3.style的数组语法,元素为对象 -->
<h2 :style="[objStyle, { backgroundColor: 'purple' }]">你好,Vue!</h2>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
data: function() {
return {
fontColor: "blue",
fontSize: 30,
objStyle: {
fontSize: '50px',
color: "green"
}
}
},
})
// 2.挂载app
app.mount("#app")
</script>
</body>
</html>

如果属性名称不是固定的,我们可以使用 :[属性名]=“值” 的格式来定义;

这种绑定的方式,我们称之为动态绑定属性。

<div id="app">
<h2 :[name]="'aaaa'">Hello World</h2>
</div>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data: function() {
return {
name: "class"
}
},
})
// 2.挂载app
app.mount("#app")
</script>

10.4 绑定对象

<div id="app">
<!-- 各个属性逐一绑定,较为繁琐 -->
<h2 :name="name" :age="age" :height="height">Hello World</h2>
<!-- v-bind绑定对象,常用于给组件传递参数 -->
<h2 v-bind="infos">Hello Bind</h2>
</div>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data: function() {
return {
infos: { name: "why", age: 18, height: 1.88, address: "广州市" },
name: "why",
age: 18,
height: 1.88
}
},
})
// 2.挂载app
app.mount("#app")
</script>

11. v-on

前面的指令是绑定元素的内容和属性,在开发中,还有一个非常重要的特性久是交互。

Vue提供了v-on来绑定事件,比如点击、拖拽、键盘事件等。

11.1 基本使用

<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>
<!-- 6.元素绑定多个事件,直接绑定对象 -->
<!-- <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 {
counter: 0
}
},
methods: {
divClick() {
console.log("divClick")
},
increment() {
this.counter++
},
divMousemove() {
console.log("divMousemove")
}
}
})
// 2.挂载app
app.mount("#app")
</script>

11.2 传递参数

在绑定事件时, 没有传递任何的参数, 那么event对象会被默认传递进来;

如果有明确传递参数,则event不会被传递;

如果有明确传递参数,又希望有event参数,可以使用$event传递。

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

11.3 修饰符

v-on支持修饰符,修饰符相当于对事件进行了一些特殊的处理:

  • .stop - 调用 event.stopPropagation()。
  • .prevent - 调用 event.preventDefault()。
  • .capture - 添加事件侦听器时使用 capture 模式。
  • .self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
  • .{keyAlias} - 仅当事件是从特定键触发时才触发回调。
  • .once - 只触发一次回调。
  • .left - 只当点击鼠标左键时触发。
  • .right - 只当点击鼠标右键时触发。
  • .middle - 只当点击鼠标中键时触发。
  • .passive - { passive: true } 模式添加侦听器
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.box {
width: 100px;
height: 100px;
background-color: orange;
}
</style>
</head>
<div id="app">
<div class="box" @click="divClick">
<button @click.stop="btnClick">按钮</button>
</div>
</div>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data: function() {
return {
message: "Hello Vue"
}
},
methods: {
btnClick(event) {
console.log("btnClick")
},
divClick() {
console.log("divClick")
}
}
})
// 2.挂载app
app.mount("#app")
</script>

12. v-if

Vue提供了一些指令进行条件渲染。

  • v-if
  • v-else
  • v-else-if
  • v-show
<div id="app">
<ul v-if="names.length > 0">
<li v-for="item in names">{{item}}</li>
</ul>
<h2 v-else>当前names没有数据, 请求获取数据后展示</h2>
</div>
<script>
// 1.创建app
const app = Vue.createApp({
data: function() {
return {
names: []
}
},
})
// 2.挂载app
app.mount("#app")
</script>
<div id="app">
<!-- v-if="条件",判断是否为空对象 -->
<div class="info" v-if="Object.keys(info).length">
<h2>个人信息</h2>
<ul>
<li>姓名: {{info.name}}</li>
<li>年龄: {{info.age}}</li>
</ul>
</div>
</div>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
info: {}
}
}
})
// 2.挂载app
app.mount("#app")
</script>

因为Vue的指令必须添加到一个元素上才能产生作用。

一般我们选择一个div作为顶层,作为根节点。

但是如果我们希望切换的是多个元素呢?这个时候,我们可以选择使用template;

template元素可以当做不可见的包裹元素,并且在v-if上使用,但是最终template不会被渲染出。

13. v-show

v-show的功能与v-if类似,但有本质的不同。

  • v-show元素无论是否需要显示到浏览器上,它的DOM实际都是有存在的,只是通过CSS的display属性来进行切换;

  • v-if当条件为false时,其对应的原生压根不会被渲染到DOM中;

其次要注意的是:v-show是不支持template;

v-show也不可以和v-else一起使用;

开发中如何进行选择呢?

如果我们的原生需要在显示和隐藏之间频繁的切换,那么使用v-show;

如果不会频繁的发生切换,那么使用v-if;

<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() {
return {
isShowCode: true
}
},
methods: {
toggle() {
this.isShowCode = !this.isShowCode
}
}
})
// 2.挂载app
app.mount("#app")
</script>

image-20230519210529575

14. v-for

遍历数组:

// 1.数组: 存放的字符串
movies: ["星际穿越", "少年派", "大话西游", "哆啦A梦"],
// 2.数组: 存放的是对象
products: [
{ id: 110, name: "Macbook", 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秒杀, 快来抢购!" },
]
<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>
<p>秒杀: {{item.desc}}</p>
</div>
</div>

遍历对象

message: "Hello Vue",
movies: [],
info: { name: "why", age: 18, height: 1.88 }
<div id="app">
<!-- 1.遍历对象 -->
<ul>
<li v-for="(value, key, index) in info">{{value}}-{{key}}-{{index}}</li>
</ul>
<!-- 2.遍历字符串(iterable) -->
<ul>
<li v-for="item in message">{{item}}</li>
</ul>
<!-- 3.遍历数字 -->
<ul>
<li v-for="item in 100">{{item}}</li>
</ul>
</div>

14.1 数组更新检测

Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。

这些被包裹过的方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

替换数组的方法:

  • 上面的方法会直接修改原来的数组;

  • 但是某些方法不会替换原来的数组,而是会生成新的数组,比如 filter()、concat() 和 slice();

<div id="app">
<ul>
<li v-for="item in names">{{ item }}</li>
</ul>
<button @click="changeArray">修改数组</button>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
names: ["abc", "cba", "nba", "aaa", "ccc"]
}
},
methods: {
changeArray() {
// 1.直接将数组修改为一个新的数组
// this.names = ["why", "kobe"]
// 2.通过一些数组的方法, 修改数组中的元素
// this.names.push("why")
// this.names.pop()
// this.names.splice(2, 1, "why")
// this.names.sort()
// this.names.reverse()
// 3.不修改原数组的方法是不能侦听的
const newNames = this.names.map(item => item + "why")
this.names = newNames
}
}
})
// 2.挂载app
app.mount("#app")
</script>

14.2 v-for中的key

在使用v-for进行列表渲染时,我们通常会给元素或者组件绑定一个key属性。

key属性的要求是唯一,一般为id值。

这个key属性有什么作用呢?我们先来看一下官方的解释:

key属性主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes;

如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能尝试就地修改/复用相同类型元素的算法;

而使用key时,它会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素;

<div id="app">
<button @click="insertF">插入f</button>
<ul>
<!-- key要求是唯一: id -->
<li v-for="item in letters" :key="item">{{item}}</li>
</ul>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
data() {
return {
letters: ["a", "b", "c", "d", "e"]
}
},
methods: {
insertF() {
this.letters.splice(2, 0, "f")
this.letters.splice()
}
}
})
// 2.挂载app
app.mount("#app")
</script>

14.3 VNode概念

VNode的全称是Virtual Node,也就是虚拟节点;

事实上,无论是组件还是元素,它们最终在Vue中表示出来的都是一个个VNode。

这样做的好处之一是可以更好地实现跨平台。

针对不同的最终设备,比如浏览器、安卓端、iOS、VR等,渲染成不同的内容。

VNode的本质是一个JavaScript的对象。

比如,如果有这一个标签:

<div class="title" style="font-size: 30px;color: red">哈哈哈</div>

那么,Vue会将其转换成为一个VNode对象,

const vnode = {
type: 'div',
props: {
class:'title',
style: {
'font-size': '30px',
'color': 'red',
},
},
children:'哈哈哈'
}

渲染成VNode之后,再将其渲染成正在的DOM对象。

image-20230519231539629

如果不只是一个简单的div,而是有一大堆的元素,那么它们会形成一个VNode Tree

image-20230519231622202

15. v-model

前端开发中,一个很重要的环节就是与用户互动。

  • 用户在登录、注册时需要提交账号密码;
  • 用户在检索、创建、更新信息时,需要提交一些数据;

此时,数据就需要做一个双向绑定。

15.1 手动实现双向绑定

<div id="app">
<!-- 手动的实现双向绑定 -->
<input type="text" :value="message" @input="inputChange">
<h2>{{message}}</h2>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
message: "Hello Vue",
}
},
methods: {
inputChange(event) {
this.message = event.target.value
},
}
})
// 2.挂载app
app.mount("#app")
</script>

:value="message"实现了数据的动态绑定,读取message的值,将其显示在页面中;

@input="inputChange"对“输入”进行监听,本质上是写了inputChange方法,但用户进行输入时,执行inputChange方法;

inputChange方法将用户输入的值重新赋值给message。

15.2 绑定input

其实v-model的原理就是这两个操作:

  • v-bind绑定value属性的值;

  • v-on绑定input事件监听到函数中,函数会获取最新的值赋值到绑定的属性中;

image-20230523000513932

<div id="app">
<!-- 登录功能 -->
<label for="account">
账号:<input id="account" type="text" v-model="account">
</label>
<label for="password">
密码:<input id="password" type="password" v-model="password">
</label>
<button @click="loginClick">登录</button>
</div>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
account: "",
password: ""
}
},
methods: {
loginClick() {
const account = this.account
const password = this.password
// url发送网络请求
console.log(account, password)
}
}
})
// 2.挂载app
app.mount("#app")
</script>

15.3 绑定textarea

<div id="app">
<textarea cols="30" rows="10" v-model="content"></textarea>
<p>输入的内容: {{content}}</p>
</div>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
content: ""
}
},
})
// 2.挂载app
app.mount("#app")
</script>

15.4 绑定checkbox

checkbox有多选框和单选框:

  • 单选框: 绑定到属性中的值是一个Boolean

  • 多选框当中, 必须明确的绑定一个value值,对应的data中属性是一个数组。

<div id="app">
<!-- 1.checkbox单选框: 绑定到属性中的值是一个Boolean -->
<label for="agree">
<input id="agree" type="checkbox" v-model="isAgree"> 同意协议
</label>
<h2>单选框: {{isAgree}}</h2>
<hr>
<!-- 2.checkbox多选框: 绑定到属性中的值是一个Array -->
<!-- 注意: 多选框当中, 必须明确的绑定一个value值 -->
<div class="hobbies">
<h2>请选择你的爱好:</h2>
<label for="sing">
<input id="sing" type="checkbox" v-model="hobbies" value="sing">
</label>
<label for="jump">
<input id="jump" type="checkbox" v-model="hobbies" value="jump">
</label>
<label for="rap">
<input id="rap" type="checkbox" v-model="hobbies" value="rap"> rap
</label>
<label for="basketball">
<input id="basketball" type="checkbox" v-model="hobbies" value="basketball"> 篮球
</label>
<h2>爱好: {{hobbies}}</h2>
</div>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
isAgree: false,
hobbies: []
}
},
})
// 2.挂载app
app.mount("#app")
</script>

15.5 绑定radio

radio与多选框不同,其值是互斥的。比如性别,要么男要么女。

在input标签中,是通过name属性来区分的。只要input标签中,拥有相同的name属性,就可以实现互斥。

因此v-model就绑定在name属性上。

<div id="app">
<div class="gender">
<label for="male">
<input id="male" type="radio" v-model="gender" value="male">
</label>
<label for="female">
<input id="female" type="radio" v-model="gender" value="female">
</label>
<h2>性别: {{gender}}</h2>
</div>
</div>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
gender: "female"
}
},
})
// 2.挂载app
app.mount("#app")
</script>

15.6 绑定select

<div id="app">
<!-- select的单选 -->
<select v-model="fruit">
<option value="apple">苹果</option>
<option value="orange">橘子</option>
<option value="banana">香蕉</option>
</select>
<h2>单选: {{fruit}}</h2>
<hr>
<!-- select的多选 -->
<select multiple size="3" v-model="fruits">
<option value="apple">苹果</option>
<option value="orange">橘子</option>
<option value="banana">香蕉</option>
</select>
<h2>多选: {{fruits}}</h2>
</div>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
fruit: "orange",
fruits: []
}
},
})
// 2.挂载app
app.mount("#app")
</script>

15.7 修饰符

  • .lazy:默认v-model绑定的是input事件,lazy修饰符将绑定change事件;
  • .number:自动将内容转换成数字;
  • .trim:去除收尾的空格
<div id="app">
<!-- 1.lazy: 绑定change事件 -->
<input type="text" v-model.lazy="message">
<h2>message: {{message}}</h2>
<hr>
<!-- 2.number: 自动将内容转换成数字 -->
<input type="text" v-model.number="counter">
<h2>counter:{{counter}}-{{typeof counter}}</h2>
<input type="number" v-model="counter2">
<h2>counter2:{{counter2}}-{{typeof counter2}}</h2>
<hr>
<!-- 3.trim: 去除收尾的空格 -->
<input type="text" v-model.trim="content">
<h2>content: {{content}}</h2>
<hr>
<!-- 4.使用多个修饰符 -->
<input type="text" v-model.lazy.trim="content">
<h2>content: {{content}}</h2>
</div>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data() {
return {
message: "Hello Vue",
counter: 0,
counter2: 0,
content: ""
}
},
watch: {
content(newValue) {
console.log("content:", newValue)
}
}
})
// 2.挂载app
app.mount("#app")
</script>

(结束)

posted @   子不语2015831  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示