4.Vue组件
4.1 初始Vue组件
4.1.1 根组件
根组件的主要作用是:
- 初始化应用程序。
- 提供全局的状态管理或配置。
- 作为其他组件的容器,构建整个应用程序的组件树。
通过根组件,开发者可以控制应用程序的整体结构和行为,确保各个部分能够协同工作。
<body>
<div id="app"></div>
<script src="../js/vue3.js"></script>
<script>
let app = Vue.createApp({}) // 创建一个Vue实例
let vm = app.mount('#app') // 根组件
// Vue.createApp({}).mount('#app')
</script>
</body>
4.1.2 全局组件
直接挂载到Vue实例上的组件就是一个全局组件。
全局组件的优点是:
- 方便使用:一旦注册,可以在任何地方直接使用,无需再次导入或声明。
- 减少重复代码:适用于需要在多个地方使用的通用组件。
全局组件的缺点是:
- 性能问题:全局组件在应用程序启动时就会被注册,可能会影响初始加载时间。
- 命名冲突:如果多个全局组件使用相同的名称,可能会导致冲突。
<body>
<div id="app">
<mycomponent></mycomponent>
</div>
<script src="../js/vue3.js"></script>
<script>
let app = Vue.createApp({}) // 创建一个Vue实例
app.component('mycomponent', { // 创建一个全局组件
template: `<div>
<p>我是全局组件。{{ num }}</p>
<button @click="add">加</button>
<button @click="sub">减</button>
</div>`,
data() {
return {
num: 0
}
},
methods: {
add() {
this.num++
},
sub() {
this.num--
}
}
})
let vm = app.mount('#app')
</script>
</body>
4.1.3 局部组件
声明在根组件上的组件就是局部组件。局部组件只能应用在根组件中,而且局部组件只有在使用时才会消耗系统资源。
局部组件的主要特点包括:
- 作用域有限:只在特定的范围内使用,不对外公开。
- 提高可维护性:通过将功能拆分为更小的、独立的组件,使得代码更易于维护和理解。
- 减少耦合:局部组件不依赖于全局状态或其他外部组件,从而减少了系统内部的耦合度
<body>
<div id="app">
<mycomponent></mycomponent>
</div>
<script src="../js/vue3.js"></script>
<script>
Vue.createApp({
data() {
return {}
},
components: {
mycomponent: {
template: '<p>我是局部组件</p>'
}
}
}).mount('#app')
</script>
</body>
4.1.4 组件模板
<body>
<div id="app">
<mycomponent></mycomponent>
<mycomponent></mycomponent>
</div>
<template id="mycomponent">
<div>
<h3>我是局部组件</h3>
<p>hello world</p>
{{ num }}
<button @click="add">加</button>
</div>
</template>
<script src="../js/vue3.js"></script>
<script>
Vue.createApp({
data() {
return {}
},
components: {
mycomponent: {
template: '#mycomponent',
data() {
return {
num: 0
}
},
methods: {
add() {
this.num += 1
}
}
}
}
}).mount('#app')
</script>
</body>
4.2 父子组件
父子组件的特点:
- 嵌套关系:父组件在其模板或JSX中直接包含子组件。
- 数据传递:父组件可以通过属性(props)向子组件传递数据。
- 事件通信:子组件可以通过事件(events)向父组件传递信息或触发父组件的方法。
- 作用域隔离:子组件通常有自己独立的作用域和状态,不会直接影响父组件或其他兄弟组件的状态。
<body>
<div id="app">
<mycomponent></mycomponent>
</div>
<template id="mycomponent">
<div>
<p>我是父组件</p>
<subcomponent></subcomponent>
</div>
</template>
<script src="../js/vue3.js"></script>
<script>
Vue.createApp({
data() {
return {}
},
components: {
mycomponent: {
template: '#mycomponent',
components: {
subcomponent: {
template: '<div>我是子组件</div>'
}
}
}
}
}).mount('#app')
</script>
</body>
4.3 组件之间的通信
4.3.1 子组件获取父组件数据
4.3.1.1 数据传递选项prop
prop
是一种用于父组件向子组件传递数据的机制。通过prop
,父组件可以将数据传递给子组件,子组件则可以通过定义props
属性来接收这些数据。
<body>
<div id="app">
<mycomponent></mycomponent>
</div>
<template id="mycomponent">
<div>
<p>我是父组件</p>
<subcomponent msg="hello world"></subcomponent>
</div>
</template>
<script src="../js/vue3.js"></script>
<script>
Vue.createApp({
data() {
return {}
},
components: {
mycomponent: {
template: '#mycomponent',
components: {
subcomponent: {
template: '<div>我是子组件,我接受父组件传值:{{ msg }}</div>',
props: ['msg']
}
}
}
}
}).mount('#app')
</script>
</body>
4.3.1.2 传值校验
<body>
<div id="app">
<mycomponent></mycomponent>
</div>
<template id="mycomponent">
<div>
<p>我是父组件</p>
<subcomponent :msg="num" title="第二个参数"></subcomponent>
</div>
</template>
<script src="../js/vue3.js"></script>
<script>
Vue.createApp({
data() {
return {
}
},
components: {
mycomponent: {
template: '#mycomponent',
data() {
return {
num: 100
}
},
components: {
subcomponent: {
template: '<div>我是子组件,我接受父组件传值:{{ msg + 1 }},{{ title }}</div>',
props: {
msg: {
// type: Number 表示msg必须是一个数字类型。
type: Number
},
title: {
// type: String 表示title必须是一个字符串类型。
type: String,
// required: true 表示title是必填项,父组件必须传递这个属性。
required: true
}
}
}
}
}
}
}).mount('#app')
</script>
</body>
除了类型和必填项的校验,Vue.js还支持更多的校验规则,例如:
default
:设置属性的默认值。validator
:自定义校验函数。
4.3.1.3单向数据流
数据只能从上到下单向流动,即从父组件流向子组件,而子组件不能直接修改父组件的状态。这种设计模式有助于提高代码的可维护性和可预测性,因为它减少了状态管理的复杂性。
<body>
<div id="app">
<mycomponent></mycomponent>
</div>
<template id="mycomponent">
<div>
<h3>我是父组件</h3>
<subcomponent :msg="num"></subcomponent>
</div>
</template>
<script src="../js/vue3.js"></script>
<script>
Vue.createApp({
data() {
return {
}
},
components: {
mycomponent: {
template: '#mycomponent',
data() {
return {
num: 10
}
},
components: {
subcomponent: {
template: `<div>
我是子组件,我接受父组件传值:{{msg}}
<button @click="changeNum">改变父组件传值</button>
</div>`,
props: {
msg: {
type: Number
}
},
methods: {
changeNum() {
this.msg = 100
}
}
}
}
}
}
}).mount('#app')
</script>
</body>
当点击按钮想要改变数据时,控制台就会弹出警告,显示msg是只读,不可以修改。若是想修改传递过来的值,只需要将传过来的参数改为自己的参数:
components: {
subcomponent: {
template: `<div>
我是子组件,我接受父组件传值:{{num}}
<button @click="changeNum">改变父组件传值</button>
</div>`,
props: {
msg: {
type: Number
}
},
data() {
return {
num: this.msg
}
},
methods: {
changeNum() {
this.num = 100
}
}
}
}
4.3.2 父组件获取子组件数据
父组件想要获取子组件的数据时,需要子组件通过$emit主动将自己的数据发送给父组件。
<body>
<div id="app">
<mycomponent></mycomponent>
</div>
<template id="mycomponent">
<div>
<p>我是父组件,我接收子组件传值:{{ChildValue}}</p>
<subcomponent msg="hello world" @childmsg="get"></subcomponent>
</div>
</template>
<script src="../js/vue3.js"></script>
<script>
Vue.createApp({
data(){
return {}
},
components: {
mycomponent: {
template: '#mycomponent',
data() {
return {
ChildValue: ''
}
},
methods: {
get(value) {
this.ChildValue = value
}
},
components: {
subcomponent: {
template: `<div>
我是子组件,我接收父组件传值:{{msg}}
<button @click="send">给父组件传值</button>
</div>`,
props: ['msg'],
methods: {
send() {
// this.$emit('自定义事件名称', 自定义事件参数)
this.$emit('childmsg', '子组件给父组件传的值')
}
}
}
}
}
}
}).mount('#app')
</script>
</body>
4.3.3 多级组件通信
以下是多级组件通信的一个例子:
<body>
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<p>我是父组件</p>
<son msg="hello world"></son>
</div>
</template>
<script src="../js/vue3.js"></script>
<script>
Vue.createApp({
data() {
return {}
},
components: {
father: {
template: '#father',
components: {
son: {
template: `<div>
我是子组件
<grandson :msg="msg"></grandson>
</div>`,
props: ['msg'],
components: {
grandson: {
template: `<div>
我是孙子组件,接收父组件传值:{{ msg }}
</div>`,
props: ['msg']
}
}
}
}
}
}
}).mount('#app')
</script>
</body>
- 上述实例中,父组件经过三级传递,将数据传递给孙子组件,太过繁琐。通过
provide
和inject
,可以简化跨层级组件之间的数据传递,提高代码的可维护性和可读性。
<body>
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<p>我是父组件</p>
<son></son>
</div>
</template>
<script src="../js/vue3.js"></script>
<script>
Vue.createApp({
data() {
return {}
},
components: {
father: {
template: '#father',
provide() {
return {
msg: 'hello world'
}
},
components: {
son: {
template: `<div>
我是子组件
<grandson></grandson>
</div>`,
components: {
grandson: {
template: '<div>我是孙子组件,我接收父组件传值:{{ msg }}</div>',
inject: ['msg']
}
}
}
}
}
}
}).mount('#app')
</script>
</body>
注意事项:
- 响应性:通过
provide
传递的数据默认不是响应式的。如果需要传递响应式数据,可以使用computed
属性。 - 依赖注入:
provide
和inject
主要用于非响应式场景,对于需要响应式数据传递的场景,建议使用Vuex或Pinia等状态管理库。
4.4 slot插槽
slot插槽用于在组件中定义可替换的内容。通过使用slot,父组件可以向子组件传递自定义的内容。
4.4.1 基本用法
<body>
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<p>我是父组件</p>
<son>
<p>插槽的内容</p>
<div>这还是插槽的内容</div>
</son>
</div>
</template>
<script src="../js/vue3.js"></script>
<script>
Vue.createApp({
data() {
return {}
},
components: {
father: {
template: '#father',
components: {
son: {
template: `<div>
<p>我是子组件</p>
<slot></slot>
</div>`
}
}
}
}
}).mount('#app')
</script>
</body>
4.4.2 slot默认内容
- 作用域插槽
作用域插槽允许父组件在子组件的插槽内容中访问子组件的数据。通过作用域插槽,子组件可以向父组件传递数据,父组件可以在插槽内容中使用这些数据。
官网中有这样一句话:父级模版里的所有内容否是在父级作用域中编译的,子级模板中的所有内容都是在子级作用域中编译的。即父组件当中的动态数据会先被解析为静态数据,再将数据传递给父组件,反之亦然。
<body>
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<p>我是父组件</p>
<son>
<p>插槽的内容 {{num}}</p>
</son>
</div>
</template>
<script src="../js/vue3.js"></script>
<script>
Vue.createApp({
data() {
return {}
},
components: {
father: {
template: '#father',
data() {
return {
num: 100
}
},
components: {
son: {
template: `<div>
<p>我是子组件</p>
<slot></slot>
</div>`
}
}
}
}
}).mount('#app')
</script>
</body>
-
默认内容
插槽可以定义默认内容,当父组件没有提供插槽内容时,将显示默认内容。
<body>
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<p>我是父组件</p>
<son></son>
</div>
</template>
<script src="../js/vue3.js"></script>
<script>
Vue.createApp({
data() {
return {}
},
components: {
father: {
template: '#father',
components: {
son: {
template: `<div>
<p>我是子组件</p>
<slot>默认内容</slot>
</div>`
}
}
}
}
}).mount('#app')
</script>
</body>
4.4.3 具名插槽
通过具名插槽,你可以在组件中定义多个插槽,每个插槽可以有不同的名称,父组件可以根据名称向这些插槽提供内容。
<body>
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<p>我是父组件</p>
<son>
<template v-slot:header>头部内容</template>
<template v-slot:footer>尾部内容</template>
</son>
</div>
</template>
<script src="../js/vue3.js"></script>
<script>
Vue.createApp({
data() {
return {}
},
components: {
father: {
template: '#father',
components: {
son: {
template: `<div>
<slot name="header"></slot>
<p>我是子组件</p>
<slot name="footer"></slot>
</div>`
}
}
}
}
}).mount('#app')
</script>
</body>
总结:插槽是父子组件关系,插槽在子组件中展示位置以及展示内容的操作手段。父组件决定展示内容,子组件决定展示位置。
4.5 动态组件
动态组件允许你根据数据或条件动态地切换组件。你可以使用 <component>
元素并结合 is
属性来实现动态组件。
4.5.1 基本用法
<body>
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<h3>父组件</h3>
<!-- <son1 v-if="num === 0"></son1>
<son2 v-if="num === 1"></son2> -->
<component :is="name"></component>
<button @click="change">点击切换子组件</button>
</div>
</template>
<script src="../js/vue3.js"></script>
<script>
Vue.createApp({
data() {
return {}
},
components: {
father: {
template: '#father',
data() {
return {
name: 'son1'
}
},
components: {
son1: {
template: '<div>子组件1</div>'
},
son2: {
template: '<div>子组件2</div>'
}
},
methods: {
change() {
if (this.name ==='son1') {
this.name ='son2'
} else {
this.name ='son1'
}
}
}
}
}
}).mount('#app')
</script>
</body>
4.5.2 保持组件状态
keep-alive
,用于在HTTP客户端和服务器之间保持持久连接。这意味着在单个TCP连接中可以发送多个HTTP请求和响应,而不需要为每个请求/响应对都建立和关闭一个新的连接。这可以显著提高性能,特别是在需要频繁通信的场景中。
以下是一些关于keep-alive
的关键点:
- 减少延迟:通过减少连接的建立和关闭次数,可以减少延迟。
- 减少资源消耗:减少了服务器和客户端的资源消耗,因为不需要频繁地创建和销毁连接。
- 提高性能:在需要多次请求的场景中,性能可以得到显著提升。
<body>
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<h3>父组件</h3>
<!-- 这里使用了keep-alive包裹了子组件,可以缓存组件,提高渲染性能 -->
<keep-alive>
<component :is="name"></component>
</keep-alive>
<button @click="change">点击切换子组件</button>
</div>
</template>
<script src="../js/vue3.js"></script>
<script>
Vue.createApp({
data() {
return {}
},
components: {
father: {
template: '#father',
data() {
return {
name: 'son1'
}
},
components: {
son1: {
template: '<div>子组件1</div>'
},
son2: {
template: '<div>子组件2</div>'
}
},
methods: {
change() {
if (this.name === 'son1') {
this.name = 'son2'
} else {
this.name = 'son1'
}
}
}
}
}
}).mount('#app')
</script>
</body>
4.6 异步组件
异步组件是指在需要时才加载其代码和资源的组件。这种方式可以显著提高应用的性能,因为只有在组件实际需要被渲染时,才会去加载相关的代码和资源,而不是在应用初始加载时就全部加载。
异步组件的优势
- 性能优化:
- 减少初始加载时间:只加载当前页面所需的组件,其他组件在需要时再加载。
- 减轻服务器负担:按需加载组件,减少服务器一次性传输的数据量。
- 更好的用户体验:
- 用户可以更快地看到页面内容,因为不需要等待所有组件都加载完成。
<body>
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<h3>父组件</h3>
<son1></son1>
<son2></son2>
</div>
</template>
<script src="../js/vue3.js"></script>
<script>
Vue.createApp({
data() {
return {}
},
components: {
father: {
template: '#father',
components: {
son1: {
template: '<div>子组件1</div>'
},
// Vue.defineAsyncComponent 用于定义一个异步组件。这个方法接受一个工厂函数,该函数返回一个 Promise。
// (resolve, reject) => { ... }: 这是一个返回 Promise 的工厂函数。resolve 和 reject 是 Promise 的两个回调函数,分别用于成功和失败的情况。
// setTimeout(() => { ... }, 3000): 这是一个定时器函数,设置在 3000 毫秒(即 3 秒)后执行回调函数。这里模拟了一个异步操作,比如从服务器获取组件的代码。
// resolve({ template: '<div>子组件2</div>' }): 在定时器结束后,调用 resolve 函数并传递一个对象,该对象包含组件的模板。这意味着在 3 秒后,组件的模板会被加载并渲染。
son2: Vue.defineAsyncComponent((resolve, reject)=>{
setTimeout(()=>{
resolve({
template: '<div>子组件2</div>'
})
},3000)
})
}
}
}
}).mount('#app')
</script>
</body>
标签:学习,Vue,return,template,---,components,Vue3,组件,data
From: https://www.cnblogs.com/yishengwanwuzhao/p/18351537