vue组件化开发
定义一个组件
构建步骤中
//子组件
<script>
export default {
data() {
return {
count: 0
}
}
}
</script>
<template>
<button @click="count++">clicked {{ count }} times.</button>
</template>
不使用构建步骤中
//子组件
export default {
data() {
return {
count: 0
}
},
template: `
<button @click="count++">
clicked {{ count }} times.
</button>`
}
一个 Vue 组件以一个包含 Vue 特定选项的 JavaScript 对象来定义
全局注册
我们可以使用 Vue 应用实例的 app.component()
方法,让组件在当前 Vue 应用中全局可用。
首先,创建一个名为
MyButton.vue
的组件,代码如下:
<!-- content in MyButton.vue -->
<template>
<button class="my-button" @click="count++">
You clicked me {{ count }} times.
</button>
</template>
<script>
export default {
data() {
return {
count: 0
}
}
}
</script>
<style scoped>
.my-button {
background-color: blue;
color: #fff;
padding: 10px 20px;
}
</style>
在
main.js
中进行全局注册:
// content in main.js
import { createApp } from 'vue'
import App from './App.vue'
import MyButton from './components/MyButton.vue'
const app = createApp(App)
app.component('MyButton', MyButton)
app.mount('#app')
最后,在
App.vue
中使用组件:
<!-- content in App.vue-->
<template>
<div class="app">
<h1>我是全局注册</h1>
<MyButton></MyButton>
<!-- <my-button></my-button> -->
</div>
</template>
为了方便,Vue 支持将模板中使用 kebab-case 的标签解析为使用 PascalCase 注册的组件。这意味着一个以 MyButton 为名注册的组件,在模板中可以通过
<MyButton>
或<my-button>
引用。这让我们能够使用同样的 JavaScript 组件注册代码来配合不同来源的模板。
局部注册
局部注册的组件需要在使用它的父组件中显式导入,并且只能在该父组件中使用。它的优点是使组件之间的依赖关系更加明确,并且对 tree-shaking 更加友好。
局部注册需要使用 components
选项:
组件实现保持不变
<!-- content in MyButton.vue -->
<template>
<button class="my-button" @click="count++">
You clicked me {{ count }} times.
</button>
</template>
<script>
export default {
data() {
return {
count: 0
}
}
}
</script>
<style scoped>
.my-button {
background-color: blue;
color: #fff;
padding: 10px 20px;
}
</style>
将
MyButton.vue
中的组件名称从全局注册中删除。为了在模板中使用该组件,我们需要在App.vue
中进行局部注册
// content in main.js
import { createApp } from 'vue'
import App from './App.vue'
// import MyButton from './components/MyButton.vue'
const app = createApp(App)
// app.component('MyButton', MyButton)
app.mount('#app')
在
App.vue
中导入组件:
<!-- content in App.vue-->
<template>
<div class="app">
<h1>我是局部注册</h1>
<my-button></my-button>
</div>
</template>
<script>
import MyButton from './components/MyButton.vue'
export default {
components: {
// 在这里进行局部注册
'my-button': MyButton
}
}
</script>
请注意:局部注册的组件在后代组件中并不可用。在这个例子中,my-button
注册后仅在当前组件可用,而在任何的子组件或更深层的子组件中都不可用。
父组件往子组件中传数据:传递 props
在 Vue 中,props
是一种用于父组件向子组件传递数据的方式。具体来说,父组件可以通过 props
向子组件传递任何数据,包括字符串、数字、布尔值、对象、数组等等。
props
是一个对象,其中包含了子组件需要接收的属性名称和属性类型。子组件在使用 props
之前,必须先声明这些属性。
<!-- 子组件 MyInput.vue -->
<template>
<div>
<button>父组件传递的整数类型数据: {{ id }}</button>
<button>父组件传递的字符串类型数据: {{ MenuTitle }}</button>
<button>父组件传递的数组类型数据: {{ list }}</button>
<button>父组件传递的对象类型数据: {{ obj }}</button>
</div>
</template>
<script>
export default {
props: {
id: {
type: Number,
required: true,
},
MenuTitle: {
type: String,
required: true,
},
list: {
type: Array,
required: true,
},
obj: {
type: Object,
required: true,
},
},
};
</script>
<!-- 父组件 App.vue -->
<template>
<div>
<h1>下面是使用子组件,并传递了多个类型的值给子组件</h1>
<my-input
:id="post.id"
:MenuTitle="post.MenuTitle"
:list="post.list"
:obj="post.obj"
></my-input>
</div>
</template>
<script>
import MyInput from "./components/MyInput.vue";
export default {
components: {
"my-input": MyInput,
},
data() {
return {
post: {
id: 1,
MenuTitle: "My Journey with Vue",
list: ["item1", "item2", "item3"],
obj: {
name: "张三",
age: 18,
sex: "男",
},
},
};
},
};
</script>
所有 prop 默认都是可选的,除非声明了
required: true
。
子组件往父组件中传数据:$emit
在 Vue 中,子组件可以通过 $emit
方法向父组件传递数据。具体步骤如下:
<!-- 子组件 ChildComponent.vue -->
<template>
<button @click="sendDataToParent">点击我向父组件传递数据</button>
</template>
<script>
export default {
methods: {
sendDataToParent() {
this.$emit('child-click', '这是从子组件传递的数据');
}
}
}
</script>
<!-- 父组件 App.vue -->
<template>
<div>
<ChildComponent @child-click="handleChildClick"></ChildComponent>
<p>从子组件传递的数据:{{ childData }}</p>
</div>
</template>
<script>
import ChildComponent from "./components/ChildComponent.vue";
export default {
components: {
ChildComponent
},
data() {
return {
childData: ''
}
},
methods: {
handleChildClick($event) {
this.childData = $event;
}
}
};
</script>
这里子组件中定义了一个 sendDataToParent
方法,在该方法中通过 $emit
触发了一个名为 child-click
的自定义事件,并将字符串 '这是从子组件传递的数据'
作为参数传递给该事件。父组件中使用了 <ChildComponent>
标签来引入子组件,并通过 @child-click="handleChildClick"
语法监听了子组件触发的 child-click
事件。当子组件触发该事件时,父组件中的 handleChildClick
方法就会被调用,并将子组件传递过来的数据赋值给 childData
数据项。在父组件的模板中,使用了 Mustache 语法 {{ childData }}
来显示从子组件中接收到的数据。
$emit
是 Vue 实例中的一个方法,用于触发当前实例上的事件。通常用于子组件向父组件传递消息。当子组件内部发生了某个事件,可以使用
$emit
来触发一个自定义事件,并且可以传递一些数据给父组件。父组件可以监听子组件上触发的事件,并在相应事件发生时做出反应,从而实现子组件向父组件传递数据和通信。
插槽
插槽是 Vue.js 中一种重要的组件通信方式,它可以让子组件在父组件中动态地插入内容,增强组件的灵活性和可复用性。Vue.js 中的插槽可以分为两种:具名插槽和默认插槽。
本质:插槽就是一种占位符,用来标记子组件的某些区域可以被父组件插入任意内容。在子组件内部,插槽通常通过
<slot>
标签来定义
具名插槽
具名插槽是通过 v-slot
指令来定义的,它允许我们给插槽起一个名称,并将插槽的内容作为子组件的一部分传递进去。具名插槽可以让子组件根据父组件传递的不同数据,动态地修改其展示的内容,从而实现更高的灵活性和可复用性。
<!-- 子组件 MyButton.vue -->
<template>
<button><slot name="text">Submit</slot></button>
</template>
<!-- 父组件 App.vue -->
<template>
<div>
<my-button>
<!-- 给名为 text 的插槽传递数据 -->
<template v-slot:text>提交表单</template>
<!--<template #text>提交表单</template>-->
</my-button>
</div>
</template>
<script>
import MyButton from './components/MyButton.vue'
export default {
components: {
MyButton
}
}
</script>
在上述代码中,我们定义了一个名为 MyButton
的子组件,它包含了一个名为 text
的具名插槽。插槽的默认内容是 Submit
,如果父组件需要插入其他的内容,可以通过具名插槽来动态设置。
在父组件中,我们通过 v-slot
指令和 name
属性来给具名插槽传递数据。在这个例子中,我们将文本 提交表单
传递到名为 text
的插槽中,最终渲染出来的按钮文本就是 提交表单
。
需要注意的是,具名插槽也可以有默认内容,例如在 MyButton
组件中的插槽默认内容是 Submit
。如果父组件没有为该插槽传递数据,则会使用默认内容进行渲染。
Vue.js 支持使用
#
符号来替代v-slot
的语法。
默认插槽
<!-- 子组件 MyCard.vue -->
<template>
<div class="card">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'my-card',
props: {
title: String
}
}
</script>
<!-- 父组件 App.vue -->
<template>
<div>
<my-card title="My Title">
<p>This is the main content</p>
<button>Submit</button>
</my-card>
</div>
</template>
<script>
import MyCard from "./components/MyCard.vue";
export default {
components: {
"my-card":MyCard
},
};
</script>
我们定义了一个名为 my-card
的组件,并在组件内部使用了一个默认插槽。默认情况下,组件内部所有未具名的子元素都会被渲染到该插槽中。
my-card
组件的插槽会将 <p>This is the main content</p>
和 <button>Submit</button>
渲染出来,从而使组件变得更加灵活,并支持用户自定义内容。
需要注意的是,当一个组件同时包含具名插槽和默认插槽时,未命名的子元素会被渲染到默认插槽中。如果要在组件中同时使用多个默认插槽,可以通过为每个默认插槽添加 name
属性来实现。
插槽作用域
<!-- 子组件 MyList.vue -->
<template>
<div class="list">
<h2>{{ title }}</h2>
<ul v-if="items.length > 0">
<li v-for="(item, index) in items" :key="index">
<slot name="item" :item="item" :index="index">
{{ item }}
</slot>
</li>
</ul>
<p v-else>{{ emptyText }}</p>
</div>
</template>
<script>
export default {
name: "MyList",
props: {
items: {
type: Array,
default: () => []
},
title: String,
emptyText: {
type: String,
default: "No data available."
}
}
};
</script>
在上面的代码中,我们为 <my-list>
组件添加了一个 name
选项,以便在插槽中使用 v-slot
指令。由于我们想要在插槽中访问 item
和 index
属性,因此我们将它们作为插槽作用域变量进行传递,使用 :item="item"
和 :index="index"
的形式。在插槽内部,我们可以通过 $slotProps
对象来获取这些属性。例如:
<!-- 父组件 App.vue -->
<div>
<my-list :items="['apple', 'banana', 'orange']">
<template v-slot:item="props">
{{ props.index + 1 }}. {{ props.item }}
</template>
</my-list>
</div>
<script>
import MyList from "./components/MyList.vue";
export default {
components: {
"my-list":MyList
},
};
</script>
在这个例子中,我们定义了一个名为 item
的插槽,并使用 v-slot:item
来指定该插槽的名称。然后,我们可以通过 props.index
和 props.item
来访问 item
和 index
属性,并在模板中使用它们来渲染每个列表项的内容。这样,我们就可以将 <my-list>
组件作为一个可复用的模块,任意地配置和定制它的显示效果和行为。
在父组件的模板中,我们可以通过在子组件标签上添加 items
属性来向子组件传递数据
我们使用 slot
元素来定义一个 item
插槽,并向插槽中传递两个属性值:item
和 index
。这些属性值将在父组件中使用 v-slot
指令时,被作用域插槽变量 props
所接收。
能够访问子组件中数据的插槽,称为作用域插槽
评论