Vue

-
-
2024-06-10

准备活动

Vue依赖NodeJS,需先行准备node环境

  • 使用npm init vue 创建vue项目
  • 进入项目目录
  • npm install 安装环境
  • npm run dev 运行

linux的npm作为系统使用时需sudo

 

Vue项目的目录结构

 

vue-project
├── index.html		// HTML入口文件
├── jsconfig.json
├── node_modules	//	Vue项目的运行依赖文件
├── package.json		// 项目信息描述文件
├── package-lock.json
├── public			// 资源文件
│   └── favicon.ico
├── README.md		// 项目注释文件
├── src				// 源码文件夹
│   ├── App.vue
│   ├── assets
│   ├── components
│   └── main.js
└── vite.config.js	// Vue配置文件

模板语法

Vue使用一种基于HTML的模板语法,使我们能够声明式的将其组件实例的数据绑定到呈现的DOM上。所有的Vue模板都是语法层面合法的HTML,可以被符合规范的浏览器和HTML解析器解析。

文本插值

最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法(即双大括号):

<template>
	<p>{{msg}}</p>
</template>
<script>
export default{
	data(){
		return {
			msg:"神奇的魔法"
		}
	}
}
</script>

使用JavaScript表达式

每个绑定仅支持单一表达式,也就是一段能够被求值的JavaScript代码。一个简单的判断方法是是否可以合法地写在return后面。

<template>
	<p>{{number +1}}</p>
	<p>{{ok?'YES':'No'}}</p>
	<p>{{message.split(' ').reverse().join(' ')}}</p>
</template>
<script>
export default{
	data(){
		return{
			number:10,
			ok:true,
			message:"大家好"
		}
	}
}
</script>

无效

<!-- 这是一个语句,而表达式 -->
{{var a = 1 }}

<!-- 条件控制也不支持,请使用三元表达式 -->
{{ if(ok) {return message}}}

<!-- 不建议在模板语法中使用逻辑表达式,逻辑部分全在JS中完成 -->

原始HTML

双花括号将会将数据插值为纯文本,而不是HTML.若想插入HTML,你需要使用v-html指令。

<template>
  <p>纯文本:{{rawHtml}}}</p>
  <p>属性:<span v-html="rawHtml"> </span></p>
</template>
<script>
export default{
	data(){
	 	return{
	 		rawHtml:"<a href='https://taitang.com'>我的博客<a>"
	 		}
	 }
}
</script>

属性绑定

双大括号你能在HTML attributes中使用。要想响应式的绑定一个attribute,应该使用v-bind指令。

<template>
	<div v-bind:id="dynamicId" v-bind:calss="dynamicClass">APPID</div>
</template>
<script>
export default{
	data(){
		return{
			dynamicId;"appid",
			dynamicClass:"appClass"
		}
	}
}
</script>
<style>
.appclass{
	color: red;
	font-size: 20px;
}
</style>

v-bind指令指示Vue将元素的id attribute 与组件的dynamicId 属性保持一致,如果绑定的值是null或者undefined,那么该attribute将会从渲染的元素上移除。

简写

因为v-bind非常常用,我们提供了特定的简写语法

<div :id="dynamisId" :class="dynamicClass"> </div>

布尔型 Attribute

布尔型attribute 依据true / false 值来决定attribute是否应该存在与该元素上,disabled就是最常见的离子之一。

<template>
<button :disabled="isButtonDisabled">Button</button>
</template>
<script>
export default{
	data(){
		return{
			isButtonDisabled:true
		}
	}
}
</script>

动态绑定多个值

如果你有一个像这样一个包含多个attribute的JavaScript对象

<template>
	<div v-bind="objectOfAttrs">动态绑定</div>
</template>
<script>
export default [
	data(){
		return{
			objectOfAttrs:{
				id:'container',
				class:'wrapper'
			}
		}
	}
}
</script>

条件渲染

在Vue中,提供了条件渲染,这类似于JavaScript的条件语句。

  • v-if
  • v-else
  • v-else-if
  • v-show

v-if

v-if 指令用于条件性渲染一块内容,这块内容只会在指令表达式返回真值是才会被渲染。

<template>
	<div v-if="flag"> 看不见我<div>
</template>
<script>
export default{
	data(){
		return {
			flag:true
		}
	}
}
</script>

v-else

你也可以使用v-else 为v-if添加一个else区块

<template>
	<div v-if="flag"> 看不见我<div>
	<div v-else>看见我啦</div> 
</template>
<script>
export default{
	data(){
		return {
			flag:true
		}
	}
}
</script>

v-else-if

顾名思义,v-else-if提供的是相应于v-if的else if区块,它可以连续多次使用

<template>
	<div v-if="type ==='A'"> A<div>
	<div v-else-if="type === 'B'">B</div>
	<div v-else-if="type === 'C'">C</div>
	<div v-else>Not A/B/C</div> 
</template>
<script>
export default{
	data(){
		return {
			type:"D"
		}
	}
}
</script>

v-show

另一个可以用来按条件显示一个元素的指令是v-show,其用法基本一样。

<template>
	<div v-show="flag"> 看不见我<div>
</template>
<script>
export default{
	data(){
		return {
			flag:true
		}
	}
}
</script>

PS:

v-if是“真实的 ”按条件渲染 ,因为它确保了在切换时,条件区块内的时间监听器和子组件都会被被销毁与重建。

v-if也是惰性的:如果在初次渲染时条件值为false,则不会做任何事。条件区块只有当条件首次变为true时才被渲染。

相比之下,v-show简单许多,元素无论初始条件如何,始终都会被渲染,只有CSS display属性会被切换。

总的来说。v-if有更高的切换开销,而v-show有更高的出事渲染开销。因此如果需要频繁切换,则使用v-show较好;如果在运行时绑定条件很少改变,则v-if会更合适。

v-if是删除/添加节点,v-show是隐藏/显示节点,有所区别。

列表渲染

v-for

我们可以使用v-for指令基于一个数组来渲染一个列表。v-for指令的值需要使用item in items 形式的特殊语法,其中items是源数据的数组,而item是迭代项的别名。

<template>
	<div>
		<p v-for="item in names">{{item}}</p>
	</div>
</template>
<script>
export default{
	data(){
		return{
			names:["哦","啊","诶"]
		}
	}
}
</script>

v-for也支持使用第二个参数表示当前项的位置索引。

<template>
	<div>
		<p v-for="(item,index) in names">{{index}}、 {{item}}</p>
	</div>
</template>
<script>
export default{
	data(){
		return{
			names:["哦","啊","诶"]
		}
	}
}
</script>

复杂数据

大多数情况,我们渲染的数据源来自于网络请求,也就是JSON格式

<template>
	<div v-for="item in result">
		<p>{{item.title}}</p>
		<img :src="item.avator" alt="">
	</div>
</template>
<script>
export default{
	data(){
		return{
			result:[{
				"id":12333,
				"title":"鄂尔多斯",
				"avator":"https://tsaitang.com/logo"
			},{
				"id":45666,
				"title":"乌鲁木齐",
				"avator":"https://tsaitang.com/logo"
			}]
		}
	}
}
</script>

你也可以使用of作为分隔符来代替in,这更接近JavaScript的迭代器语法。

<div v-for="item of items"></div>

v-for 与对象

你也可以使用v-for来遍历一各对象的所有属性

<template>
	<div>
		<!-- 使用item in userInfo则遍历对象的value值 -->
		<p v-for="(value, key, index) in userInfo">{{index}}、{{key}}:{{value}}</p>
	</div>
</template>
<script>
export default{
	data(){
		return{
			userInfo:{
				name:"kater",
				age:20
			}
		}
	}
}
</script>

 通过key管理状态

Vue默认按照“就地更新”的策略来更新通过v-for渲染的元素列表。当数据项的顺序改变时,Vue不会随之移动DOM元素的顺序,而是就地地更新每个元素,确保它们在原本指定的索引位置上渲染。

为了给Vue一个提示,以便它可以跟随每个节点的标识,从而重用和重新排序现有的元素,你需要为每个元素对应的块提供一个唯一的key attribute:

<template>
	<div>
		<p v-for="(item,index) in names" :key="index">{{item}}</p>
	</div>
</template>
<script>
export default{
	data(){
		return{
			names:["哦","啊","诶"]
		}
	}
}
</script>

事件处理

我们可以使用v-on指令(简写为@)来监听DOM事件,并在事件触发时执行对应的JavaScript。

用法:v-on:click="methodName"或 @click=“handler”。

事件处理器的值可以是

  • 内联事件处理器:事件被触发时执行的内联JavaScript语句(与onclick类似)。
  • 方法事件处理器:一个指向组件上定义的方法的属性名或是路径。

内联事件处理器

内联事件处理器通常用于简单场景

<template>
	<button @click="count++">add 1 </button>
	<p>Count is:{{count}}</p>
</template>
<script>
export default{
	data(){
		return {
			count:0
		}
	}
}
</script>

方法事件处理器

<template>
	<button @click="addCount">addCount</button>
	<p>Count is:{{count}}</p>
</template>
<script>
export default{
	data(){
		return {
			count:0
		}
	},
	<!-- 方法、函数 -->
	methods:{
		addCount(){
			<!-- this.成员 读取data中数据 -->
			this.count+=1
		}
	}
}
</script>

事件参数

事件参数可以获取event对象和通过事件传递数据

获取event对象

<template>
	<button @click="addCount">addCount</button>
	<p>Count is:{{count}}</p>
</template>
<script>
export default{
	data(){
		return {
			count:0
		}
	},
	methods:{
		// event对象	
		addCount(e){	
			// Vue中的event对象就是原生JS的Event对象。
			console.log(e.target.innerHTML = "Add" + this.count)
			this.count+=1
		}
	}
}
</script>

传递参数

<template>
	<button @click="addCount('hello')">addCount</button>
	<p>Count is:{{count}}</p>
</template>
<script>
export default{
	data(){
		return {
			count:0
		}
	},
	methods:{
		// event对象	
		addCount(message){	
			// Vue中的event对象就是原生JS的Event对象。
			console.log(message)
			this.count+=1
		}
	}
}
</script>

获取列表中的内容

<template>
	<p @onclick=“getNameHandle(item)” v-for="(item,index) in names" :key="index">{{ item }}</p>
</template>
<script>
export default{
	data(){
		return {
			names:["iwen","ime","frank"]
		}
	},
	methods:{
			getNameHandle(name){
				console.log(name);
			}
		}
	}
}
</script>

传递参数过程中获取event

<template>
	<p @onclick=“getNameHandle(item,$event)” v-for="(item,index) in names" :key="index">{{ item }}</p>
</template>
<script>
export default{
	data(){
		return {
			names:["iwen","ime","frank"]
		}
	},
	methods:{
		getNameHandle(name,e){
			console.log(name,e);
			}
		}
	}
}
</script>

事件修饰符

在处理事件是调用event.preventDefault()或是 event.stopPropagation()是很常见的。尽管我们可以直接在方法内调用,但如果方法能更专注与数据逻辑而不是去处理DOM事件的细节会更好

为解决这一问题,Vue为v-on提供了事件修饰符,常用以下几个:

  • .stop  //  阻止事件冒泡
  • .prevent  // 阻止默认事件
  • .once  // 阻止一次性事件
  • .enter  // 阻止键盘事件

阻止默认事件

<template>
	<div>
		<a @click.prevent=“clickHandle”  href=“https://www.tsaitang.com”>阻止默认事件</a>
	</div>
</template>
<script>
export default{
	data(){
		return {
		}
	},
	methods:{
		clickHandle(){
		// 阻止默认事件 
		// e.preventDefault();
			console.log("阻止点击");
			}
		}
	}
}
</script>

阻止事件冒泡

<template>
	<div  @click="clickDiv">
		<p @click.stop="clickP">冒泡测试</p>
	</div>
</template>
<script>
export default{
	data(){
		return {
		}
	},
	methods:{
		clickDiv(){
			console.log("点击div");
			},
		clickP(e){
				e.stopPropagation(); // 阻止事件冒泡
				conlose.log("点击p");	
			}
		}
	}
}
</script>

数组变化的侦测

变更方法

Vue能够侦听响应式数组的变更方法,并在它们被调用的时出发相关的更新。这些变更方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()
<template>
	<div>
		<p v-for="(item,index) in names" :key="index">{{item}}</p>
		<button @click="clickAddNamesHandle">增加数据</button>
	</div>
</template>
<script>
export default{
	data(){
		return {
		}
	},
	methods:{
		clickAddNamesHande(){
			this.names.push("sakura")
		}
	}
}
</script>

替换一个数组

变更方法,顾名思义,就是会对调用它们的原数组进行变更。相对地,也有一些不可变(immutable)方法,例如filter(),concat();和slice(),这些都不会更改原数组,而总是返回一个新数组。当遇到的是非变更方法时,我们需要将旧的数组替换为新的。

// `items` 是一个数组的 ref
items.value = items.value.filter((item) => item.message.match(/Foo/))

计算属性

模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。因此我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑

<template>
	<h3>{{data.id}}</h3>
	<p>{{dataContent}}</p>
</template>
<script>
export default{
	data(){
		return {
			data:{
				id:12333,
				content:["name","age","gender"]
			}
		}
	},
	//  计算属性
	computed:{
		dataContent(){
			return this.data.content.length >0?'Yes':'No'
		}
	}
}
</script>

与方法区别:

计算属性:计算属性值会基于响应式依赖被缓存。一个计算属性近会在其响应式依赖更新时才重新计算

方法:方法调用总是会在重渲染时再次执行函数

class绑定

数据绑定的一个常见需求场景是操纵元素的CSS class 列表,因为class是attribute, 我们可以和其他attribute一样使用v-bind将它们和动态的字符串绑定。但是在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此,Vue专门为class的v-bind用法提供了特殊的功能增强,除了字符串外,表达式的值也可以是对象或数组。

绑定对象

<template>
	<div :class="{active :isActive,'text-danger':hasError}">isActive</div>
</template>
<script>
export default{
	data(){
		return {
			isActive :true,
			hasError:true
			}
		}
	}
}
</script>

绑定多个对象

<template>
	<div :class="classObject">isActive</div>
</template>
<script>
export default{
	data(){
		return {
		classObject:{
				active :true,
				'text-danger':true
				}
			}
		}
	}
}
</script>

 绑定数组

<template>
	<div :class="[activeClass,errorClass]">isActive</div>
</template>
<script>
export default{
	data(){
		return {
		classObject:{
				activeClass:'active',
				errorClass:'text-danger'
				}
			}
		}
	}
}
</script>

如果你也想在数组中有条件的渲染某个class,你也可以使用三元表达式:

<template>
	<div :class="[isActive ? 'active' : ' ']">isActive</div>
</template>
<script>
export default{
	data(){
		return {
		classObject:{
					isActive:true
				}
			}
		}
	}
}
</script>

数组和对象

<template>
	<div :class="[{'active':isActive},errorClass]">isActive</div>
</template>
<script>
export default{
	data(){
		return {
		classObject:{
				isActive:true,
				errorClass:'text-danger'
				}
			}
		}
	}
}
</script>

数组和对象嵌套时只能数组嵌套对象,不能反其道而行之。

 

style绑定

数据绑定的一个常见需求场景是操纵元素的CSS class 列表,因为class是attribute, 我们可以和其他attribute一样使用v-bind将它们和动态的字符串绑定。但是在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此,Vue专门为class的v-bind用法提供了特殊的功能增强,除了字符串外,表达式的值也可以是对象或数组。

<template>
	<div :style="{'color':activeColor,fontsize:fontsize+'px'}">style绑定</div>
</template>
<script>
export default{
	data(){
		return {
				activeColor:'red',
				fontsize:30
			}
		}
	}
}
</script>
<template>
	<div :style="styleObject“>style绑定</div>
</template>
<script>
export default{
	data(){
		return {
				styleObject:{
					color:'red',
					fontSize:'30px'
				}
			}
		}
	}
}
</script>

侦听器

我们可以使用watch选项在每次响应式属性发生变化时触发一个函数

<template>
	<p>{{mesage}}</p>
	<button @click="clickHandle">修改数据</botton>
</template>
<script>
export default{
	data(){
		return {
			message:"前端"
			}		
		}
	},
	methods:{
		clickHandle(){
			this.mesage = "后端"
		}
	},
	watch:{  // 与数据名对应
		message(newVlue,oldValue){
			console.log(newValue,oldValue);
		}
	}
}
</script>

函数名必须与监听的数据对象名保持一致

表单的输入绑定

在前端处理表单时,我们常常需要将表单输入框的内容同步给JavaScript中[相应的变量.手动连接值绑定和更改事件监听器可能会很麻烦,v-model指令帮我们简化了这一步骤

<template>
	<input type="text" v-model="mesage">
	<p>{{mesage}}</p>
</template>
<script>
export default{
	data(){
		return {
		message:""
		}
	}
}
</script>

复选框

<template>
	<input type="checkbox" id="checkbbox" v-model="checked">
	<label for="checkbox">{{checked}}</label>
</template>
<script>
export default{
	data(){
		return {
		checked:true
		}
	}
}
</script>

修饰符 

v-model也提供了修饰符: .lazy、.number、.trim

.lazy

默认情况下,v-model会在每次input事件后更新数据。你可以添加lazy修饰符来改为在每次change事件后更新数据。

<template>
	<input type="text"  v-model.lazy="message">
	<p>{{message}}</p>
</template>
<script>
export default{
	data(){
		return {
			message:""
		}
	}
}
</script>

模板引用

虽然Vue的生命性渲染模型为你抽象了大部分将对DOM的操作,但在某些情况下,我们仍然需要直接访问底层DOM元素,要实现这一点,我们可以使用特殊的ref attribute

挂载结束后引用都会被暴露在this.$ref之上

// 没有特别的需求不要操作DOM

<template>
	<div ref="container" class="container">{{content}}</div>
	<button @click="getElementHandle">获取元素</botton>
</template>
<script>
export default{
	data(){
		return {
		content:"内容"
		}
	},
	mothods:{
		getElementHandle(){
			// innerHTML是原生JS的属性
			this.$refs.container.innerHTML = "新内容"
		}
	}
}
</script>

组件组成

组件最大的优势就是可复用性

当使用构建步骤时,我们一般会将Vue组件定义在一个单独的.vue文件中,这就被叫做单文件组件(SFC)

组件组成结构

<template>
	<div>承载标签</div>
</template>
<script>
export default{
}
</script>
<style scoped>
<!-- scoped 让当前样式只在当前组件生效-->
</style>

组件引用

<template>
	<TemplateDemo>
</template>
<script>
import TemplateDemo from "./components/TemplateDemo.vue"
export default{
	components:{
		TemplateDemo
	}
}
</script>
<style scoped>
</style>

组件嵌套关系

组件允许我们将UI划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际的应用中,组件常常被组织成层层嵌套的树状结构。

这和我们嵌套的HTML元素的方式类似,Vue实现了自己的组件模型,使我们可以在每个组件内封装自定义的内容逻辑。

创建组件及引用关系

Header

<template>
	<h3>Header</h3>
</template>
<style scoped>
h3{		
width:100%
height:100px;
border:5px solid #999;
text-align:center;
line-height:100px;
box-sizing:border-box;
}
</style>

Main

<template>
	<div class="main">
		<h3>Main</h3>
		<Article></Article>
	</div>
</template>
<style scoped>
.main{
	float:left;
	width:70%;
	height:400px;
	border 5px solid #999;
	box-sizing:border-box;
	border-top:0px;
}
</style>

Aside

<template>
	<div class="aside">
		<h3>Aside</h3>
		<Item />
		<Item />
	</div>
</template>
<style scoped>
.aside{
	float:right;
	width:30%;
	height:400px;
	border 5px solid #999;
	box-sizing:border-box;
	border-top:0px;
	border-left:0px;
}
</style>

Article

<template>
	<h3>Aside</h3>
</template>
<style scoped>
h3{
	width:80%;
	margin: 0 auto;
	text-align:center;
	line-height:100px;
	box-sizing:border-box;
	margin-top: 50px;
	background:#999;
}
</style>

Item

<template>
	<h3>Item</h3>
</template>
<style scoped>
.h3{
	width:80%;
	margin:0 auto;
	text-align:center;
	line-height:100px;
	box-sizing:border-box;
	margin-top:10px;
	background:#999;
}
</style>

组件的注册方式

一个Vue的组件在使用前需要先被注册,这样Vue才能在渲染模板时找到其对应的实现。组件的注册有两种方式:全局注册和局部注册。

全局注册

import { createApp } from 'vue'
import App from './App.vue'
import GlobalCpmponent from "./components/GlobalComponent.vue"

const app = createApp(App);
app.component("GlobalComponent",Globalcomponent)
<!-- 在这注册-->
app.mount('#app')
<template>
	<h3>全局应用组件</h3>
</template>

局部注册

全局注册虽然方便,但要注意以下问题

  • 全局注册,但没有使用的组件无法在打包时被自动移除(tree-shaking)。打包后依旧在JS文件中
  • 全局注册在大型项目中使项目的依赖关系变得不那么明确。在父组件中使用子组件时,不太容易定位子组件的实现。和使用过多的全局变量一样,这可能会影响应用长期的可维护性。

局部注册需要使用components选项

<template>
	<GlobalComponent />
</template>
<script>
import GlobalComponent from "./components/GlobalComponent.vue"
export default{
	components:{
		GlobalComponent
	}
}
</script>
<style scoped>

</style>

组件传递数据 Props

组件与组件之间不是完全独立的,而是有交集的,那就是总是充满与组件之间是可以传递数据的

传递数据的解决方案就是props

<template>
	<h3>传递数据</h3>
	<ComponentB title="传递数据" />
</template>
<script>
	import ComponentB from "./components/ComponentB.vue"
	export default{
		components:{
			ComponentB
		}
	}
</script>
<style scoped>
</style>
<template>
	<h3>传递数据</h3>
	<p>{{title}}</p>
</template>
<script>
	export default{
		props:["title"]
	}
</script>
<style scoped>
</style>

动态数据传递

<template>
	<h3>ComponentA</h3>
	<ComponentB :title="message" />
</template>
<script>
import ComponentB from "./components/ComponentB.vue"
	export default{
		data(){
			return{
				message:"动态数据"
			}
		},
		components:{
			ComponentB
		}
	}
</script>
<style scoped>
</style>
<template>
	<h3>传递数据</h3>
	<p>{{title}}</p>
</template>
<script>
	export default{
		props:["title"]
	}
</script>
<style scoped>
</style>

props传递数据只能由父到子,由上到下,不能反向传递。

 

组件传递多种数据类型

通过prop传递数据,不仅可以传递字符串类型的数据,还可以是其类型,例如:数字、对象、数组等

但实际上任何类型的值都可以作为props的值被传递

Number

<template>
	<h3>ComponentA</h3>
	<ComponentB :age="age" />
</template>
<script>
import ComponentB from "./components/ComponentB.vue"
	export default{
		data(){
			return{
				age:20
			}
		},
		components:{
			ComponentB
		}
	}
</script>
<style scoped>
</style>
<template>
	<h3>ComponentB</h3>
	<p>{{ age }}</p>
</template>
<script>
	export default{
		props:["age"]
	}
</script>
<style scoped>
</style>

Array

<template>
	<h3>ComponentA</h3>
	<ComponentB :names="names" />
</template>
<script>
import ComponentB from "./components/ComponentB.vue"
	export default{
		data(){
			return{
				names:["iwen","ime","frank"]
			}
		},
		components:{
			ComponentB
		}
	}
</script>
<style scoped>
</style>

<template>
	<h3>ComponentB</h3>
	<p v-for="(item,index) of names" :key="index">{{ item }}</p>
</template>
<script>
	export default{
		props:["names"]
	}
</script>
<style scoped>
</style>

Object

<template>
	<h3>ComponentA</h3>
	<ComponentB :userInfo="userInfo" />
</template>
<script>
import ComponentB from "./components/ComponentB.vue"
	export default{
		data(){
			return{
				userInfo:{
					name:"iwen",
					age:20
				}
			}
		},
		components:{
			ComponentB
		}
	}
</script>
<style scoped>
</style>

<template>
	<h3>ComponentB</h3>
	<p>{{ userInfo.name }}</p>
	<p>{{ userInfo.age }}</p>
</template>
<script>
export default{
		props:["userInfo"]
	}
</script>
<style scoped>
</style>

组件传递数据Props的校验

Vue组件可以更细致地声明对传入的props的校验要求

<template>
	<h3>ComponentA</h3>
	<ComponentB :title="Props校验" :userInfo="userInfo" />
</template>
<script>
import ComponentB from "./components/ComponentB.vue"
	export default{
		data(){
			return{
				userInfo:{
					name:"iwen",
					age:20
				}
			}
		},
		components:{
			ComponentB
		}
	}
</script>
<style scoped>
</style>

默认值

<template>
	<h3>ComponentB</h3>
	<p>{{ title }}</p>
	<p>{{ userInfo.name }}</p>
	<p>{{ userInfo.age }}</p>
	<p>{{age}}</p>
</template>
<script>
export default{
		props:{
			title:{
				type:String
			},
			userInfo:{
				type:Object,
				default(){
					return{}
				}
			},
			age:{
				type:Number,
				default:20
			}
		}
	}
</script>
<style scoped>
</style>

必选项

<template>
	<h3>ComponentB</h3>
	<p>{{ title }}</p>
	<p>{{ userInfo.name }}</p>
	<p>{{ userInfo.age }}</p>
	<p>{{age}}</p>
</template>
<script>
export default{
		props:{
			title:{
				type:String,
				required:true
			},
			userInfo:{
				type:Object,
				default(){
					return{}
				}
			},
			age:{
				type:Number,
				default:20
			}
		}
	}
</script>
<style scoped>
</style>

注意!prop是只读的!

<template>
	<h3>ComponentB</h3>
	<p>{{ title }}</p>
	<button @click="updateHandle">修改数据</button>
</template>
<script>
export default{
		props:{
			title:{
				type:String,
				required:true;
			}
		},
		updateHandle(){
			this.title="不允许修改"
		}
	}
</script>
<style scoped>
</style>

组件事件

在组件的模板表达式中,可以直接使用$emit方法触发自定义事件

触发自定义事件的目的是组件之间传递数据

<template>
	<h3>ComponentA</h3>
	<ComponentB @some-event="getHandle" />
	<p>ComponrentA接收的数据:{{ message }}</p>
</template>
<script>
import ComponentB from "./components/ComponentB.vue"
	export default{
		data(){
			return{
				message:“”
			}
		},
		components:{
			ComponentB
		},
		methods:{
			getHandle(data){
				this.message = data;
			}
		}
	}
</script>
<style scoped>
</style>
<template>
	<h3>ComponentB</h3>
	<button @click="sendHandle">发送数据</button>
</template>
<script>
export default{
		methods:{
			sendHandle(){
				this.$emit("someEvent","ComponentB的数据")
			}
		}
	}
</script>
<style scoped>
</style>

注意:

组件间传递数据的方案

上传下:props

下传上:自定义事件(this.$emit)

 

组件事件配合v-model使用

如果是用户输入,我们希望在获取数据的同时发送数据配合v-midel使用

<template>
	<h3>ComponentA</h3>
	<ComponentB @some-event="getHandle" />
	<p>ComponrentA接收的数据:{{ message }}</p>
</template>
<script>
import ComponentB from "./components/ComponentB.vue"
	export default{
		data(){
			return{
				message:“”
			}
		},
		components:{
			ComponentB
		},
		methods:{
			getHandle(data){
				this.message = data;
				}
		}
	}
</script>
<style scoped>
</style>
<template>
	<h3>ComponentB</h3>
	<imnput v-model="searchText">
</template>
<script>
export default{
		data(){
			return{
				searchText:""
			}
		},
		methods:{
			searchText(newValue,oldValue){
				this.$emit("someEvent","newValue")
			}
		}
	}
</script>
<style scoped>
</style>

组件数据传传递

props也可以实现下层传上层:传递函数

<template>
	<ComponentB :onFnEvent="fn">
	<p>{{ message }}</p>
</template>
<script>
import ComponentB from "./components/ComponentB.vue"
	export default{
		data(){
			return{
				message:“”
			}
		},
		components:{
			ComponentB
		},
		methods:{
			fn(data){
				this.message = data;
				}
		}
	}
</script>
<style scoped>
</style>
<template>
	<h3>ComponentB</h3>
	<p>{{ onFnEvent('测试数据')}}</p>
</template>
<script>
export default{
		props:{
			onFnEvent(){
				type:Function
			}
		}
	}
</script>
<style scoped>
</style>

透传属性

“透传attribute”是指传递给一个组件,却没有被该组件声明为prop或者emits的attritube或者v-on事件的监听器。最常见的离子就是class、style和id

当一个组件以单个元素为根渲染时,透传的attribute会自动被添加到根元素上

<template>
	<h3>ComponentA</h3>
	<ComponentB class="component-b"/>
</template>
<script>
import ComponentB from "./components/ComponentB.vue"
export default{
		components:{
			ComponentB
		}
	}
</script>
<style scoped>
</style>
<template>
	<h3>ComponentB</h3>
</template>
<script>
export default{
	// inheritAttrs:false  // 禁用穿透
}
</script>
<style>
.component-b{
	color:red;
}
</style>

插槽Slot

我们已经了解到组件能够接收任意类型的JavaScript值作为props,但组件要如何接收模板内容呢?在某些场景中,我们可能想要为子组件传递一些模板片段,让子组件在它们的组件中渲染这些片段

<template>
	<h3>ComponentA</h3>
	<ComponentB>
		<h3>插槽传递试图内容</h3>
	</ComponentB>
</template>
<script>
import ComponentB from "./components/ComponentB.vue"
export default{
		components:{
			ComponentB
		}
	}
</script>
<style scoped>
</style>
<template>
	<h3>ComponentB</h3>
	<slot/>
</template>

<slot>元素是一个插槽出口(slot outlet),标示了父元素提供的插槽内容(slot content)将在哪里被渲染

渲染作用域

插槽内容可以访问到父组件的作用域,因为插槽内容本身也是在父组件模中定义的

<template>
	<h3>ComponentA</h3>
	<ComponentB>
		<p>{{ message }}</p>
	</ComponentB>
</template>
<script>
import ComponentB from "./components/ComponentB.vue"
	export default{
		data(){
			return{
				message:“message在父级”
			}
		},
		components:{
			ComponentB
		}
	}
</script>
<style scoped>
</style>
<template>
	<h3>ComponentB</h3>
	<slot/>
</template>

默认内容

<template>
	<h3>CompoentB</h3>
	<slot>插槽默认值</slot>
</template>

具名插槽

<template>
	<h3>ComponentA</h3>
	<ComponentB>
		<template v-slot:header>
			<h3>标题</h3>
		</template>
		<template v-slot:main>
			<p>内容</p>
		</template>
	</ComponentB>
</template>
<script>
import ComponentB from "./components/ComponentB.vue"
	export default{
		data(){
			return{
				message:“message在父级”
			}
		},
		components:{
			ComponentB
		}
	}
</script>
<style scoped>
</style>
<template>
	<h3>ComponentB</h3>
	<slot name="haeder"/>
	<hr>
	<slot name="main"/>
</template>

v-slot 有对应的简写#,因此 <template v-slot:header>可以简写为<template #header> 。意思是将这部分模板片段传入子组件的header插槽中。

插槽中的数据传递

在某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据,要做到这一点,我们需要一种方法来让子组件在渲染时将一部分数据提供给插槽

我们也确实有本法这么做!可以像对组件传递props那样,向一个插槽的出口上传递attribute

<template>
	<h3>ComponentA</h3>
	<ComponentB v-slot:slotProps>
			<h3>{{ message }} - {{slotProps.text}}</h3>
	</ComponentB>
</template>
<script>
import ComponentB from "./components/ComponentB.vue"
	export default{
		data(){
			return{
				message:“message在父级”
			}
		},
		components:{
			ComponentB
		}
	}
</script>
<style scoped>
</style>
<template>
	<h3>ComponentB</h3>
	<slot :text="message"/>
</template>
<script>
export default{
	data(){
		return{
			message:"ComponentB的数据"
		}
	}
}
</script>

具名插槽传递数据

<template>
	<h3>ComponentA</h3>
	<ComponentB #header=“slotProps”>
			<h3>{{ message }} - {{slotProps.text}}</h3>
	</ComponentB>
</template>
<script>
import ComponentB from "./components/ComponentB.vue"
	export default{
		data(){
			return{
				message:“message在父级”
			}
		},
		components:{
			ComponentB
		}
	}
</script>
<style scoped>
</style>
<template>
	<h3>ComponentB</h3>
	<slot name="header" :text="message"/>
</template>
<script>
export default{
	data(){
		return{
			message:"ComponentB的数据"
		}
	}
}
</script>

组件生命周期

每个Vue组件实例在创建时都要经历一系列的初始化步骤,比如设置好数据侦听、编译模板、挂载实例到DOM,以及在数据改变时更新DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码

生命周期函数

brforeCreate // 组件创建之前

created  // 组件创建之后

beforeMount  //  组件渲染之前

– –  渲染页面

mounted  // 组件渲染之后

beforeUpdate  //  组件更新之前

– – 更新数据

Updated  //  组件更新之后

beforeUnmount  //  组件销毁之前

unmounted  //  组件销毁之后

<template>
	<h2> 组件生命周期函数 </h2>
</template>
<script>
export default{
		data(){
			return{
			}
		},

		methods:{
			beforeCreate(){},
			created(){},
			beforeMount(){},
			mounted(){},
			beforeUpdata(){},
			Update(){},
			beforeUnmount(){}.
			unmounted(){}
				}
		}
	}
</script>
<style scoped>
</style>

通过ref获取元素DOM结构

<template>
	<h3>组件生命周期函数应用</h3>
	<p ref="name">Name</p>
</template>
<script>
export default{
	beforeMount(){
		console.log(this.$refs.name) // undefind
	},
	mounted(){
		console.log(this.$refs.name)
	}
}
</script>

模拟网络请求渲染数据

<template>
	<h3>组件生命周期函数应用</h3>
	<ul>
		<li v-for="(item.index) in banner" :key="index">
			<p>{{ item.name}}</p>
		</li>
	</ul>
</template>
<script>
export default{
	data(){
		return{
			banner:[]
		}
	},
	mounted(){
		this.banner = [
			{"name":"iwen"}
		]
	}
}
</script>

动态组件

<template>
	<component :is="tabComponent" />
	<button @click="changeHandle">切换组件</button>
</template>
<script>
imoirt ComponentA from "./components/ComponentA.vue"
imoirt ComponentB from "./components/ComponentB.vue"
export default{
	data(){
		tagComponent:"ComponentA"
	},
	components:{
		ComponentA,
		ComponentB
	},
	methods(){
		changedHandle(){
			this.tabConponent = this.tabComponent =="ComponentA" ? "ComponentB" : "ComponentA"
		}
	}
}
</script>

组件保持存活

当使用<component :is="">来在多个组件切换时,被切换掉的组件会被卸载。我们可以通过<keep-alive>组件强制被切换掉的组件仍然保持存活的状态

<template>
	<keep-alive>
		<component :is="tabComponent" />
	</keep-alive>
	<button @click="changeHandle">切换组件</button>
</template>
<script>
imoirt ComponentA from "./components/ComponentA.vue"
imoirt ComponentB from "./components/ComponentB.vue"
export default{
	data(){
		tagComponent:"ComponentA"
	},
	components:{
		ComponentA,
		ComponentB
	},
	methods(){
		changedHandle(){
			this.tabConponent = this.tabComponent =="ComponentA" ? "ComponentB" : "ComponentA"
		}
	}
}
</script>

异步组件

在大型项目中,我们可能需要拆分应用为更小的块,并且仅在需要时再从服务器加载相关组件。Vue提供了defineAsyncComponent方法来实现此功能

<template>
	<keep-alive>
		<component :is="tabComponent" />
	</keep-alive>
	<button @click="changeHandle">切换组件</button>
</template>
<script>
import { defineAsyncComponent } from 'vue'
import ComponentA from "./components/ComponentA.vue"
const AsyncComponentB = defineAsyncComponent(() =>
	import('./components/ComponentB.vue')
)
export default{
	data(){
		tagComponent:"ComponentA"
	},
	components:{
		ComponentA,
		AsyncComponentB
	},
	methods(){
		changedHandle(){
			this.tabConponent = this.tabComponent =="ComponentA" ? "AsyncComponentB" : "ComponentA"
		}
	}
}
</script>

 依赖注入

通常情况下,当我们需要从父组件向子组件传递数据时,会使用props.想象一下这样的结构:有一些多层级的嵌套组件,形成了一颗巨大的组件树,而某个深层的子组件需要一个较远的祖先组件中的部分数据。在这种情况下,如果仅使用props则必须将其沿着组件链逐级传递下去,这会非常麻烦

这一问题被称为prop逐级透传

provide和inject可以帮助我们解决这一问题。一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级多深,都可以注入由父组件提供给整条链路的依赖

Provide

要为组件后代提供数据,需要用到provide选项

export default{
	provide:{
		message:'hello!'
	}
}

也可以读取data中的数据

export default{
	data(){
		return{
			message:'hello!'
		}
	},
	provide(){
		return{
			message:this.message
		}
	}
}

除了在一个组件中提供依赖,我们还可以在整个应用层面提供依赖

import {createApp} from 'vue'
const app = createApp({})
app.provide(/* 注入名 */ 'message',/* 值 */ 'hello!')

Inject

要注入上层组件提供的数据,需要使用inject选项来声明

export default{
	inject:['message'],
	created(){
		console.log(this.message) // Inject Value
	}
}

注入会在组件自身的状态之前被解析,因此你可以在data ()中访问到注入的属性

export default{
	inject:['message'],
	data(){
		return {
			fullMessage:this.message
		}
	}
}

应用

应用实例

每个Vue应用都是通过createApp函数创建一个新的应用实例(每个项目只有一个实例对象)

import { createApp } from 'vue'
const app = createApp({
	/* 根组件选项 */
})

根对象

我们传入createApp的对象实际是一个组件,每个应用都需要一个根组件,其他组件将作为其子组件

import {createApp} from 'vue'
// 从一个单文件组件导入到根组件
import App from './App.vue'
const app = create(app)

挂载应用

应用实例必须在调用了.mount()方法后才会渲染出来。该方法接收一个容器参数,可以是一个实际的DOM元素或是一个CSS选择器字符串

app.mount('#app')

<!-- #app 在 项目目录/index.html的<div id="app"></div> -->

公共资源

src目录下的assets文件夹的作用就是存放公共资源,例如图片、公共CSS或者字体、图标等。

“您的支持是我持续分享的动力”

微信收款码
微信
支付宝收款码
支付宝


目录