避免直接改变道具,因为该值将被覆盖

Posted

技术标签:

【中文标题】避免直接改变道具,因为该值将被覆盖【英文标题】:Avoid mutating a prop directly since the value will be overwritten 【发布时间】:2017-03-27 06:12:59 【问题描述】:

我在升级到 Vue 2.0 时遇到非常常见的问题

我收到警告:

避免直接改变prop,因为值会被覆盖 每当父组件重新渲染时。相反,使用数据或 基于道具值的计算属性。道具被变异: “用户名”(在组件中找到)

我多次阅读文档,但我仍然无法理解如何修复它。

usernamepassword 在主 Vue 应用程序中声明。

这是我的代码:

var GuestMenu = Vue.extend(
   props : ['username', 'password'],
      template: `
        <div id="auth">
            <form class="form-inline pull-right">
                <div class="form-group">
                    <label class="sr-only" for="UserName">User name</label>
                  <input type="username" v-model="username" class="form-control" id="UserName" placeholder="username">
                </div>
                <div class="form-group">
                  <label class="sr-only" for="Password">Password</label>
                  <input type="password" v-model="password" class="form-control" id="Password" placeholder="Password">
                </div>
            </form>
        </div>`,
    );

 

App = new Vue ( 
   el: '#app',
  data: 
    
      topMenuView: "guestmenu",
      contentView: "guestcontent",
      username: "",
      password: "",

    
)

我尝试了v-bind,但它似乎不起作用,我不明白为什么。它应该将值绑定到父级(主 Vue 应用程序)

【问题讨论】:

请看这个问题***.com/questions/39868963/…,应该能帮到你 Vue 2 - Mutating props vue-warn的可能重复 【参考方案1】:

从 Vue 2.3.0 开始,您可以使用 .sync 修饰符:

来自https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier的示例:

<text-document :title.sync="title"></text-document>

在你的控制器中...

this.$emit('update:title', newTitle)

【讨论】:

【参考方案2】:

您应该使用 getter 和 setter 创建一个计算属性,然后使用$emit 更新该属性,例如:

var GuestMenu = Vue.extend(
    props: ['username', 'password'],
    template: `
    <div id="auth">
        <form class="form-inline pull-right">
            <div class="form-group">
                <label class="sr-only" for="UserName">User name</label>
              <input type="username" v-model="usernameInput" class="form-control" id="UserName" placeholder="username">
            </div>
            <div class="form-group">
              <label class="sr-only" for="Password">Password</label>
              <input type="password" v-model="passwordInput" class="form-control" id="Password" placeholder="Password">
            </div>
        </form>
    </div>`,
    computed: 
        usernameInput: 
            get: function()
                return this.username;
            ,
            set: function(newValue)
                this.$emit('update:username', newValue)
               
        ,
        passwordInput: 
            get: function()
                return this.password;
            ,
            set: function(newValue)
                this.$emit('update:password', newValue)
               
        ,
    
);

【讨论】:

这对我有用,但我必须从第一个答案中添加 .sync 位。【参考方案3】:

Vue.js 认为这是反模式。例如,声明和设置一些 props,如

this.propsVal = 'new Props Value'

所以要解决这个问题,你必须从 Vue 实例的数据或计算属性的 props 中获取值。喜欢……

props: ['propsVal'],
data: function() 
   return 
       propVal: this.propsVal
   ;
,
methods: 
...

你可以像往常一样使用你的 props 值。

【讨论】:

这不起作用 - 我在更改 this.propsVal 时仍然收到错误 @Gerfried 取决于变量的类型。如果你有一个对象/数组,它就不起作用,因为它只是一个新的引用。它仅适用于布尔值、字符串和数字等原语。如果你想让它工作,你必须克隆对象。例如。使用 lodash _.cloneDeep 或使用 JSON.parse(JSON.stringify(yourObjectProp))。【参考方案4】:

我不确定您到底想达到什么目标,但我会从中选择两个选项。

第一个:一切都是为了摆脱这个警告。

data () 
  return 
    childVal: this.parentVal
  

第二个:你想在父母和孩子之间进行交流。

如果我没记错的话,这是一个 &lt;input&gt; 在子组件中与其父组件通信的基本示例。

父 HTML:

<p> user </p>
<child-component v-model="user">

父 JS:

data () 
  return 
    user: 'John'
  

子 HTML:

<input v-bind:value="value" @input="$emit('input', $event.target.value)">

儿童 JS:

props: ['value']

工作示例:http://jsfiddle.net/kf3aLr1u/

您还可以在文档https://vuejs.org/v2/guide/components.html 中阅读更多相关信息。

【讨论】:

【参考方案5】:

如果你想改变道具 - 使用对象。

<component :user="global.user"></component>

组件:

    props: ['user'],
    methods: 
      setUser: function() 
        this.user.username= "User";
        this.user.password= "myPass123";
      
    

【讨论】:

你不应该使用对象来“绕过”这个警告。相反,您根本不应该修改道具,而是将它们分配给本地可变的“数据”属性。如果你需要修改全局数据,你应该考虑Vuex,但无论你如何处理全局,你都不应该编辑props。 我正在使用道具将组件中的状态别名为小组件。我想改变这个组件中的道具。在纯函数方面:我有很多纯函数可以操作单个不纯函数。我讨厌函数式编程中的这些 vue 雏形。 React + MobX 没有这种垃圾。 @DaveGoodchild 您在重申 vue.js 文档/团队中的“派对路线”时 100% 正确,例如访问 [本指南] (vuejs.org/v2/guide/comparison.html) 并搜索“意见”,您会看到他们将 vue 作为“无意见”的框架/库出售。然后在整个社区/文档中,我看到极端的“意见”表现为硬的“事实”。我希望我的组件改变道具。我不希望发出数百个事件的 Vuex/样板代码的复杂性。但这不是vue团队的“意见”。至少他们已经从主页上删除了“无主见”。 给猫剥皮的方法有很多,但在 vue 世界中只有一种! 例如看过vue的原作者说html表单中从来没有隐藏字段的用例!!!!!!我不敢苟同。【参考方案6】:

具有适当 getsetcomputed 属性为我工作:

computed: 
  dialogDataProp: 
      get: function() 
        return this.dialog;
      ,
      set: function() 
    

上面用于切换对话框的代码。

【讨论】:

你能提供完整的父子道具事件吗?这是一个子计算属性吗?你如何设置你的父母? 所以,它是&lt;v-dialog v-model="dialogDataProp"&gt;,我按照Vuetify 使用它 - 并且有getset 来控制切换。 对不起,这里还有props: dialog: type: Boolean, default: false ,'parent' 正在为prop :dialog 传递值。但是,我们不会改变 prop - 我们使用 computed,如图所示。 我也使用 vuetify 的 v-dialog,但是当对话框单击外部并关闭对话框时,对话框的状态不会变为 false。 感谢您的回复,现在我终于可以使用 set() 在父级中传递 false 值并将对话框值更改为父级,以便我现在可以使用道具切换 v-dialog 【参考方案7】:

如果你的道具是一个物体,快速解决方案。

您可以避免使用$emit 或在javascript 中使用Object.assign() 来避免出现该错误。 这与v-model 属性的作用相同。

示例:

// Update the user
Object.assign(this.userProp, user);

【讨论】:

【参考方案8】:

你可以使用emit,那么:

<text-document :title.sync="title"></text-document>

改变值是:

this.$emit('update:title', newTitle)

或其他方式:

this.$emit('update')

你可以上模板:

<text-document :update="oneFunctionInMethods()"></text-document>

【讨论】:

【参考方案9】:

当您使用 v-bind 时,该属性使用两个方向绑定,这就是您收到警告的原因。

如果需要从父级Vue组件传递初始用户名,可以使用v-bind和另一个数据属性如_username,并在组件创建时将初始值从属性复制到内部数据:

props : ['username', 'password'],
data () 
  return 
    _username: this.username,
    _password: this.password
  

编辑:您还可以使用 $watch 在属性更改时更新 _username / _password 组件数据。

【讨论】:

我是否正确理解 this.username 中的 this 指向父级? this 指向属性用户名和密码(当前组件),虽然我不明白unexpeced token : 错误 TiagoLr,“来自父级Vue组件”不是来自父级,而是来自父级。用户指定后,应将其发送给父级。 啊,那我很糟糕,要从孩子与父母交流,您应该使用事件:vuejs.org/v2/guide/…。您也可以使用this.$parent,尽管这不是推荐的模式。 其实要拥有一个更灵活的应用你需要使用vuex和store patterngithub.com/vuejs/vuex【参考方案10】:
var GuestMenu = Vue.extend(
   props : 
    uNpW:type:Object
   
      template: `
        <div id="auth">
            <form class="form-inline pull-right">
                <div class="form-group">
                    <label class="sr-only" for="UserName">User name</label>
                  <input type="username" v-model="uNpW.username" class="form-control" id="UserName" placeholder="username">
                </div>
                <div class="form-group">
                  <label class="sr-only" for="Password">Password</label>
                  <input type="password" v-model="uNpW.password" class="form-control" id="Password" placeholder="Password">
                </div>
            </form>
        </div>`,
    );
App = new Vue ( 
   el: '#app',
  data: 
    
      topMenuView: "guestmenu",
      contentView: "guestcontent",
      unAndPw:username: "",password: ""

    
)
in main html
<guest-menu :uNpW=unAndPw> </guest-menu>

你不需要发射或任何其他东西

【讨论】:

【参考方案11】:

按照这个非常简单的说明进行操作:

//Parent Component
<InsertForm :formSchema="formSchema" v-on:getFormSchema="setFormSchema"></InsertForm>
<script>
import InsertForm from "./insertForm.vue"; //select file

export default 
components:  InsertForm ,

data: () => (
    formSchema:
       id:'',
       webUrl:''
    ,
),

methods: 
      setFormSchema(data)
      
        this.formSchema = data.formSchema;
      
    

</script>

// From Child Component. That means from insertForm.vue file

<script>

export default 
    props: ["formSchema"],
    data: () => (

    ),

//Where you need
this.$emit("getFormSchema", 'formSchema':"id":1,"webUrl":"bdprescription.com");
</script>

【讨论】:

以上是关于避免直接改变道具,因为该值将被覆盖的主要内容,如果未能解决你的问题,请参考以下文章

避免直接改变道具

避免直接改变道具 - Datatable VueJS

Vue警告避免改变道具“taskToEdit”

[Vue 警告]:避免直接改变 prop

避免在可重用组件中直接改变 prop

Vue关闭组件返回避免直接改变道具