prop 单向数据流

1:浏览器报错
vue.js:584 [Vue warn]: 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: "imgSrc"

 

image.png

2:源码
错误的:
子组件:

 

html:
<img :src="imgSrc" style="width: 90%;">
prop:
    props: {
      imgSrc: {
        type: String,
        required: true
      }
methods:
     /**
       * @desc:显示弹窗
       * @param:parentToChildData  [string]  父组件向子组件传递的数据,这里是图片地址
       * @param:isVisible          [boolean]  弹窗是否可见
       * */
      isShowDia(parentToChildData, isVisible){
        let self = this
        if (!parentToChildData) {
          return
        }
        self.dialogIsVisible = isVisible
        // self.imgSrc - prop 中传递的量
        self.imgSrc = parentToChildData //<---问题出现在这里,这里操作了this身上的prop上的imgSrc
      },

此时对应的父组件:

 

html:
   <zoomInViewImage ref="zoomInViewImageDom" ></zoomInViewImage>
js:
  import zoomInViewImage from '@/components/common/banner/zoomInViewImage'
  components: {zoomInViewImage},
    data() {
      return {
        imgSrc: ""
      };
methods:{
      openZoomImageFn(imgSrc){
        let self = this
        self.$refs['zoomInViewImageDom'].isShowDia(imgSrc, true)
      }
}

问题出现在子组件中 方法中最后一句代码,这里对prop 中定义的imgSrc变量进行了赋值操作,其值本应来自父组件传递,不倡导你在子组件中对其做更改,也就是单向数据流。摘录一段官方的说法。

单向数据流

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

其实父组件这么写,可以不需要prop进行值的传递,因为在父组件中是可以调到子组件的方法的,那么把这个子组件的isShowDia方法再封一层就可以把当前组件的参数传递进去了。所有有两种修正思路。

修正后:思路1-prop
子组件:

 

html:
   <img :src="imgSrc" style="width: 90%;">
js:
   props: {
      imgSrc: {
        type: String,
        required: true
      }
    },
  isShowDia(parentToChildData, isVisible){
        let self = this
        if (!parentToChildData) {
          return
        }
        self.dialogIsVisible = isVisible
        // self.imgSrc - prop 中传递的量
        self.imgSrc = parentToChildData
      },

父组件:
这里注意:因为js中的prop采用了驼峰写法imgSrc ,写到标签身上变成属性时候要转 短横线形式 img-src。camelCase => came-case

 

html:
<zoomInViewImage ref="zoomInViewImageDom" :img-src="imgSrc"></zoomInViewImage>
js:
    import zoomInViewImage from '@/components/common/banner/zoomInViewImage'
    components: {zoomInViewImage},
    data() {
      return {
        imgSrc:""
      }
    },
    openZoomImageFn(imgSrc){
        let self = this
        self.$refs['zoomInViewImageDom'].isShowDia(imgSrc, true)
        self.imgSrc = imgSrc //<---在这里完成赋值,然后通过标签身上的prop属性把值传递给子组件
      },

修正后:思路2-利用父中调子中的函数传参
子组件:

 

html:
  <img :src="imgSrc" style="width: 90%;">
js:
    data() {
      return {
        imgSrc:""
      }
    },
      /**
       * @desc:显示弹窗
       * @param:parentToChildData  [string]  父组件向子组件传递的数据,这里是图片地址
       * @param:isVisible          [boolean]  弹窗是否可见
       * */
      isShowDia(parentToChildData, isVisible){
        let self = this
        if (!parentToChildData) {
          return
        }
        self.dialogIsVisible = isVisible
        // self.imgSrc - prop 中传递的量
        self.imgSrc = parentToChildData
      },

父组件:

 

html:
   <!--图片放大-->
   <zoomInViewImage ref="zoomInViewImageDom" ></zoomInViewImage>
js:
   import zoomInViewImage from '@/components/common/banner/zoomInViewImage'
   components: {zoomInViewImage},

    openZoomImageFn(imgSrc){
       let self = this
       self.$refs['zoomInViewImageDom'].isShowDia(imgSrc, true)
     },

完整代码:
子组件

 

<template>
  <el-dialog id="imgDia" title="" :visible.sync="dialogIsVisible">
    <img :src="imgSrc" style="width: 90%;">
  </el-dialog>
</template>

<script>
  export default {

    // 组件的名称
    name: 'zoomInViewImage',

    // props 可以是数组或对象,用于接收来自父组件的数据
    props: {
      imgSrc: {
        type: String,
        required: true
      }
    },

    // 数据绑定
    data() {
      return {
        dialogIsVisible: false,
      }
    },
    methods: {
      /**
       * @desc:显示弹窗
       * @param:parentToChildData  [string]  父组件向子组件传递的数据,这里是图片地址
       * @param:isVisible          [boolean]  弹窗是否可见
       * */
      isShowDia(parentToChildData, isVisible){
        let self = this
        if (!parentToChildData) {
          return
        }
        self.dialogIsVisible = isVisible
    
      },
      // 关闭弹窗
    }
  }

</script>

<style lang="less" rel="stylesheet/less">
  #imgDia {
    .el-dialog {
      background: none !important;
      box-shadow: none;
    }
    .el-dialog__headerbtn .el-dialog__close {
      color: #fff;
    }
  }
</style>


父组件

 

<!--/*
 * @Author: linchaoliang
 * @Date:   2018-6-19 09:57:39
 * @Last Modified by:   Macrolam
 * @Last Modified time: 2018-6-20 09:57:35
 */-->
<!-- 查询列表模板 -->
<template>
  <el-container id="bannerMan" class="search__list_for_table">
    <el-main>
      <el-row class="select_conditions mgt10">

        <!-- 需要展示的结果页面 不可以删除 没有结果 做什么搜索 -->
        <el-row class=" result__tabel">
          <!-- 表格展示区 如果是其他的内容不是表格就把下面换了吧 -->
          <el-table
            :data="listData"
            v-loading='loading'
            style="width: 100%"
            ref="multipleTable"
            :empty-text="$t('message.label.no_data')"
            max-height="700"
          >
            <!--缩略图-->
            <el-table-column
              :label="$t('message.banMan_list.thumbnail')"
              width="150">
              <template slot-scope="scope">
                <a :href="scope.row.imgSrc" target="_blank">
                  <img :src="scope.row.imgSrc" style="width: 90px;height:60px;">
                </a>
                <i class="el-icon-zoom-in" @click="openZoomImageFn(scope.row.imgSrc_2)"
                   style="position: relative;top: -20px;left: 8px;cursor:pointer;"></i>
              </template>
            </el-table-column>
          </el-table>
        </el-row>
      </el-row>
    </el-main>
    <!--图片放大-->
    <zoomInViewImage ref="zoomInViewImageDom" :img-src="imgSrc"></zoomInViewImage>
  </el-container>
</template>
<script>
  // 混合查询的方法
  import {listSearchMixin} from "@/script/mixin/mixin"
  import banManData from "../../../../test/mockData/banMan"
  /*todo*/
  import {api} from "@/api/api"
  import zoomInViewImage from '@/components/common/banner/zoomInViewImage'
  export default {
    // 组件名称
    name: "bannerMan",
    // 混合模式, 复用组件的内容
    mixins: [listSearchMixin],
    // 父子通信
    props: {},
    computed: {},
    // 数据绑定
    data() {
      return {
        labelPosition: 'left',
        optionArr: banManData.statusArr,
        listData: banManData.list, /*todo*/
        imgSrc: ""
      };
    },
    // 组件
    components: {zoomInViewImage},
    // 方法 查询方法默认为 search方法 无需再次定义 直接使用即可
    methods: {
      // 重置查询条件 在这里重置查询条件吧
      reset(){

        this.params = {}
        // 页码
        this.page = 1
        // 一页显示多少条数据
        this.page_size = 20
      },

      openZoomImageFn(imgSrc){
        let self = this
        self.$refs['zoomInViewImageDom'].isShowDia(imgSrc, true)
        self.imgSrc = imgSrc
      },
    },
  };
</script>

<style lang="less" rel="stylesheet/less">

</style>

3:原因:
把两种方式混到一起,prop数据流是单向传递的,所以报警告。



作者:麦壳儿UIandFE2
链接:https://www.jianshu.com/p/54cfaa2f5d5b
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值