Vue

01. 简介

1.1 前端三大框架

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

  • Vue
  • React
  • angular

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中返回的对象的属性。

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

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

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

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

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>

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>

效果:

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>

效果:

07. v-pre

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

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

效果:

08. v-cloak

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

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

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

在某些极端的情况下,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>

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对象。

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

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事件监听到函数中,函数会获取最新的值赋值到绑定的属性中;

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

(结束)

Vue01-简介与入门的更多相关文章

  1. JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式

    相关链接: JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式 JS面向对象(2) -- this的使用,对 ...

  2. python3-day1-python简介及入门

    python简介及入门 python简介 Python的创始人为Guido van Rossum.1989年圣诞节期间,在阿姆斯特丹,Guido为了打发圣诞节的无趣,决心开发一个新的脚本解释程序,做为 ...

  3. Robot Framework-工具简介及入门使用

    Robot Framework-Mac版本安装 Robot Framework-Windows版本安装 Robot Framework-工具简介及入门使用 Robot Framework-Databa ...

  4. Linux内核学习笔记-1.简介和入门

    原创文章,转载请注明:Linux内核学习笔记-1.简介和入门 By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...

  5. Akka 简介与入门

    Akka 简介与入门 http://www.thinksaas.cn/group/topic/344095/ 参考官网  http://akka.io/ 开源代码  https://github.co ...

  6. 【转】Docker简介与入门

    转自:https://segmentfault.com/a/1190000000448808 Docker是个新生的事物,概念类似虚拟化.网上关于Docker入门的东西已经很多了.不过本文探讨了Doc ...

  7. Quartz入门例子简介 从入门到菜鸟(一)

    转: Quartz入门例子简介 从入门到菜鸟(一) 2016年11月19日 22:58:24 爱种鱼的猫 阅读数:4039   刚接触quartz这个词并不是在学习过程中...而是WOW里面的界面插件 ...

  8. Lombok简介及入门使用 (转载)

    Lombok简介及入门使用 lombok既是一个IDE插件,也是一个项目要依赖的jar包. Intellij idea开发的话需要安装Lombok plugin,同时设置 Setting -> ...

  9. Shiro简介、入门案例、web容器的集成

    目的: shiro简介 Shiro入门案例 Shiro与web容器的集成 shiro简介(中文官网:https://www.w3cschool.cn/shiro/andc1if0.html) 1.什么 ...

  10. Python【第一章】:简介和入门

    ython简介 Python的创始人为Guido van Rossum.1989年圣诞节期间,在阿姆斯特丹,Guido为了打发圣诞节的无趣,决心开发一个新的脚本解释程序,做为ABC 语言的一种继承.之 ...

随机推荐

  1. Canvas好难,如何让研发低成本实现Web端流程图设计功能

    摘要:本文由葡萄城技术团队于博客园原创并首发.转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 前言 相信大家在职场中经常会用到流程图,在互联网行业,绘制流程 ...

  2. [etcd]简介与安装

    简介 etcd是一个采用Raft协议实现强一致性的分布式键值数据库,它提供了一种可靠的方式存储需要被分布式系统或机器集群访问的数据. 常见使用场景:服务注册与发现.键值对存储.消息发布和订阅.分布式锁 ...

  3. 全免费开源-国内搭建ChatGPT个人镜像站与维护全攻略

    本教程收集于:AIGC从入门到精通教程汇总 全免费开源,仅需一个域名就可以部署国内的ChatGPT镜像版本. One-Click to deploy well-designed ChatGPT web ...

  4. .NET周刊【8月第3期 2023-08-20】

    国内主题 抓的是周树人,与我鲁迅有什么关系? https://www.cnblogs.com/JulianHuang/p/17642511.html 问题:作者看到了一个关于Dictionary.Cl ...

  5. 《Kali渗透基础》05. 主动信息收集(二)

    @ 目录 1:端口扫描 2:UDP 扫描 2.1:Scapy 2.2:nmap 3:半开放扫描 3.1:Scapy 3.2:nmap 3.3:hping3 4:全连接扫描 4.1:Scapy 4.2: ...

  6. 微服务下使用maven做多环境配置

    分享技术,用心生活 前言:很多项目在开发,提测,上线时都会提前手动改一些配置文件来适应对应环境,麻烦不说了,而且也容易出错:生产环境的配置也容易暴露.基于此,我们基于spring cloud alib ...

  7. ffmpeg 在xp和server2003/2008/2012上修复无法定位GetNumaNodeProcessorMaskEx的问题

    问题 在给开发一个手机视频网站时需要用到ffmpeg截取视频缩略图, 把项目提交到服务器(server2003/ server2008)上时, 发现在调用命令时会出现错误"无法定位GetNu ...

  8. ES13 中11个令人惊叹的 JavaScript 新特性

    前言 与许多其他编程语言一样,JavaScript 也在不断发展.每年,该语言都会通过新功能变得更加强大,使开发人员能够编写更具表现力和简洁的代码. 小编今天就为大家介绍ES13中添加的最新功能,并查 ...

  9. Blackmail

    Blackmail Arthur Hailey The chief house officer, Ogilvie, who had declared he would appear at the Cr ...

  10. .Net核心级的性能优化(GC篇)

    1.前言 大部分人对于.Net性能优化,都停留在业务层面.或者简单的.Net框架配置层面.本篇来看下.Net核心部分GC垃圾回收配置:保留VM,大对象,独立GC,节省内存等.Net8里面有很多的各种G ...