目录
组件认知
一个应用会以一棵嵌套的组件树的形式来组织
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
层层划分 页由多组
组件是可复用的实例,所以它们与根实例接收相同的选项,例如 data
、computed
、watch
、methods
以及生命周期钩子等
组件简单使用
项目结构:
我们使用的组件是单文件组件(有一套完整的.vue内容 单独模块功能的封装)而不是字符串模板
Search.vue
<template>
<h2>你好呀 这个是搜索框 组件</h2>
</template>
HelloWorld.vue
<script setup>
import { ref } from 'vue'
defineProps({
msg: String
})
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<p>
Recommended IDE setup:
<a href="https://code.visualstudio.com/" target="_blank">VS Code</a>
+
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
</p>
<p>
<a href="https://vitejs.dev/guide/features.html" target="_blank">
Vite Documentation
</a>
|
<a href="https://v3.vuejs.org/" target="_blank">Vue 3 Documentation</a>
</p>
<button type="button" @click="count++">count is: {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test hot module replacement.
</p>
</template>
<style scoped>
a {
color: #42b983;
}
</style>
App.vue 根组件
<script>
//导入自己定义的组件
import SearchVue from './components/Search.vue'
import HelloWorldVue from './components/HelloWorld.vue'
export default{
components:{
//加载组件
SearchVue,
HelloWorldVue
}
}
</script>
<template>
<!-- 使用自己定义的组件 -->
<!-- App.vue 相当于父组件 而Search.vue和HelloWorld.vue相当于子组件 -->
<SearchVue></SearchVue>
<HelloWorldVue></HelloWorldVue>
</template>
<style>
</style>
效果展示
data函数return的理解
<script>
export default{
data(){
//data 让子组件每次返回不同对象 防止数据污染 到其他使用的该组件的父组件
return{
msg:"你好呀"
}
}
}
</script>
<template>
<h2>你好呀 这个是搜索框 组件</h2>
<h3>{{msg}}</h3>
<button @click="this.msg='HelloWorld'">改变msg</button>
</template>
<style>
</style>
对比:
<script>
const obj={
msg:'hello',
}
export default{
data(){
return obj
}
}
</script>
<template>
<h2>你好呀 这个是搜索框 组件</h2>
<h3>{{msg}}</h3>
<button @click="this.msg='HelloWorld'">改变msg</button>
</template>
<style>
</style>
通过 Prop 向子组件传递数据 子取父数据
理解:
简单使用:
父组件 App.vue
<script>
//导入自己定义的组件
import SearchVue from './components/Search.vue'
export default{
data(){
return{
transfer:[1,2,3,4]
}
},
components:{
//加载组件
SearchVue
}
}
</script>
<template>
<!--命名可随便取值(message,title) v-bind :message 绑定动态值 title 绑定静态值 -->
<SearchVue :message="transfer" title="我要向Search.vue 传递我的信息啦"></SearchVue>
</template>
<style>
</style>
子组件 Search.vue
1.以字符串数组形式列出的 props
<script>
export default{
data(){
return
},
//以字符串数组形式列出的 props
//通过props 去接收到父组件传递过来的值
props: ['title','message'],
}
</script>
<template>
<h2>你好呀 这个是搜索框 组件</h2>
<h3>这个是静态的值 {{title}}</h3>
<h3>这个是动态的值 {{message}}</h3>
</template>
<style>
</style>
2. proos以对象的形式
<script>
export default{
data(){
return {
}
},
//proos以对象的形式
props: {
//1.可对类型的限制
//2.可设置默认值 父组件没有传值的时候就会使用默认值
//3.设置是否为必传值
//以上不满足设置要求的话都会有警告喔
title:{
//类型可设置为多个
type:[String],
default:"这个是message的默认值喔",
required: true
},
message:{
// 对象或数组的默认值必须从一个工厂函数返回
type:Array,
default(){
return []
}
}
}
}
</script>
<template>
<h2>你好呀 这个是搜索框 组件</h2>
<h3>这个是静态的值 {{title}}</h3>
<h3>这个是动态的值 {{message}}</h3>
</template>
<style>
</style>
结果展示:
警告:
单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
另外,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告
1.这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用
2.这个 prop 以一种原始的值传入且需要进行转换。
监听子组件事件 父取子数据
理解
简单使用
代码
Search.vue
<script>
export default{
data(){
return {
message:"我想要去到父组件"
}
},
methods:{
//1.在子组件中 通过 $emit来触发事件 this.$emit('自定义事件的名称','发送的事件参数')
sendParent(){
this.$emit('send',this.messgae)
}
}
}
</script>
<template>
<button @click="sendParent">点击按钮</button>
</template>
<style>
</style>
App.vue
<script>
//导入自己定义的组件
import SearchVue from './components/Search.vue'
export default{
data(){
return{
msg:""
}
},
components:{
//加载组件
SearchVue
},
methods:{
//子组件的自定义事件 会给该方法一个参数 该参数就是 this.$emit的第二个参数'发送的事件参数'
getChildMsg(value){
console.log(value);
this.msg=value;
}
}
}
</script>
<template>
<!-- 获取子组件SearchVue的数据 通过自定义事件 -->
<!-- 2.在父组件中 通过v-on监听子组件中自定义的事件 -->
<SearchVue @send="getChildMsg"></SearchVue>
<h2>通过获取到子组件的值================{{msg}}</h2>
</template>
<style>
</style>
结果:
父子组件的访问方式
父组件访问子组件 $refs 常用
理解
代码
vue.app
<script>
//导入自己定义的组件
import SearchVue from './components/Search.vue'
export default{
data(){
return{
msg:""
}
},
//钩子函数
mounted(){
console.log(this.$refs);
},
components:{
//加载组件
SearchVue
}
}
</script>
<template>
<!-- 父组件访问子组件 $refs -->
<!-- ref: 用来给元素或者子组件注册引用信息 -->
<SearchVue ref="search"></SearchVue>
<h2 ref="h2"></h2>
</template>
<style>
</style>
search.vue
<script>
export default{
data(){
return {
message:"我想要去到父组件"
}
},
methods:{
//1.在子组件中 通过 $emit来触发事件 this.$emit('自定义事件的名称','发送的事件参数')
sendParent(){
this.message="嘻嘻";
}
}
}
</script>
<template>
<button @click="sendParent">点击按钮</button>
</template>
<style>
</style>
展示效果
子组件访问父组件 $parent $root
提示不太推荐使用 组件复用性高 这样使用会降低子组件的使用场景
理解
代码
$parent $root 用法相同 就展示一个使用方法
$parent
Search.vue
<script>
export default{
data(){
return {
message:"我想要去到父组件"
}
},
//钩子函数
mounted(){
//获取父组件 信息
console.log(this.$parent);
}
}
</script>
<template>
</template>
<style>
</style>
App.vue
<script>
//导入自己定义的组件
import SearchVue from './components/Search.vue'
export default{
data(){
return{
msg:"父组件信息"
}
},
components:{
//加载组件
SearchVue
},
}
</script>
<template>
<SearchVue></SearchVue>
</template>
<style>
</style>
结果
插槽
通过插槽分发内容
理解
简单使用 代码
App.vue
<script>
//导入自己定义的组件
import SearchVue from './components/Search.vue'
export default{
data(){
return{
msg:""
}
},
components:{
//加载组件
SearchVue
}
}
</script>
<template>
<SearchVue> <button>我是子组件插槽按钮</button></SearchVue>
</template>
<style>
</style>
Search.vue
<script>
export default{
data(){
return {
message:""
}
}
}
</script>
<template>
<h2>下面要使用插槽了</h2>
<!-- <slot> 作为我们想要插入内容的占位符 -->
<slot></slot>
</template>
<style>
</style>
结果展示
具名插槽 渲染作用域 备用内容
具名插槽:
<slot>
元素有一个特殊的 attribute:name
。通过它可以为不同的插槽分配独立的 ID,也就能够以此来决定内容应该渲染到什么地方一个不带
name
的<slot>
出口会带有隐含的名字“default”
渲染作用域:
该插槽可以访问与模板其余部分相同的实例 property (即相同的“作用域”)。
官网 理解:父组件中用到的插槽中添加的内容 属于父组件 子组件使用不了
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
备用内容:
有时为一个插槽指定备用 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。
具名插槽 渲染作用域 代码
Search.vue
<script>
export default{
data(){
return {
msg:"子组件信息"
}
}
}
</script>
<template>
<h2>下面要使用插槽了</h2>
<!-- <slot> 作为我们想要插入内容的占位符 -->
<slot name="left"></slot>
<div></div>
<slot name="right"></slot>
</template>
<style>
</style>
App.vue
<script>
//导入自己定义的组件
import SearchVue from './components/Search.vue'
export default{
data(){
return{
msg:"父组件信息"
}
},
components:{
//加载组件
SearchVue
}
}
</script>
<template>
<!-- 如果有多个值 可同时放入组件进行替换 一起作为替换元素 -->
<!-- v-slot 只能添加在<template>上 -->
<SearchVue>
<template v-slot:left><button>我是子组件左插槽按钮</button>
<!-- 父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的 -->
<h2>{{msg}}</h2>
</template>
<template v-slot:right><button>我是子组件右插槽按钮</button></template>
</SearchVue>
</template>
<style>
</style>
结果
备用内容 渲染作用域 代码
Search.vue
<script>
export default{
data(){
return {
msg:"子组件信息"
}
}
}
</script>
<template>
<slot><button>我是备用按钮 父组件要是没有的话 我就存在了</button></slot>
</template>
<style>
</style>
App.vue
<script>
export default{
data(){
return {
msg:"子组件信息"
}
}
}
</script>
<template>
<!-- 备用内容 会在没有提供内容的时候被渲染 -->
<slot><button>我是备用按钮 父组件要是没有的话 我就存在了</button>
{{msg}}
</slot>
</template>
<style>
</style>
结果
作用域插槽
理解
父组件替换插槽的标签 但是数据由子组件来提供
代码
Search.vue
<script>
export default{
data(){
return {
msg:"子组件信息",
list:["菠萝","香蕉","苹果","哈密瓜","西瓜"],
thing:"(*^_^*)"
}
}
}
</script>
<template>
<!-- 绑定子组件 数据 -->
<slot :thing="thing" :msg="msg"></slot>
<slot name="Uselist" :list="list" ></slot>
</template>
<style>
</style>
App.vue
<script>
//导入自己定义的组件
import SearchVue from './components/Search.vue'
export default{
data(){
return{
msg:"父组件信息"
}
},
components:{
//加载组件
SearchVue
}
}
</script>
<template>
<!-- 如果有多个值 可同时放入组件进行替换 一起作为替换元素 -->
<!-- v-slot 只能添加在<template>上 -->
<SearchVue>
<!-- 作用域插槽:父组件替换插槽的标签 但是数据由子组件来提供 -->
<!-- 包含所有插槽 prop的对象命名为 slotProps 命名 随意 -->
<template v-slot:default="sloProps">
<h2>{{sloProps}}</h2>
<h2>{{sloProps.msg}}</h2>
</template>
</SearchVue>
<br/>
<!-- 无序序列表 -->
<SearchVue>
<template v-slot:Uselist="fruitList">
<ul>
<li v-for="item in fruitList.list" :key="item">
{{item}}
</li>
</ul>
</template>
</SearchVue>
<br/>
<!-- 有序列表 -->
<SearchVue>
<template v-slot:Uselist="fruitList">
<ol>
<li v-for="item in fruitList.list" :key="item">
{{item}}
</li>
</ol>
</template>
</SearchVue>
</template>
<style>
</style>
结果
组件之间跨级通信 Provide / Inject
无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。这个特性有两个部分:父组件有一个
provide
选项来提供数据,子组件有一个inject
选项来开始使用这些数据。
理解
代码
App.vue
<script>
//导入自己定义的组件
import NavigationBarVue from './components/NavigationBar.vue'
export default{
data(){
return{
message:"根组件"
}
},
components:{
//加载组件
NavigationBarVue
}
}
</script>
<template>
<NavigationBarVue></NavigationBarVue>
</template>
<style>
</style>
Search.vue
<template>
<InputVue></InputVue>
</template>
<script>
import InputVue from './Input.vue'
export default {
data(){
return{
message:"父组件"
}
},
components:{
//加载组件
InputVue
}
}
</script>
<style>
</style>
非响应式
默认情况下,
provide/inject
绑定并不是响应式的如果我们更改了
祖先组件
的数据,这个变化并不会反映在 inject 的子组件
property 中。
NavigationBar.vue
<template>
<h2>祖先组件========>{{message}}</h2>
<br/>
<button @click="this.message='你好呀'">改变message按钮</button>
<br/>
<SearchVue></SearchVue>
</template>
<script>
import SearchVue from './Search.vue'
export default {
data(){
return{
message:"hello"
}
},
// provide:{msg:"Hello"} 这样使用不可传入组件实例属性 所以简单写一下用法
//可访问组件实例的属性 函数方法
provide(){
return{
//非响应式 访问
msg:this.message
}
},
components:{
//加载组件
SearchVue
}
}
</script>
<style>
</style>
Input.vue
<script>
export default{
data(){
return {
message:"子组件"
}
},
inject:['msg']
}
</script>
<template>
<h2>子组件=====>{{msg}}</h2>
</template>
<style>
</style>
展示
点按钮前
点按钮后
响应式
NavigationBar.vue
<template>
<h2>祖先组件========>函数{{message}} 对象{{obj.msg}}</h2>
<br/>
<button @click="this.message='你好呀'">改变message按钮</button>
<button @click="this.obj.msg='你也好呀'">改变obj.msg按钮</button>
<br/>
<SearchVue></SearchVue>
</template>
<script>
import SearchVue from './Search.vue'
export default {
data(){
return{
message:"hello",
obj:{
msg:"hi"
}
}
},
// provide:{msg:"Hello"} 这样使用不可传入组件实例属性 所以简单写一下用法
//可访问组件实例的属性 函数方法
provide(){
return{
//函数返回响应式数据
msg:() => this.message,
//响应式对象方式
obj:this.obj
}
},
components:{
//加载组件
SearchVue
}
}
</script>
<style>
</style>
Input.vue
<script>
export default{
data(){
return {
message:"子组件"
}
},
inject:['msg','obj'],
computed:{
newMsg(){
return this.msg()
}
}
}
</script>
<template>
<h2>子组件=====>函数{{newMsg}} 对象{{obj.msg}}</h2>
</template>
<style>
</style>
结果
点按钮前
点按钮后