父传子【props】
- props 遵循着单向绑定 原则
- 每次父组件更新后,所有的子组件中的 props 都会被更新到最新值,
- 子组件不能修改父组件传递过来的数据,若是子组件修改了,控制台会抛出警告
js
// 父组件
<comp-child mytitle="标题11111" :isShowLogo="true" />
<comp-child mytitle="标题222222" />
// 子组件
<template>
<header>
<div v-if="isShowLogo">logo</div>
<<h2>{{ mytitle }}</h2>
</header>
</template>
<script>
export default {
// 子组件接收父组件传递的内容用:props
// props: ["mytitle", "isShowLogo"],
// 防止父组件传入的属性类型不对,使用props的type属性
props: {
mytitle: {
type: String,
},
isShowLogo: {
type: Boolean,
default: false, // 设置默认值
// required: true, // 必须传值
},
},
};
</script>
子传父【$emit】
- 父组件通过v-on事件(简写@事件)来监听子组件中的传值
- 子组件通过$emit触发事件,并传值
- 例:
- 父组件:@myevent="事件处理函数"
- 子组件:this.$emit("myevent","传递的值")
- 【注】子组件中监听的事件名称 == 父组件中v-on绑定的事件
父组件
- 组件监听子组件,方法不加(),默认会传一个事件对象e
- 事件对象e就是子组件传递过来的值
js
<template>
<!-- 父组件监听子组件,方法不加(),默认会传一个事件对象e -->
<comp-childMenu @myevent="changeEvent" />
<ul v-if="isShow">
<li v-for="data in list" :key="data">
{{ data }}
</li>
</ul>
</template>
<script>
import compChildMenu from "./comp-childMenu.vue";
export default {
data() {
return {
list: ["首页", "用户管理", "商品管理", "订单管理"],
isShow: true,
};
},
components: {
compChildMenu,
},
methods: {
changeEvent(e) {
this.isShow = !this.isShow;
console.log("父组件", e);
},
},
};
</script>
<style>
* {padding: 0px;margin: 0px;}
ul,li {list-style: none;}
ul {width: 180px;border-right: 1px solid #ccc;}
li {line-height: 50px;border-bottom: 1px solid #ccc;text-align: center;}
</style>
子组件
js
<template>
<div>
<h2>菜单 <button @click="showSide">显示 / 隐藏</button></h2>
</div>
</template>
<script>
export default {
data() {
return {
isShow: true,
};
},
methods: {
showSide() {
//子组件通过$emit触发事件,并传值,
// 父组件通过 @myevent="changeEvent" 监听事件,并接收值
this.$emit("myevent", this.isShow); // "myevent"是事件名(跟父组件中定义的一致),this.isShow是传的值
},
},
};
</script>
<style scoped>
div {display: flex;align-items: center;padding-left: 50px;background: wheat;height: 70px;}
button {margin-left: 20px;}
</style>
效果图
- 模拟菜单显示隐藏效果
父组件直接访问子组件【ref + $refs】
- ref如果绑定在dom节点上,拿到的就是 原生dom节点
- ref如果绑定在组件上,拿到的就是 组件对象,可以实现通信功能
父组件
- 步骤:
- 在子组件标签上添加ref属性,值为自定义的
- 父组件中通过【this.$refs.ref属性名.子组件中的属性名】来获取或修改子组件中的值
- 【注】this.$refs.ref属性名:获取的是子组件的实例对象(proxy)
js
<template>
<compChild title="用户名" type="text" ref="username" />
<!-- 自定义ref的属性名 -->
<compChild title="密码" type="password" ref="password" />
<button @click="login()">登录</button>
<button @click="reset()">重置</button>
</template>
<script>
import compChild from "./compChild.vue";
export default {
components: {
compChild,
},
methods: {
login() {
// 通过$refs获取子组件的值
// mytext是子组件中双向绑定input框的data状态
console.log(
`提交给后端的数据:用户名:${this.$refs.username.mytext};密码:${this.$refs.password.mytext}`
);
},
reset() {
// 通过$refs直接给子组件赋值
this.$refs.username.mytext = this.$refs.password.mytext = "";
console.log("清空数据成功!");
},
},
};
</script>
<style>
button {padding: 0px 10px;margin: 0px 20px;}
</style>
子组件
js
<template>
<div>
<label>{{ title }}:</label>
<input :type="type" v-model="mytext" />
</div>
</template>
<script>
export default {
props: ["title", "type"],
data() {
return {
mytext: "",
};
},
};
</script>
效果图
子组件直接访问父组件【$parent】
- $parent 子组件直接访问父组件(亲父亲)
- $root 直接访问根组件
父组件
js
<template>
<compChild />
<ul v-if="isShow">
<h2>父组件内列表</h2>
<li>用户管理</li>
<li>商品管理</li>
<li>订单管理</li>
</ul>
</template>
<script>
import compChild from "./compChild.vue";
export default {
components: {
compChild,
},
data() {
return {
isShow: true,
};
},
};
</script>
子组件
js
<template>
<div>
<h3>compChild -- 子组件</h3>
<button @click="clickShow()">显示 / 隐藏</button>
</div>
</template>
<script>
export default {
methods: {
clickShow() {
this.$parent.isShow = !this.$parent.isShow; //直接获取到父组件的属性,并修改
},
},
};
</script>
<style scoped>
div {background: wheat;padding: 10px;}
</style>
跨级通信【provide + inject】
- 主组件:使用provide暴露自己
- 要用主组件的其他组件:要先使用inject接收
主组件
js
<template>
<div class="box">
<compHeader></compHeader>
<compNav></compNav>
</div>
</template>
<script>
import compHeader from "./compHeader.vue";
import compNav from "./compNav.vue";
export default {
components: {
compHeader,
compNav,
},
data() {
return {
title: "首页",
};
},
provide() {
return {
app: this, // 提供数据,this表示当前组件实例对象
};
},
};
</script>
<style>
* {padding: 0px;margin: 0px;}
ul,li {list-style: none;}
</style>
组件 - 头部
js
<template>
<!-- 显示数据 -->
<h2>{{ app.title }}</h2>
</template>
<script>
export default {
inject: ["app"], // 接收父组件传递过来的数据
};
</script>
<style>
h2 {text-align: center;line-height: 80px; border-bottom: 1px solid #ccc;}
</style>
组件 - 底部导航
js
<template>
<div>
<ul>
<li v-for="item in nav" :key="item" @click="clickTitle(item)">
{{ item }}
</li>
</ul>
</div>
</template>
<script>
export default {
inject: ["app"], // 接收父组件传过来的数据
data() {
return {
nav: ["首页", "影院", "资讯", "我的"],
};
},
methods: {
clickTitle(title) {
this.app.title = title; // 修改父组件的数据
},
},
};
</script>
<style>
ul {display: flex;justify-content: space-around;background: wheat;position: fixed;width: 100%;bottom: -1px;}
li {line-height: 50px;padding: 0px 20px;}
</style>
效果图
- 单页面切换App
动态组件【component + is】
- component 动态组件
- :is="组件名" 渲染对应的组件
父组件
- 案例是是跨级通信案例的加强版
js
<template>
<div class="box">
<!-- 头部 -->
<compHeader></compHeader>
<!-- 内容区 -->
<component :is="obj[title]"></component>
<!-- 底部导航 -->
<compNav></compNav>
</div>
</template>
<script>
import compHeader from "./compHeader.vue";
import compNav from "./compNav.vue";
import Home from "./components/Home.vue";
import Cinema from "./components/Cinema.vue";
import Info from "./components/Info.vue";
import Mine from "./components/Mine.vue";
export default {
components: {
compHeader, // 头部
compNav, // 底部导航
Home, // 首页
Cinema, // 影院
Info, // 资讯
Mine, // 我的
},
data() {
return {
title: "首页",
obj: {
首页: "Home",
影院: "Cinema",
资讯: "Info",
我的: "Mine",
},
};
},
provide() {
return {
app: this, // 提供数据,this表示当前组件实例对象
};
},
};
</script>
异步组件 【defineAsyncComponent】
- 在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。
- Vue 提供了
defineAsyncComponent
方法来实现此功能
简单版
js
// 导入异步组件工具
import { defineAsyncComponent } from "vue";
// 异步导入组件
AsyncComp: defineAsyncComponent(() => import('./Foo.vue')),
加载与错误提示(加强版)
- 这里还需要创建两个组件,加载中(LoadingComponent)和加载失败(ErrorComponent)页面
js
const AsyncComp = defineAsyncComponent({
// 加载函数
loader: () => import('./Foo.vue'),
// 加载异步组件时使用的组件
loadingComponent: LoadingComponent,
// 展示加载组件前的延迟时间,默认为 200ms
delay: 200,
// 加载失败后展示的组件
errorComponent: ErrorComponent,
// 如果提供了一个 timeout 时间限制,并超时了
// 也会显示这里配置的报错组件,默认值是:Infinity
timeout: 3000
})
v-model使用在组件上
- 父组件:
- 在组件上绑定了v-model,就相当于做了以下两个步骤
- 组件上绑定modelValue属性
- 组件上绑定update:modelValue事件
- 在组件上绑定了v-model,就相当于做了以下两个步骤
- 子组件:
- 获取到父组件传递过来的值 modelValue值
- 在修改输入框中的值的时候触发@input事件,然后通过$emit方法将值传递给父组件
父组件
js
<template>
<!-- <Child type="text" v-model="username" /> -->
<!-- 原理 -->
<Child :modelValue="username" @update:modelValue="(e) => (username = e)" />
</template>
<script>
import Child from "./Child.vue";
export default {
components: {
Child,
},
data() {
return {
username: "",
};
},
};
</script>
子组件
js
<!--
<template>
<input :type="type" @input="ChangeInput" :value="modelValue" />
</template>
<script>
export default {
props: ["type", "modelValue"],
data() {
return {};
},
methods: {
ChangeInput(e) {
console.log(e.target.value);
this.$emit("update:modelValue", e.target.value);
},
},
};
</script>
-->
<!-- 原理 -->
<!--
子组件:
获取到父组件传递过来的值 modelValue值
在修改输入框中的值的时候触发@input事件,然后通过emit方法将值传递给父组件
-->
<template>
<input :type="type" @input="ChangeInput" :value="modelValue" />
</template>
<script>
export default {
props: ["type", "modelValue"],
methods: {
ChangeInput(e) {
console.log(e.target.value);
this.$emit("update:modelValue", e.target.value);
},
},
};
</script>