说明
想简单封装一下组件,结果报错。
错误的实现
子组件
<template>
<el-drawer direction="rtl"
:visible.sync="visibile" size="50%">
详情
xxx
</el-drawer>
</template>
<script>
import axios from "axios";
import { outerFrontMonitorLogDetail } from "@/api/getData.js";
export default {
name: 'frontMonitorLogDetail',
props: {
visibile: { type: Boolean, default: false },
detailData: { type: Object },
},
data() {
return {
}
},
computed: {
},
mounted() {
},
created() {
},
methods: {
},
}
</script>
<style lang="css" scoped>
</style>
通过 visible
传入过来,希望可以控制页面的显示与否。
调用方
<template>
<el-container direction="vertical">
<el-main>
<frontMonitorLogDetail :visibile.sync="drawFlag"></frontMonitorLogDetail>
<el-button @click="showDraw" >点击</el-button>
</el-main>
</el-container>
</template>
<script>
import axios from "axios";
import frontMonitorLogDetail from '@/components/frontMonitorLogDetail';
export default {
components: { frontMonitorLogDetail },
name: 'test',
data() {
return {
drawFlag: false
}
},
mounted() {
},
methods: {
showDraw() {
this.drawFlag = true;
}
}
}
</script>
<style lang="css" scoped>
</style>
点击按钮,展示页面。
但是关闭会报错,因为关闭改变了 drawFlag 的属性,而且是在子控件中改变的。
错误原因
报错场景
A 组件中引用 B 组件,使用 v-model 给 B 传递参数,B 使用 props: { value } 接收内容,在 B 中根据逻辑直接修改并赋值 value, 事件触发后在浏览器 console 里看到报错,内容如下:
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.
Instead, use a data or computed property based on the prop's value. Prop being mutated: "value"
分析原因
从报错内容上来看,我们改动了子组件中引用的父组件的变量,也就是 props 中的数据,是不能这么操作的;
从提示的信息上看,使用 mutated 是否可行?
在 Vue2 中组件 props 中的数据只能单向流动,即只能从父组件通过组件的 DOM 属性 attribute 传递 props 给子组件,子组件只能被动接收父组件传递过来的数据,并且在子组件中,不能修改由父组件传来的 props 数据。
组件内不能修改props的值,同时修改的值也不会同步到组件外层,即调用组件方不知道组件内部当前的状态是什么。
2.1 这是什么原因造成?
在 vue1.x 版本中利用 props 的 twoWay 和 .sync 绑定修饰符就可以实现 props 的双向数据绑定。
在 vue2.0 中移除了组件的 props 的双向数据绑定功能,如果需要双向绑定需要自己来实现。
在 vue2.0 中组件的 props 的数据流动改为了只能单向流动,即只能由(父组件)通过组件的 v-bind:attributes 传递给(子组件),子组件只能被动接收父组件传递过来的数据,并在子组件内不能修改由父组件传递过来的 props 数据。
官方文档解释:
prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解。
虽然废弃了props的双向绑定对于整个项目整体而言是有利且正确的,但是在某些时候我们确实需要从组件内部修改props的需求
如何解决
在 Vue2.0 中,实现组件属性的双向绑定方式(算不上是绑定了,算是异步修改), 可使用如下方法
-
v-model 指令或 .sync 修饰符
-
将修改属性的方法通过 v-bind 传给子组件调用,子组件直接按方法使用即可
-
将修改属性的方法通过 v-on 传递给子组件调用,使用
$emit()
实现回调修改、或者使用this.$parent
去修改
解决例子
子组件
<template>
<el-drawer direction="rtl"
:visible.sync="drawerFlag" size="50%">
详情
xxx
</el-drawer>
</template>
<script>
import axios from "axios";
import { outerFrontMonitorLogDetail } from "@/api/getData.js";
export default {
name: 'frontMonitorLogDetail',
props: {
visibile: { type: Boolean, default: false },
detailData: { type: Object },
},
data() {
return {
detailVisible: this.visibile
}
},
computed: {
drawerFlag:{
get(){
return this.visibile
},
set(v){
// 值发生变化
this.$emit("changeDrawer",v)
}
}
},
mounted() {
},
created() {
},
methods: {
},
}
</script>
<style lang="css" scoped>
</style>
开关标识变化的时候,暴露一个 changeDrawer
方法,给父类调用。
调用
<template>
<el-container direction="vertical">
<el-main>
<frontMonitorLogDetail :visibile.sync="drawFlag" @changeDrawer="changeDrawer"></frontMonitorLogDetail>
<el-button @click="showDraw" >点击</el-button>
</el-main>
</el-container>
</template>
<script>
import axios from "axios";
import frontMonitorLogDetail from '@/components/frontMonitorLogDetail';
export default {
components: { frontMonitorLogDetail },
name: 'test',
data() {
return {
drawFlag: false
}
},
mounted() {
},
methods: {
showDraw() {
this.drawFlag = true;
},
changeDrawer(v) {
this.drawFlag = v;
}
}
}
</script>
<style lang="css" scoped>
</style>
问题解决。
参考资料
https://blog.csdn.net/u013948858/article/details/118342541