组件传值介绍
vue作为现在前端使用的主流框架之一,组件一直是vue的核心部分。起初,我在学习vue的过程中接触到了组件之间传值的功能,但是由于在工作中使用较少,对于这一部分没有深入的认识,导致在开发过程中遇到了一些问题,通过进一步的学习,我了解到了vue组件传值的方式以及存在两类不同的组件传值方式:同级组件之间的传值、父子组件之间的传值
1.组件作用
组件作用:用来减少Vue实例(对象)中的代码量,日后在使用Vue开发过程中,可以根据不同的业务功能将页面中划分不同的多个组件,然后多个组件去构建整个页面的布局,便于日后使用Vue进行开发时页面管理(可以做到复用组件),方便日后的维护。
2.组件定义
2.1 全局组件注册
全局组件注册给Vue实例对象,定义之后可以在Vue实例的作用范围内使用该组件
<div id="app">
<!-- 必须给需要绑定的元素,设置选择器(支持CSS的选择器,但是我推荐使用ID选择,容器是唯一) -->
<h2>{{title}}</h2>
<!-- 使用全局注册组件,可以做到复用 -->
<login></login> <login></login> </div>
<!-- 1.先引入框架的支持(VUE文件),推荐位置body结束标签的前面(使用的是浏览器运行环境
,如果使用NodeJS是另一种引入方式) -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 定义全局组件
Vue.component('login',{
template:'<diu><h2>用户登陆页面</h2></diu>' });
let vm = new Vue({
el: '#app',
data: {
title: '组件学习 - 全局组件注册'
}
});
</script>
复制代码
代码说明:
- Vue.component 用来开发全局组件
- 参数1:定义组件名称login
- 参数2:组件配置 {} template:” 用来书写组件的html代码,template中必须只能存在一个root元素
- 使用时需要在Vue实例的作用范围内根据组件名称使用全局组件
- 如果在注册组件过程中使用,驼峰命名规则组件的方式,在使用组件时,必须将驼峰的所有单词小写加入中划线(–)进行使用,个人不太推荐驼峰。
2.2局部组件注册
局部组件注册将组件注册给对应Vue实例中一个 components属性 来完成组件注册,这种方式不会对Vue实例造成累加。
<div id="app">
<!-- 必须给需要绑定的元素,设置选择器(支持CSS的选择器,但是我推荐使用ID选择,容器是唯一) -->
<h2>{{title}}</h2>
<login></login>
<register></register>
<user-table></user-table>
</div>
<!-- 第三种方式 -->
<template id="userTemplate">
<diu><h2>用户列表页面</h2></diu>
</template>
<!-- 1.先引入框架的支持(VUE文件),推荐位置body结束标签的前面(使用的是浏览器运行环境,如果使用NodeJS是另一种引入方式) -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 3.创建VUE对象(ViewModel中间件),数据Model -->
<script>
let reg = {
template: "<diu><h2>用户注册页面</h2></diu>",
};
const userTemplate = {
template: "#userTemplate",
};
let vm = new Vue({
el: "#app",
data: {
title: "组件学习 - 局部组件注册",
},
components: {
login: {
//第一种方式:直接定义
template: "<diu><h2>用户登陆页面</h2></diu>",
},
register: reg, //第二种方式:定义变量
userTable: userTemplate, //第三种方式:注意我使用了驼峰的方式,脚手架推荐方式
},
});
</script>
复制代码
3.关于父子组件的生命周期的执行过程,我会在后续增加
-
beforeCreate:组件开始初始化,仅仅注册组件自己事件和生命周期函数
-
created:组件已经注入data/methods/computed相关数据方法
-
beforeMount:将template中指向html编译Vue模版,此时还没有完成模版中内容渲染
-
mounted:将template中html编译模版进行数据渲染并且将渲染完成的数据再内存中形成虚拟dom替换template指向dom
-
beforeUpdate:当组件中data数据发生变化时,会触发beforeUpdate,此时页面中数据还是原始数据
-
updated:此时页面中数据和data属性一致
-
beforeDestroy:销毁VUE实例之前触发方法
-
destroy:VUE实例已经彻底销毁、监听进制全部消失
4.组件数据传递
4.1(vue3以下组件传递)
4.1.1 父–>子(prop)
作用 props用来给组件传递相应静态数据或者动态数据
在子组件里定义一个props,即props:[msg],msg可以是对象也可以是基本数据类型
4.1.1.1父组件内容
<template>
<div class="parent">
//传递的数据 message
<Children :msg="message"></Children>
</div>
</template>
<script>
//引入的组件
import Children from "../components/Children";
export default {
name: "Parent",
components: {
Children,
},
data() {
return {
message: "hello world",
};
},
};
</script>
复制代码
4.1.1.2子组件内容
<template>
<section>父组件传过来的消息是:{{myMsg}}</section>
</template>
<script>
export default {
name: "Children",
components: {},
//通过props接收到的内容
props: ["msg"],
data() {
return {
//可以通过this拿到传过来的数据
myMsg: this.msg,
};
},
methods: {},
};
</script>
复制代码
4.1.2子–>父(emit)
这里需要使用自定义事件,在子组件中使用this.$emit(‘myEvent’) 触发,然后在父组件中使用@myEvent监听
4.1.2.1 子组件
<template>
<section>
<br />
//声明点击事件进行传递
<div @click="clickme">click me</div>
</section>
</template>
<script>
export default {
name: "Children",
components: {},
data() {
return {
childNum: 0,
};
},
methods: {
clickme() {
// 通过自定义事件addNum把值传给父组件
this.$emit("addNum", this.childNum++);
},
},
};
</script>
复制代码
4.1.2.2 父组件
<template>
<div class="parent">
这里是计数:{{parentNum}}
//这里的addNum 是子组件传递的时候声明的时间 也就是子组件 this.$emit("addNum", this.childNum++);
<Children-Com @addNum="getNum"></Children-Com>
</div>
</template>
<script>
import ChildrenCom from "../components/Children";
export default {
name: "Parent",
components: {
ChildrenCom,
},
data() {
return {
parentNum: 0,
};
},
methods: {
// childNum是由子组件传入的
getNum(childNum) {
this.parentNum = childNum;
},
},
};
</script>
复制代码
4.1.3兄弟组件传值(通过第三方声明bus组件进行相连)
运用自定义事件e m i t的触发和监听能力,定义一个公共的事件总线evenBus,通过它作为中间桥梁,我们就可以传值给任意组件了。而且通过eventBus的使用, 可以加深emit的触发和监听能力,定义一个公共的事件eventBus,通过它作为中间桥梁,我们就可以传值给任意组件了。而且通过eventBus的使用,可以加深emit的理解
4.1.3.1 bus组件
import Vue from 'vue'
export default new Vue()
复制代码
4.1.3.2 Children1.vue(第一个兄弟组件)
<template>
<section>
<div @click="Children1">push message</div>
<br />
</section>
</template>
<script>
//引用bus组件
import eventBus from "./EventBus";
export default {
name: "Children1",
components: {},
data() {
return {
childNum: 0,
};
},
methods: {
Children1() {
// 通过事件总线发送消息
eventBus.$emit("Children1", this.childNum++);
},
},
};
</script>
复制代码
4.1.3.3 Children2.vue(第二个兄弟组件)
<template>
<section>children1传过来的消息:{{msg}}</section>
</template>
<script>
//引用bus组件
import eventBus from "./EventBus";
export default {
name: "Children2",
components: {},
data() {
return {
msg: "",
};
},
mounted() {
// 通过事件总线监听消息
eventBus.$on("Children1", (children1Msg) => {
this.msg = children1Msg;
});
},
};
</script>
复制代码
4.1.3.4 把Children1和Children2放到一个组件里
<template>
<div class="parent">
<Children1></Children1>
<Children2></Children2>
</div>
</template>
<script>
//引入的两个兄弟组件 路径组件改变
import Children1 from "../components/Children1";
import Children2 from "../components/Children2";
export default {
name: "Parent",
components: {
Children1,
Children2,
},
data() {
return {};
},
methods: {},
};
</script>
复制代码
4.1.4 路由间传值
使用问号传值
A页面跳转B页面时使用 this.$router.push(’/B?name=danseek’)
B页面可以使用 this.$route.query.name 来获取A页面传过来的值
配置的路由如下
//这个就是router里面index.js的内容
{
path: '/b/:name',
name: 'b',
component: () => import( '../views/B.vue')
}
复制代码
4.2(vue3以上的组件传值)
4.2.1父传子(props)
4.2.1.1 父组件
<template>
<div class="home">
<div>
<Children :data="num" @fatherNums="shouFun"></Children>
</div>
</div>
</template>
<script>
import { ref } from "vue";
import Children from "../components/children";
export default {
name: "Home",
components: {
Children,
},
setup(props) {
// 父传子组件
const num = ref(18);
return {
num,
};
},
};
</script>
复制代码
4.2.1.2 子组件
<template>
<div>
{{ num }}
</div>
</template>
<script>
//需要引用的ref绑定
import { ref } from "vue";
export default {
props: {
data: Number,
},
setup(props, { emit }) {
//接收也是props
const num = ref(props.data);
return {
num,
};
},
};
</script>
复制代码
4.2.2 子传父(emit)
4.2.2.1父组件
<template>
<div class="home">
<div>
<p>{{nums}}</p>
<Children :data="num" @fatherNums="shouFun"></Children>
</div>
</div>
</template>
<script>
import { ref } from "vue";
//引入的组件
import Children from "../components/children";
export default {
name: "Home",
//是通过components属性来引入的组件
components: {
Children,
},
setup(props) {
const nums = ref(null);
// 父接子
const shouFun = (val) => {
console.log(val);
nums.value = val;
};
return {
nums,
};
},
};
</script>
复制代码
4.2.2.2 子组件
<template>
<div>
<button @click="fatherNum">子传父</button>
</div>
</template>
<script>
import { ref } from "@vue/reactivity";
export default {
props: {
data: Number,
},
setup(props, { emit }) {
//通过emit和点击事件传递
const fatherNum = () => {
emit("fatherNums", 666);
};
return {
fatherNum,
};
},
};
</script>
复制代码
总结
组件传递优点
props传递数据的优点显而易见、灵活简单,可以对props数据进行数据计算、数据监听等处理,十分灵活方便,但这里单单只是父子一层。
组件传递缺点
我们子组件中使用父组件props的时候,如果涉及到一些变量赋值,修改等操作,props被莫名其妙的修改了,连同父组件的数据也被篡改了,这让我们很疑惑,父组件的props不是不能修改吗?这里怎么变了,
其实在vue中的props能不能改变,这个得分情况。
props如果是基础数据类型,当改变时,就会抛出错误:
当props是引用类型的话,我们修改这个数据的某一个属性的实话,就可以。
由此我们可以得出结论:子组件虽然不能直接对父组件prop进行重新赋值,但父组件是引用类型的时候,子组件可以修改父组件的props下面的属性。
这就很尴尬了。如果我们设计的初衷就是父组件数据也能同时被修改,这个结果可以接受,如果不希望父组件的数据有变化是,这就是一个严重的逻辑bug。这就是props通讯的风险之一。