Vue 2-Vue-warn 뮤팅 소품


181

나는 https://laracasts.com/series/learning-vue-step-by-step 시리즈를 시작했습니다 . 이 오류로 Vue, Laravel 및 AJAX 레슨을 중단했습니다 .

vue.js : 2574 [Vue warn] : 부모 구성 요소가 다시 렌더링 될 때마다 값을 덮어 쓰므로 prop를 직접 변경하지 마십시오. 대신 소품의 값을 기준으로 데이터 또는 계산 된 속성을 사용하십시오. 소품이 변경 중 : "list"(component에 있음)

main.js 에이 코드가 있습니다.

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    created() {
        this.list = JSON.parse(this.list);
    }
});
new Vue({
    el: '.container'
})

목록 소품을 덮어 쓸 때 문제가 created () 에 있지만 Vue의 초보자이므로 문제를 해결하는 방법을 완전히 모릅니다. 누구나 그것을 고치는 방법을 알고 있습니까 (그리고 이유를 설명하십시오)?


2
추측, 그것은 단지 경고 메시지이며 오류가 아닙니다.
David R

답변:


264

이것은 Vue 2에서 소품을 로컬로 변경하는 것이 안티 패턴으로 간주 된다는 사실과 관련이 있습니다.

소품을 로컬로 변경 하려면 지금해야 할 일은 값을 초기 값으로 data사용 하는 필드를 선언 한 props다음 사본을 변경하는 것입니다.

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    data: function () {
        return {
            mutableList: JSON.parse(this.list);
        }
    }
});

이에 대한 자세한 내용은 Vue.js 공식 안내서를 참조하십시오.


주 1 : 것을 제발 참고 는해야 하지 당신에 같은 이름을 사용 prop하고data , 즉 :

data: function () { return { list: JSON.parse(this.list) } // WRONG!!

2 주 : 이후 나는 약간의 혼동이 느낌 에 대한 props반응성 , 나는에보고해야 할 제안 스레드를


3
이것은 훌륭한 답변이지만 이벤트 바인딩을 포함하도록 업데이트해야한다고 생각합니다. 자식에서 트리거 된 이벤트는 부모를 업데이트하여 업데이트 된 소품을 자식에게 전달합니다. 이런 식으로 진실의 근원은 여전히 ​​모든 구성 요소에 걸쳐 유지됩니다. 여러 구성 요소에서 공유되는 상태를 관리하기 위해 Vuex를 언급 할 가치가 있습니다.
웨스 하퍼

@WesHarper, .sync 수정자를 속기로 사용하여 부모의 업데이트 이벤트를 자동으로 얻을 수도 있습니다.
danb4r

48

Vue 패턴이 props위아래로 움직 events입니다. 간단하게 들리지만 사용자 컴포넌트를 작성할 때 잊어 버리기 쉽습니다.

Vue 2.2.0부터 v-model을 사용할 수 있습니다 ( 계산 된 속성 포함 ). 이 조합은 구성 요소 사이에 간단하고 깨끗하며 일관된 인터페이스를 만듭니다.

  • props구성 요소에 전달 된 모든 항목 은 반응 상태로 유지됩니다 (즉, watch변경 사항이 감지 될 때 복제되지 않았거나 로컬 복사본을 업데이트하는 기능이 필요하지 않습니다 ).
  • 변경 사항은 자동으로 부모에게 전달됩니다.
  • 여러 레벨의 구성 요소와 함께 사용할 수 있습니다.

계산 된 속성을 사용하면 setter와 getter를 별도로 정의 할 수 있습니다. 이를 통해 Task컴포넌트를 다음과 같이 다시 작성할 수 있습니다.

Vue.component('Task', {
    template: '#task-template',
    props: ['list'],
    model: {
        prop: 'list',
        event: 'listchange'
    },
    computed: {
        listLocal: {
            get: function() {
                return this.list
            },
            set: function(value) {
                this.$emit('listchange', value)
            }
        }
    }
})  

모델 프로퍼티 정의 prop와 연관된 v-model및 이벤트의 변경에 출사한다. 그런 다음 다음과 같이 부모에서이 구성 요소를 호출 할 수 있습니다.

<Task v-model="parentList"></Task>

listLocal계산 재산권 부품 내에 간단하고 게터 세터 인터페이스를 제공한다 (전용 가변되는 것처럼 생각). 내에서 #task-template렌더링 할 수 listLocal있으며 반응 상태를 유지합니다 (즉, parentList변경하면 Task구성 요소 가 업데이트 됩니다). listLocalsetter (예 :) 를 호출하여 음소거 할 수 this.listLocal = newList있으며 변경 사항이 부모에게 전달됩니다.

이 패턴의 장점 listLocalTask(의 v-model) 하위 구성 요소에 전달할 수 있으며 하위 구성 요소의 변경 사항이 최상위 구성 요소로 전파된다는 것입니다.

예를 들어, EditTask작업 데이터를 수정하기위한 별도의 구성 요소가 있다고 가정합니다. 동일 v-model하고 계산 된 속성 패턴 을 사용하여을 사용하여 listLocal구성 요소에 전달할 수 있습니다 v-model.

<script type="text/x-template" id="task-template">
    <div>
        <EditTask v-model="listLocal"></EditTask>
    </div>
</script>

만약 EditTask이 방출이 적절하게 호출 변화 set()listLocal최고 수준에 이벤트를 전파함으로써합니다. 마찬가지로 EditTask구성 요소는을 사용하여 다른 자식 구성 요소 (예 : 양식 요소)를 호출 할 수도 있습니다 v-model.


1
동기화를 제외하고 비슷한 것을하고 있습니다. 문제는 변경 이벤트를 생성 한 후 다른 메소드를 실행해야하지만 메소드가 실행되고 getter에 도달하면 변경 이벤트가 아직 리스너에 의해 선택되지 않았기 때문에 이전 값을 얻습니다. 아이에게 다시 전파되었습니다. 이것은 부모 업데이트가 다시 전파 될 때까지 로컬 데이터가 정확하도록 소품을 변경하려는 경우입니다. 이와 관련하여 생각이 있습니까?
koga73

나는 setter에서 getter를 호출하는 것이 좋지 않다고 생각합니다. 고려해야 할 것은 계산 된 속성을 감시하고 거기에 논리를 추가하는 것입니다.
chris

소품과 이벤트가 나와 함께 클릭되었습니다. 이것은 VueJs 문서에서 어디에 설명되어 있습니까?
nebulousGirl


나는 이것이 부모 구성 요소에 변경 사항을 방출하는보다 고통없는 방법이라고 생각합니다.
무하마드

36

Vue는 경고합니다. 컴포넌트에서 prop을 변경하지만 부모 컴포넌트가 다시 렌더링되면 "list"가 덮어 쓰여지고 모든 변경 사항을 잃게됩니다. 따라서 그렇게하는 것은 위험합니다.

대신 다음과 같이 계산 된 속성을 사용하십시오.

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    computed: {
        listJson: function(){
            return JSON.parse(this.list);
        }
    }
});

25
양방향 바인딩 소품은 어떻습니까?
Josh R.

7
양방향 데이터 바인딩을 원하는 경우 사용자 정의 이벤트를 사용해야합니다. 자세한 정보는 다음을 참조하십시오. vuejs.org/v2/guide/components.html#sync-Modifier 여전히 소품을 직접 수정할 수는 없습니다. 소품 교체를 처리합니다.
Marco

22

Lodash를 사용하는 경우 소품을 반환하기 전에 복제 할 수 있습니다. 이 패턴은 부모와 자식 모두에서 해당 소품을 수정하는 경우에 유용합니다.

컴포넌트 그리드 에 소품 목록 이 있다고 가정 해 봅시다. .

부모 구성 요소에서

<grid :list.sync="list"></grid>

하위 구성 요소

props: ['list'],
methods:{
    doSomethingOnClick(entry){
        let modifiedList = _.clone(this.list)
        modifiedList = _.uniq(modifiedList) // Removes duplicates
        this.$emit('update:list', modifiedList)
    }
}

19

프로포즈 다운, 이벤트 업. Vue의 패턴입니다. 요점은 부모로부터 전달되는 소품을 변경하려고 할 때입니다. 작동하지 않으며 부모 구성 요소가 반복적으로 덮어 씁니다. 하위 구성 요소는 sth를 수행하도록 상위 구성 요소에 알리는 이벤트 만 생성 할 수 있습니다. 이 제한이 마음에 들지 않으면 VUEX를 사용할 수 있습니다 (실제로이 패턴은 복잡한 구성 요소 구조를 빨아들입니다 .VUEX를 사용해야합니다!)


2
이벤트 버스를 이용하는 옵션도 있습니다
Steven Pribilinskiy

이벤트 버스는 vue 3에서 기본적으로 지원되지 않습니다. github.com/vuejs/rfcs/pull/118
AlexMA

15

하위 구성 요소의 소품 값을 변경해서는 안됩니다. 정말로 변경해야 할 경우 사용할 수 있습니다 .sync. 이처럼

<your-component :list.sync="list"></your-component>

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    created() {
        this.$emit('update:list', JSON.parse(this.list))
    }
});
new Vue({
    el: '.container'
})

7

VueJs 2.0에 따르면 컴포넌트 내부의 prop을 변경해서는 안됩니다. 그들은 부모에 의해서만 변이됩니다. 따라서 이름이 다른 데이터에 변수를 정의하고 실제 소품을 보면서 업데이트해야합니다. 부모가 목록 소품을 변경 한 경우 구문 분석하여 mutableList에 할당 할 수 있습니다. 다음은 완벽한 솔루션입니다.

Vue.component('task', {
    template: ´<ul>
                  <li v-for="item in mutableList">
                      {{item.name}}
                  </li>
              </ul>´,
    props: ['list'],
    data: function () {
        return {
            mutableList = JSON.parse(this.list);
        }
    },
    watch:{
        list: function(){
            this.mutableList = JSON.parse(this.list);
        }
    }
});

mutableList를 사용하여 템플릿을 렌더링하므로 구성 요소에서 목록 소품을 안전하게 유지합니다.


비싼 시계를 사용하지 않습니까?
킥 Buttowski

6

구성 요소에서 소품을 직접 변경하지 마십시오. 변경 해야하는 경우 다음과 같이 새 속성을 설정하십시오.

data () {
    return () {
        listClone: this.list
    }
}

그리고 listClone의 값을 변경하십시오.


6

대답은 간단합니다. 값을 일부 로컬 구성 요소 변수 (데이터 속성, 게터, 세터 또는 감시자로 계산할 수 있음)에 할당하여 직접 소품 변이를 끊어야합니다.

다음은 감시자를 사용하는 간단한 솔루션입니다.

<template>
  <input
    v-model="input"
    @input="updateInput" 
    @change="updateInput"
  />

</template>

<script>
  export default {
  props: {
    value: {
      type: String,
      default: '',
    },
  },
  data() {
    return {
      input: '',
    };
  },
  watch: {
    value: {
      handler(after) {
        this.input = after;
      },
      immediate: true,
    },
  },
  methods: {
    updateInput() {
      this.$emit('input', this.input);
    },
  },
};
</script>

데이터 입력 구성 요소를 만드는 데 사용하는 것으로 제대로 작동합니다. 부모로부터 전송 된 모든 새로운 데이터 (v 모델 (ed))는 값 감시자에 의해 감시되며 입력 변수에 할당되며 입력이 수신되면 해당 조치를 포착하고 데이터가 입력되었음을 제안하는 부모에게 입력을 방출 할 수 있습니다 양식 요소에서.


5

나는 또한이 문제에 직면했다. $on및 사용 후 경고가 사라졌습니다 $emit. 사용 $on과 비슷 $emit하며 자식 구성 요소에서 부모 구성 요소로 데이터를 보내는 것이 좋습니다.


4

소품을 변경하려면 object를 사용하십시오.

<component :model="global.price"></component>

구성 요소:

props: ['model'],
methods: {
  changeValue: function() {
    this.model.value = "new value";
  }
}

참고로 객체를 변형 할 때는주의해야합니다. Javascript 객체는 참조로 전달되지만주의 사항이 있습니다. 변수 과 동일하게 설정하면 참조가 손상됩니다 .
ira

3

이와 같은 계산 방법을 추가해야합니다

component.vue

props: ['list'],
computed: {
    listJson: function(){
        return JSON.parse(this.list);
    }
}

3

https://vuejs.org/v2/guide/components.html 에 따른 단방향 데이터 흐름 구성 요소는 단방향 데이터 흐름을 따르고 모든 소품은 자식 속성과 부모 사이의 단방향 바인딩을 형성합니다. 하나는 부모 속성이 업데이트 될 때 자식으로 흐르지 만 다른 방향으로는 흐르지 않으므로 자식 구성 요소가 실수로 부모를 변경하지 못하게하여 앱의 데이터 흐름을 이해하기 어렵게 만들 수 있습니다.

또한 부모 구성 요소가 업데이트 될 때마다 자식 구성 요소의 모든 소품이 최신 값으로 새로 고쳐집니다. 즉, 하위 구성 요소 내에서 소품을 변경하려고 시도해서는 안됩니다. 당신이 .vue하면 콘솔에서 경고합니다.

일반적으로 소품을 변경하려는 유혹이있는 경우는 두 가지가 있습니다. 소품은 초기 값을 전달하는 데 사용됩니다. 자식 구성 요소는 나중에 로컬 데이터 속성으로 사용하려고합니다. 소품은 변형이 필요한 원시 값으로 전달됩니다. 이러한 사용 사례에 대한 정답은 다음과 같습니다. prop의 초기 값을 초기 값으로 사용하는 로컬 데이터 특성을 정의하십시오.

props: ['initialCounter'],
data: function () {
  return { counter: this.initialCounter }
}

prop의 값에서 계산 된 계산 된 속성을 정의하십시오.

props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

2

Vue.js 소품은 Vue의 안티 패턴으로 간주되므로 변경되지 않습니다.

필요한 접근 방식 은 목록 의 원래 prop 속성 을 참조하는 구성 요소에 데이터 속성을 만드는 것입니다.

props: ['list'],
data: () {
  return {
    parsedList: JSON.parse(this.list)
  }
}

이제 구성 요소에 전달 된 목록 구조가 구성 요소의 data속성을 통해 참조되고 변경 됩니다 :-)

list 속성을 구문 분석하는 것 이상을 수행하려면 Vue 구성 요소의 computed속성을 사용하십시오. 이를 통해 소품에 깊이를 더 많이 부여 할 수 있습니다.

props: ['list'],
computed: {
  filteredJSONList: () => {
    let parsedList = JSON.parse(this.list)
    let filteredList = parsedList.filter(listItem => listItem.active)
    console.log(filteredList)
    return filteredList
  }
}

위의 예는 목록 소품을 구문 분석 하고 활성 목록 템플릿으로 필터링하고 슈 니트 및 낄낄 거림을 위해 로그 아웃 합니다. 반환합니다.

참고 : data& computed속성 모두 템플릿에서 동일하게 참조됩니다.

<pre>{{parsedList}}</pre>

<pre>{{filteredJSONList}}</pre>

computed속성 (메소드)을 호출해야 한다고 생각하기 쉽습니다.


2
Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    computed: {
      middleData() {
        return this.list
      }
    },
    watch: {
      list(newVal, oldVal) {
        console.log(newVal)
        this.newList = newVal
      }
    },
    data() {
      return {
        newList: {}
      }
    }
});
new Vue({
    el: '.container'
})

아마도 이것은 당신의 요구를 충족시킬 것입니다.


2

최고의 답변에 덧붙여서

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    data: function () {
        return {
            mutableList: JSON.parse(this.list);
        }
    }
});

배열에 의한 props 설정은 dev / prototyping을위한 것이며, 프로덕션 환경에서는 prop 유형 ( https://vuejs.org/v2/guide/components-props.html )을 설정하고 prop가없는 경우 기본값을 설정하십시오 부모에 의해 채워졌습니다.

Vue.component('task', {
    template: '#task-template',
    props: {
      list: {
        type: String,
        default() {
          return '{}'
        }
      }
    },
    data: function () {
        return {
            mutableList: JSON.parse(this.list);
        }
    }
});

이렇게하면 mutableList정의되지 않은 JSON.parse 오류 대신 빈 객체를 얻을 수 있습니다.


0

Vue.js는 이것을 안티 패턴으로 간주합니다. 예를 들어 다음과 같은 소품을 선언하고 설정하십시오.

this.propsVal = 'new Props Value'

따라서이 문제를 해결하려면 소품에서 데이터 또는 Vue 인스턴스의 계산 된 속성으로 값을 가져와야합니다.

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

이것은 확실히 작동합니다.


0

위의 문제 외에도 다음과 같은 문제가있는 다른 사용자의 경우 :

"props 값이 필요하지 않아서 항상 반환되지 않으면 전달 된 데이터는 undefined(빈 대신) 반환 됩니다." 어느 것이 <select>기본값을 망칠 수 있는지, 나는 beforeMount()다음과 같이 값이 설정되어 있는지 (그리고 그렇지 않으면 설정되어 있는지) 확인하여 해결했습니다 .

JS :

export default {
        name: 'user_register',
        data: () => ({
            oldDobMonthMutated: this.oldDobMonth,
        }),
        props: [
            'oldDobMonth',
            'dobMonths', //Used for the select loop
        ],
        beforeMount() {
           if (!this.oldDobMonth) {
              this.oldDobMonthMutated = '';
           } else {
              this.oldDobMonthMutated = this.oldDobMonth
           }
        }
}

HTML :

<select v-model="oldDobMonthMutated" id="dob_months" name="dob_month">

 <option selected="selected" disabled="disabled" hidden="hidden" value="">
 Select Month
 </option>

 <option v-for="dobMonth in dobMonths"
  :key="dobMonth.dob_month_slug"
  :value="dobMonth.dob_month_slug">
  {{ dobMonth.dob_month_name }}
 </option>

</select>

0

많은 코드, 감시자 및 계산 된 속성을 사용하지 않는 데 도움이되는이 대답을주고 싶습니다. 어떤 경우에는 이것이 좋은 해결책이 될 수 있습니다.

소품은 단방향 통신을 제공하도록 설계되었습니다.

show/hide소품 이있는 모달 버튼 이 있으면 가장 좋은 해결책은 이벤트를 내보내는 것입니다.

<button @click="$emit('close')">Close Modal</button>

그런 다음 모달 요소에 리스너를 추가하십시오.

<modal :show="show" @close="show = false"></modal>

(이 경우 소품 showv-if="show"베이스 모달에서 직접 쉽게 사용할 수 있기 때문에 아마도 불필요합니다 )


0

나는 개인적으로 항상 소품을 변경 해야하는 경우 먼저 계산 된 속성으로 전달하고 거기에서 돌아옵니다. 그러면 소품을 쉽게 돌연변이 할 수 있습니다. 구성 요소 또는 우리는 또한 볼 수 있습니다.


0

Vue props는 단방향 데이터 흐름이므로 자식 구성 요소가 실수로 부모의 상태를 변경하지 못하게합니다.

공식 Vue 문서 에서이 문제를 해결하는 두 가지 방법을 찾을 것입니다

  1. 하위 구성 요소가 소품을 로컬 데이터로 사용하려면 로컬 데이터 속성을 정의하는 것이 가장 좋습니다.

      props: ['list'],
      data: function() {
        return {
          localList: JSON.parse(this.list);
        }
      }
    
  2. 소품은 변형이 필요한 원시 값으로 전달됩니다. 이 경우 prop의 값을 사용하여 계산 된 속성을 정의하는 것이 가장 좋습니다.

      props: ['list'],
      computed: {
        localList: function() {
           return JSON.parse(this.list);
        },
        //eg: if you want to filter this list
        validList: function() {
           return this.list.filter(product => product.isValid === true)
        }
        //...whatever to transform the list
      }
    
    

0

예, vue2의 속성 변경은 안티 패턴입니다. 그러나 ... 다른 규칙을 사용하여 규칙을 어 기고 앞으로 나아가십시오! Paret 범위의 컴포넌트 속성에 .sync 수정자를 추가하면됩니다. <your-awesome-components :custom-attribute-as-prob.sync="value" />

🤡은 배트맨을 죽이는 것이 간단합니다 😁


0

TypeScript가 선호하는 언어 인 경우 개발

<template>
<span class="someClassName">
      {{feesInLocale}}
</span>
</template>  



@Prop({default: 0}) fees: any;

// computed are declared with get before a function
get feesInLocale() {
    return this.fees;
}

그리고 아닙니다

<template>
<span class="someClassName">
      {{feesInLocale}}
</span>
</template>  



@Prop() fees: any = 0;
get feesInLocale() {
    return this.fees;
}

0

나는 줄 때 아래 스낵바 구성 요소이며, 플로팅 작업을 이 같은 V 모델에 직접 변수를 작동하는지하지만 콘솔에서이 오류 등을 제공합니다

부모 구성 요소가 다시 렌더링 될 때마다 값을 덮어 쓰므로 prop을 직접 변경하지 마십시오. 대신 소품의 값을 기준으로 데이터 또는 계산 된 속성을 사용하십시오.

<template>
        <v-snackbar v-model="snackbar">
        {{ text }}
      </v-snackbar>
</template>

<script>
    export default {
        name: "loader",

        props: {
            snackbar: {type: Boolean, required: true},
            text: {type: String, required: false, default: ""},
        },

    }
</script>

이 돌연변이 오류를 제거하는 올바른 방법은 감시자를 사용하는 것입니다

<template>
        <v-snackbar v-model="snackbarData">
        {{ text }}
      </v-snackbar>
</template>

<script>
/* eslint-disable */ 
    export default {
        name: "loader",
         data: () => ({
          snackbarData:false,
        }),
        props: {
            snackbar: {type: Boolean, required: true},
            text: {type: String, required: false, default: ""},
        },
        watch: { 
        snackbar: function(newVal, oldVal) { 
          this.snackbarData=!this.snackbarDatanewVal;
        }
      }
    }
</script>

이 스낵바를로드 할 주요 구성 요소에서이 코드를 수행하면됩니다.

 <loader :snackbar="snackbarFlag" :text="snackText"></loader>

이것은 나를 위해 일했다

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.