Vue3 知识大总结(一)基础语法


一. 初识 Vue.js 开发

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

1. Vue 下载和使用

安装和使用Vue这个JavaScript库有一些方式:

  • 方式一:在页面中通过CDN的方式来引入
  • 方式二:下载Vue的JavaScript文件,并且自己手动引入
  • 方式三:通过npm包管理工具安装使用它(webpack再讲)
  • 方式四:直接通过Vue CLI创建项目,并且使用它

1.1 方式一:CDN引入

<div id="app"></div>

<script src="https://unpkg.com/vue@next"></script>
<script>
	// 使用Vue
	const app = Vue.createApp({
		template: `<h2>Hello world</h2><span>哈哈哈</span>`,
	});
	// 挂载
	app.mount("#app");
</script>

1.2 方式二:下载和引入

下载Vue的源码(https://unpkg.com/vue@next)到vue.js文件中

<div id="app"></div>

<script src="./lib/vue.js"></script>
<script>
	// 1.创建app
	const app = Vue.createApp({
		template: `<h1>Hello world</h1><span>哈哈哈</span>`
});
	// 2.挂载app
	app.mount("#app");
</script>

2. Vue 初体验之三个案例

2.1 动态数据展示

<div id="app"></div>

<script src="./lib/vue.js"></script>
<script>
	const app = Vue.createApp({
		template: `<h2>{{message}}</h2>`,
		data: function () {
			return {
				title: "Hello World",
				message: "你好呀,Vue3",
			};
		},
	});
	
	app.mount("#app");
</script>

2.2 动态展示列表

<div id="app"></div>

<script src="./lib/vue.js"></script>
<script>
	const app = Vue.createApp({
		template: `
		<h2>电影列表</h2>
		<ul>
			<li v-for="item in movies">{{item}}</li>   
		</ul>
		`,
		data: function () {
			return {
				movies: ["情书", "怦然心动", "傲慢与偏见"],
			};
		},
	});
	
	app.mount("#app");
</script>

2.3 计数器案例

<div id="app"></div>

<script src="./lib/vue.js"></script>
 <script>
	const app = Vue.createApp({
		template: `
		<h2>当前计数: {{counter}}</h2>
		<button @click="increment">+1</button>
		<button @click="decrement">-1</button>
		`,
		data: function () {
			return {
				counter: 0,
			};
		},
		methods: {
			increment: function () {
				this.counter++;
			},
			decrement: function () {
				this.counter--;
			},
		},
	});
	
	 app.mount("#app");
</script>

3. 命令式和声明式编程

原生开发和Vue开发的模式和特点涉及到两种不同的编程范式:

  • 命令式编程和声明式编程
  • ==声明式编程==

    • 声明式编程关注的是 “what to do”,由框架(机器)完成 “how”的过程
    • 我们会在createApp传入的对象中声明需要的内容,模板template、数据data、方法methods
    • 目前Vue、React、Angular、小程序的编程模式,我们称之为声明式编程
  • ==命令式编程==

    • 命令式编程关注的是 “how to do”,自己完成整个how的过程
    • 我们每完成一个操作,都需要通过JavaScript编写一条代码,来给浏览器一个指令
    • 在早期的原生JavaScript和jQuery开发的过程中,我们都是通过这种命令式的方式在编写代码的

在这里插入图片描述

3.1 MVVM 模型

MVC和MVVM都是一种软件的体系结构

  • MVC是Model – View –Controller的简称,是在前期被使用非常框架的架构模式,比如iOS、前端
  • MVVM是Model-View-ViewModel的简称,是目前非常流行的架构模式

通常情况下,我们也经常称Vue是一个MVVM的框架

在这里插入图片描述

4. data 属性

  • data属性必须是一个函数,并且该函数需要返回一个对象:
  • data中返回的对象会被Vue的响应式系统劫持,之后对该对象的修改或者访问都会在劫持中被处理:
    • 所以我们在template或者app中通过 访问counter,可以从对象中获取到数据
    • 所以我们修改counter的值时,app中的 也会发生改变

5. methods 属性

  • methods属性是一个对象,通常我们会在这个对象中定义很多的方法
    • 这些方法可以被绑定到模板中
    • 在该方法中,我们可以使用this关键字来直接访问到data中返回的对象的属性
  • 方法不能使用箭头函数,否则this就会是window了

二、Vue基础 - 模板语法

1. Mustache 语法(插值语法)

  • 如果我们希望把数据显示到模板(template)中,使用最多的语法是 “Mustache”语法 (双大括号) 的文本插值
  • Mustache中不仅仅可以是data中的属性,也可以是一个JavaScript的表达式
<div id="app">
	<!-- 1.基本使用 -->
	<h2>{{ message }}</h2>
	<h2>当前计数: {{ counter }}</h2>
	
	<!-- 2.表达式 -->
	<h2>计数双倍: {{ counter * 2 }}</h2>
	<h2>展示的信息: {{ info.split(" ") }}</h2>
	
	<!-- 3.三元运算符 -->
	<h2>{{ age >= 18 ? "成年人" : "未成年人" }}</h2>
	
	<!-- 4.调用methods中函数 -->
	<h2>{{ formatDate(time) }}</h2>
	
	<!-- 5.注意: 这里不能定义语句 -->
	<!-- <h2>{{ const name = "why" }}</h2> -->
</div>

<script src="./lib/vue.js"></script>
<script>
	// 1.创建app
	const app = Vue.createApp({
		// data: option api
		data: function () {
			return {
				message: "Hello Vue",
				counter: 100,
				info: "my name is why",
				age: 22,
				time: 123,
			};
		},
		methods: {
			formatDate: function (date) {
				return "2022-10-10-" + date;
			},
		},
	});
	
	// 2.挂载app
	app.mount("#app");
</script>

2. 基本指令

2.1 不常见的指令

  • v-once 用于指定元素或者组件只渲染一次
    • 当数据发生变化时,元素或者组件以及其所有的子元素将视为静态内容并且跳过
```
```js
<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: option api
		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>
  • v-text 用于更新元素的 textContent
<div id="app">
	// 展示出的内容:aaHello Worldbbb
	<h2> aa {{message}} bbb</h2>
	// 展示出的内容:Hello World,覆盖aaa
	<h2 v-text="message">aaa</h2>
	
	<span v-text="msg"></span>
	<!-- 等价于 -->
	<span>{{msg}}</span>
</div>
  • v-html 使 html 的内容可以被Vue解析出来
<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;">哈哈哈</span>`
			}
		},
	})
	// 2.挂载app
	app.mount("#app")
</script>
  • v-pre 用于跳过元素和它的子元素的编译过程,显示原始的Mustache标签
<div id="app">
	<div v-pre>
		<h2>{{ message }}</h2>
	</div>
</div>

// 浏览器界面中展示:{{ message }}
  • v-cloak 保持在元素上直到关联组件实例结束编译
    可以隐藏未编译的 Mustache 标签直到组件实例准备完毕
<style>
	[v-cloak] {
		display: none;
	}
</style>

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

2.2 新的指令 v-memo

<div id="app">
	<div v-memo="[name, age]">
		<h2>姓名: {{ name }}</h2>
		<h2>年龄: {{ age }}</h2>
		<h2>身高: {{ height }}</h2>
	</div>
	
	<button @click="updateInfo">改变信息</button>
</div>
  • 若name和age都保持不变,这个div及其子项的所有更新都将被跳过
  • 优化性能

3. v-bind 绑定属性

除了内容需要动态来决定外,我们也希望动态绑定某些属性

3.1 v-bind 绑定基本属性

  • v-bind用于绑定一个或多个属性值,或者向另一个组件传递props值(这个学到组件时再介绍)
  • 可以绑定图片的链接src、网站的链接href、动态绑定一些类、样式等等
  • v-bind有一个对应的语法糖,也就是简写方式::
<div id="app">
	<button @click="switchImg">切换图片</button>
	
	<img v-bind:src="showImgUrl" alt="" />
	<!-- 语法糖写法 -->
	<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/P89nasGuX_gjdBe_2PpQuw==/109951168455849588.jpg",
				imgUrl2: "http://p1.music.126.net/zLNyOeVM5CVdx0OQ9TU11A==/109951168454700007.jpg",
				showImgUrl: "http://p1.music.126.net/P89nasGuX_gjdBe_2PpQuw==/109951168455849588.jpg",
				href: "http://www.baidu.com",
			};
		},
		methods: {
			switchImg: function () {
				this.showImgUrl = this.showImgUrl === this.imgUrl1 ? this.imgUrl2 : this.imgUrl1;
			},
		},
	});
	
	// 2.挂载app
	app.mount("#app");
</script>

3.2 v-bind 绑定 class

  • 基本绑定
  • 对象语法: 我们可以传给 :class (v-bind:class 的简写) 一个对象({ className: Boolean }),以动态地切换 class
    • 若为false,则不会加载该class
  • 数组语法: 我们可以把一个数组传给 :class,以应用一个 class 列表
<style>
	.active {
		color: red;
	}
</style>


<div id="app">
	<!-- 1.基本绑定class -->
	<h2 :class="classes">Hello World</h2>
	
	<!-- 2.动态class可以写对象语法 -->
	<!-- 2.1.对象语法的基本使用(掌握) -->
	<button :class="{ active: isActive, why: true, kobe: false }" @click="btnClick">我是按钮</button>
	
	<!-- 2.2.动态绑定的class是可以和普通的class同时的使用 -->
	<button class="abc cba" :class="{ active: isActive, why: true, kobe: false }" @click="btnClick">我是按钮</button>
	
	<!-- 2.3.动态绑定的class是可以从methods获取 -->
	<button class="abc cba" :class="getDynamicClasses()" @click="btnClick">我是按钮</button>
	
	<!-- 3.动态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>

3.3 v-bind 绑定 style

可以利用v-bind:style来绑定一些CSS内联样式

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

  • 对象语法:{ cssname: cssvalue }
  • 数组语法:[obj1, obj2]
<div id="app">
	<!-- 1.普通的html写法 -->
	<h2 style="color: red; font-size: 30px;">哈哈哈哈</h2>
	
	<!-- 2.style中的某些值, 来自data中 -->
	<!-- 2.1.动态绑定style, 在后面跟上对象类型 (重要)-->
	<h2 :style="{ color: fontColor, fontSize: fontSize }">哈哈哈哈</h2>
	
	<!-- 2.2.动态的绑定属性, 这个属性是一个对象 -->
	<h2 :style="objStyle">呵呵呵呵</h2>
	
	<!-- 3.style的数组语法 -->
	<h2 :style="[objStyle, { backgroundColor: 'purple' }]">嘿嘿嘿嘿</h2>
</div>

<script src="../lib/vue.js"></script>
<script>
	// 1.创建app
	const app = Vue.createApp({
		// data: option api
		data: function() {
			return {
				fontColor: "blue",
				fontSize: '30px',
				objStyle: {
					fontSize: '50px',
					color: "green"
				}
			}
		},
	})
	// 2.挂载app
	app.mount("#app")
</script>

3.4 动态绑定属性名

  • 如果属性名称不是固定的,我们可以使用 :[属性名]=“值” 的格式来定义
<!-- 动态绑定属性:属性名称不是固定的, -->
<div id="app">
	<h2 :[name]="value">Hello World</h2>
</div>

<script src="../lib/vue.js"></script>
<script>
	// 1.创建app
	const app = Vue.createApp({
		// data: option api
		data: function() {
			return {
				name: "class"
			}
		},
	})
	// 2.挂载app
	app.mount("#app")
</script>

3.5 v-bind 绑定对象

可以直接使用 v-bind 绑定一个对象,将一个对象的所有属性,绑定到元素上

<div id="app">
	<h2 :name="name" :age="age" :height="height">Hello World</h2>
	<!-- v-bind绑定对象: 给组件传递参数 -->
	<h2 v-bind="infos">Hello Bind</h2>
</div>

<script src="../lib/vue.js"></script>
<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>

4. 事件绑定 v-on

在前端开发中,我们需要经常和用户进行各种各样的交互。使用v-on指令监听用户发生的事件,比如点击、拖拽、键盘事件等等

4.1 v-on 的基本使用

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

<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")
			},
			divMousemove() {
				console.log("divMousemove")
			}
		}
	})
	// 2.挂载app
	app.mount("#app")
</script>

4.2 v-on 参数传递

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

4.3 v-on 的修饰符

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

<div id="app">
	// 此时点击button按钮不会冒泡到输出divClick
	<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 {}
		},
		methods: {
			btnClick() {
				console.log("btnClick")
			},
			divClick() {
				console.log("divClick")
			}
		}
	})
	// 2.挂载app
	app.mount("#app")
</script>

在这里插入图片描述

5. 条件渲染

在某些情况下,我们需要根据当前的条件决定某些元素或组件是否渲染,这个时候我们就需要进行条件判断

5.1 v-if/else/else-if

v-if的渲染原理:

  • v-if是惰性
  • 当条件为false时,其判断的内容完全不会被渲染或者会被销毁掉
  • 当条件为true时,才会真正渲染条件块中的内容
<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>
	
	<!-- v-else -->
	<div v-else>
		<h2>没有输入个人信息</h2>
		<p>请输入个人信息后, 再进行展示~</p>
	</div>
</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>
<div id="app">
	<h1 v-if="score > 90">优秀</h1>
	<h2 v-else-if="score > 80">良好</h2>
	<h3 v-else-if="score >= 60">及格</h3>
	<h4 v-else>不及格</h4>
</div>

<script src="../lib/vue.js"></script>
<script>
	// 1.创建app
	const app = Vue.createApp({
		// data: option api
		data() {
			return {
				score: 40
			}
		},
	})
	// 2.挂载app
	app.mount("#app")
</script>

5.1.1 template 元素

v-if是一个指令,所以必须将其添加到一个元素上:

  • 如果需要切换多个元素,此时我们将元素放进一个div,但是我们并不希望div这种元素被渲染
  • 这个时候,我们可以选择使用template
  • template元素可以当做不可见的包裹元素,并且在v-if上使用,但是最终template不会被渲染出来

v-for 指令处也可使用template

<div id="app">
	<!-- v-if="条件" -->
	<template class="info" 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>

5.2 v-show

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](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](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>
  • 与v-if用法区别:

    • v-show是不支持template
    • v-show不可以和v-else一起使用
  • 与v-if的本质区别:

    • v-show元素无论是否需要显示到浏览器上,它的DOM实际都是有存在的,只是通过CSS的display属性来进行切换
    • v-if当条件为false时,其对应的原生压根不会被渲染到DOM中
  • 开发中如何进行选择:

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

6. v-for 列表渲染

6.1 v-for 的基本使用

  • v-for的基本格式是 “item in 数组“:数组通常是来自data或者prop,也可以是其他方式
  • 同时可以拿到数组的索引:**”(item, index) in 数组”**,需按顺序
<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>

<script src="../lib/vue.js"></script>
<script>
	// 1.创建app
	const app = Vue.createApp({
		// data: option api
		data() {
			return {
				// 1.movies
				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秒杀, 快来抢购!" },
				]
			}
		},
	})
	// 2.挂载app
	app.mount("#app")
</script>

6.2 v-for 其他的类型

  • v-for也支持遍历对象,并且支持有一二三个参数:
    • 一个参数: “value in object”
    • 二个参数: “(value, key) in object”
    • 三个参数: “(value, key, index) in object”
  • v-for同时也支持数字的遍历
    • 每一个item都是一个数字
  • v-for也可以遍历其他可迭代对象(Iterable)
<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 100">{{item}}</li>
	</ul>
</div>

<script src="../lib/vue.js"></script>
<script>
	// 1.创建app
	const app = Vue.createApp({
		// data: option api
		data() {
			return {
				message: "Hello Vue",
				info: { name: "why", age: 18, height: 1.88 }
			}
		},
	})
	// 2.挂载app
	app.mount("#app")
</script>

6.3 数组更新检测

被侦听的数组发生变更,也会触发视图更新

直接修改原来数组的方法:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()
<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.不修改原数组的方法不能侦听(watch)
				const newNames = this.names.map(item => item + "why")
				this.names = newNames
			}
		}
	})
	// 2.挂载app
	app.mount("#app")
</script>

6.4 v-for 绑定 key 属性

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

6.4.1 VNode & 虚拟 DOM

  • VNode的全称是Virtual Node,也就是虚拟节点
  • 事实上,无论是组件还是元素,它们最终在Vue中表示出来的都是一个个VNode
  • VNode的本质是一个JavaScript的对象
  • template元素 ->解析成 VNode—>转换为真实DOM元素

在这里插入图片描述

  • 如果我们不只是一个简单的div,而是有一大堆的元素,那么它们应该会形成一个VNode Tree
  • template元素—>一个个VNode虚拟节点—>VNode Tree –>虚拟DOM—>真实DOM

在这里插入图片描述

虚拟DOM的一大作用:方便代码跨平台运行

在这里插入图片描述

6.4.2 key 的作用

案例:点击按钮插入f

<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: option api
		data() {
			return {
				letters: ["a", "b", "c", "d", "e"]
			}
		},
		methods: {
			insertF() {
				this.letters.splice(2, 0, "f")
			}
		}
	})
	// 2.挂载app
	app.mount("#app")
</script>
  • 没有key的操作:
    • diff算法, 后续VNode复用性不强

在这里插入图片描述

  • 有key的操作:
    • 根据key找到之前的VNode进行复用;
    • 没有VNode可以复用, 再创建新的VNode

在这里插入图片描述
在这里插入图片描述

三、Options API

1. 计算属性 computed

1.1 复杂数据的处理方式

在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示

  • mustache插值语法自己写逻辑:在模板中放入太多的逻辑会让模板过重和难以维护
  • methods完成逻辑:所有的data使用过程都会变成了一个方法的调用

1.2 computed 用法

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

  • computed: { fullname() {} }
<div id="app">
	<!-- 插值语法表达式直接进行拼接 -->
	<!-- 1.拼接名字 -->
	<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() {
			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>

1.3 computed 和 methods 区别

  • computed底层会缓存, 性能更高
    • 计算属性会基于它们的依赖关系进行缓存;
    • 在数据不发生变化时,计算属性是不需要重新计算的
    • 但是如果依赖的数据发生变化,在使用时,计算属性依然会重新进行计算
```
```js
<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>
	<!-- 修改name值 -->
	<button @click="changeLastname">修改lastname</button>
</div>

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

在这里插入图片描述

1.4 计算属性的 setter 和 getter (了解)

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

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

2. 侦听器 watch

希望在代码逻辑中监听某个数据的变化,这个时候就需要用侦听器watch来完成

2.1 基本侦听 watch

<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() {
			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.如果是对象类型, 那么拿到的是代理对象 Proxy
				console.log("info数据发生了变化:", newValue, oldValue)
				console.log(newValue.name, oldValue.name)
				// 3.获取原生对象
				console.log(Vue.toRaw(newValue))
			}
		}
	})
	// 2.挂载app
	app.mount("#app")
</script>

2.2 侦听器 watch 的配置选项

若修改info.name,用watch侦听info,无法侦听到

  • watch只侦听info的引用变化,对于内部属性的变化是不会做出响应的
  • 这个时候我们可以使用一个选项deep进行更深层的侦听
  • 还有另外一个属性,是希望一开始就会立即执行一次
    • 使用immediate选项
    • 无论后面数据是否有变化,侦听的函数都会有限执行一次
```
```js
<div id="app">
	<h2>{{ info.name }}</h2>
	<button @click="changeInfo">修改info</button>
</div>

<script src="[../lib/vue.js](../lib/vue.js)"></script>
<script>
	// 1.创建app
	const app = Vue.createApp({
		// data: option api
		data() {
			return {
				info: { name: "why", age: 18 }
			}
		},
		methods: {
			changeInfo() {
				// 创建一个新对象, 赋值给info
				// this.info = { name: "kobe" }
				
				// 直接修改原对象某一个属性
				this.info.name = "kobe"
			}
		},
		watch: {
			// 默认watch监听不会进行深度监听
			// info(newValue, oldValue) {
				// console.log("侦听到info改变:", newValue, oldValue)
			// }
			
			// 进行深度监听
			info: {
				handler(newValue, oldValue) {
					console.log("侦听到info改变:", newValue, oldValue) // 侦听到info改变: Proxy {name: 'kobe', age: 18} Proxy {name: 'kobe', age: 18}
					console.log(newValue === oldValue)
				}, // true
				// 1.info进行深度监听
				deep: true,
				// 2.第一次渲染直接执行一次监听器
				immediate: true
			},
			"info.name": function(newValue, oldValue) {
				console.log("name发生改变:", newValue, oldValue) // name发生改变: kobe why
			}
		}
	})
	// 2.挂载app
	app.mount("#app")
</script>

2.3 其他的写法

在created的生命周期(后续会讲到)中,使用 this.$watchs 来侦听

  • 第一个参数是要侦听的源
  • 第二个参数是侦听的回调函数callback
  • 第三个参数是额外的其他选项,比如deep、immediate
<div id="app">
	<h2>{{message}}</h2>
	<button @click="changeMessage">修改message</button>
</div>

<script src="[../lib/vue.js](../lib/vue.js)"></script>
<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>

四、v-model 双向绑定

1. v-model 基本使用

表单提交是开发中非常常见的功能,也是和用户交互的重要手段

  • 这要求我们可以在代码逻辑中获取到用户提交的数据,我们通常会使用v-model指令来完成
    • v-model指令可以在表单 input、textarea以及select元素上创建双向数据绑定
    • v-model 本质上不过是语法糖,它负责监听用户的输入事件来更新数据,并在某种极端场景下进行一些特殊处理
  • v-model的原理是背后有两个操作:
    • v-bind绑定value属性的值
    • v-on绑定input事件监听到函数中,函数会获取最新的值赋值到绑定的属性中
```
```js
<input type="text" :value="message" @input="inputChange">

等价于

<input type="text" v-model="message">

2. v-model 绑定其他类型

2.1 textarea

<div id="app">
	<textarea cols="30" rows="10" v-model="content"></textarea>
	<p>输入的内容: {{content}}</p>
</div>

<script src="../lib/vue.js"></script>
<script>
	// 1.创建app
	const app = Vue.createApp({
		// data: option api
		data() {
			return {
				content: ""
			}
		},
	})
	// 2.挂载app
	app.mount("#app")
</script>

2.2 checkbox

  • 单个勾选框:
    • v-model即为布尔值
    • 此时input的value属性并不影响v-model的值。
  • 多个复选框:
    • 当是多个复选框时,因为可以选中多个,所以对应的data中属性是一个数组
    • 当选中某一个时,就会将input的value添加到数组中
```
```js
<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>
		<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>

2.3 radio

此时不需要通过添加name属性使两选项排他,添加v-model可以自动排他

<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 src="../lib/vue.js"></script>
<script>
	// 1.创建app
	const app = Vue.createApp({
		// data: option api
		data() {
			return {
				gender: "female" // 默认选女
			}
		},
	})
	// 2.挂载app
	app.mount("#app")
</script>

2.4 select

  • 单选:只能选中一个值
    • v-model绑定的是一个
    • 当我们选中option中的一个时,会将它对应的value赋值到fruit中
  • 多选:可以选中多个值
    • v-model绑定的是一个数组
    • 当选中多个值时,就会将选中的option对应的value添加到数组fruit中
```
```js
<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 src="../lib/vue.js"></script>
<script>
	// 1.创建app
	const app = Vue.createApp({
		// data: option api
		data() {
			return {
				fruit: "orange",
				fruits: []
			}
		},
	})
	// 2.挂载app
	app.mount("#app")
</script>

3. v-model 值绑定

在真实开发中,我们的数据可能是来自服务器的,那么我们就可以先将值请求下来,绑定到data返回的对象中,再通过v-bind来进行值的绑定,这个过程就是值绑定

<div id="app">
	<!-- 1.select的值绑定 -->
	<select multiple size="3" v-model="fruits">
		<option v-for="item in allFruits"
		:key="item.value"
		:value="item.value">
			{{item.text}}
		</option>
	</select>
	
	<h2>多选: {{fruits}}</h2>
	<hr>
	
	<!-- 2.checkbox的值绑定 -->
	<div class="hobbies">
		<h2>请选择你的爱好:</h2>
		<template v-for="item in allHobbies" :key="item.value">
			<label :for="item.value">
				<input :id="item.value" type="checkbox" v-model="hobbies" :value="item.value"> {{item.text}}
			</label>
		</template>
		
		<h2>爱好: {{hobbies}}</h2>
	</div>
</div>

<script src="../lib/vue.js"></script>
<script>
	// 1.创建app
	const app = Vue.createApp({
		// data: option api
		data() {
			return {
				// 水果
				allFruits: [
					{ value: "apple", text: "苹果" },
					{ value: "orange", text: "橘子" },
					{ value: "banana", text: "香蕉" },
				],
				fruits: [],
				// 爱好
				allHobbies: [
					{ value: "sing", text: "唱" },
					{ value: "jump", text: "跳" },
				],
				hobbies: []
			}
		}
	})
	// 2.挂载app
	app.mount("#app")
</script>

4. v-model 修饰符

4.1 lazy

  • 默认情况下,v-model在进行双向绑定时,绑定的是input事件,那么会在每次内容输入后就将最新的值和绑定的属性进行同步
  • 如果我们在v-model后跟上lazy修饰符(v-model.lazy),那么会将绑定的事件切换为 change 事件,只有在提交时(比如回车)才会触发

4.2 number

  • v-model绑定后的值总是string类型,即使在我们设置input的type为number也是string类型;
  • 如果我们希望转换为数字类型,那么可以使用 .number 修饰符

4.3 trim

如果要自动过滤用户输入的空白字符,可以给v-model添加 trim 修饰符

这些修饰符可以叠加使用:v-model.lazy.trim


文章作者: Nanying
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Nanying !
评论
  目录