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있으며 변경 사항이 부모에게 전달됩니다.

이 패턴의 장점 listLocal은 Task(의 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>

(이 경우 소품 show은 v-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.