vue 에서 발생 한 구덩이 의 변화 검출 문제(배열 관련)

11870 단어 vue달라지다검출
최근 프로젝트 에서 문제 가 하나 생 겼 는데 왜 그런 지 모 르 겠 어 요.그래서 마지막 으로 데모 실천 을 하고 문 서 를 찾 는 방식 으로 해 결 했 습 니 다.여기 서 정 리 를 하 겠 습 니 다.
예 1

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>vue</title>
 <script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
 <style>
  li:hover {
   cursor: pointer;
  }
 </style>
</head>
<body>
 <div class="wrap">
  <ul>
   <li v-for="item,index in items" v-on:click="handle(index)">
    <span>{{item.name}}</span>
    <span>{{numbers[index]}}</span>
   </li>
  </ul>
 </div>
 <script>
  var vm = new Vue({
   el: ".wrap",
   data: {
    numbers: [],
    items: [
     {name: 'jjj'},
     {name: 'kkk'},
     {name: 'lll'},
    ]
   },
   methods: {
    handle: function (index) {
     // WHY:     ,view    ,   console               
      if (typeof(this.numbers[index]) === "undefined" ) {
       this.numbers[index] = 1;
      } else {
       this.numbers[index]++;
      }
    }
   }
  });
 </script>
</body>
</html>

이곳 의 실현 목적 은 매우 명확 하 다.나 는 li 를 클릭 할 때 존재 여 부 를 먼저 검 측 하고 싶다.당연히 존재 하지 않 기 때문에 값 을 1 로 설정 하고 다시 클릭 하면 숫자 를 누적 시킨다.
그러나 문 제 는 클릭 한 후에 숫자 가 view 층 에서 업데이트 되 지 않 았 고 console 인쇄 를 통 해 데이터 가 확실히 업데이트 되 었 다 는 것 입 니 다.다만 view 층 에서 제때에 감지 되 지 않 았 습 니 다.저 는 vue 가 실 현 될 때 데이터 가 양 방향 으로 연결 되 었 으 니 model 층 이 변화 한 후에 왜 view 층 에서 업데이트 되 지 않 았 습 니까? 
우선,나 는 이것 이 배열 의 문제 인지 아 닌 지 를 고려 했다.그래서 나 는 다음 과 같은 예 를 테스트 했다.
예 2

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>vue</title>
 <script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
 <style>
  li:hover {
   cursor: pointer;
  }
 </style>
</head>
<body>
 <div class="wrap">
  <ul>
   <li v-for="item,index in items" v-on:click="handle(index)">
    <span>{{item.name}}</span>
    <span>{{numbers[index]}}</span>
   </li>
  </ul>
 </div>
 <script>
  var vm = new Vue({
   el: ".wrap",
   data: {
    numbers: [],
    items: [
     {name: 'jjj'},
     {name: 'kkk'},
     {name: 'lll'},
    ]
   },
   methods: {
    handle: function (index) {
     //     ,            view   
     this.items[index].name += " success";
    }
   }
  });
 </script>
</body>
</html>

이때,내 가 다시 테스트 할 때,이곳 의 모델 층 에 변화 가 생 겼 을 때,view 층 은 제때에 효과적으로 업 데 이 트 될 수 있다 는 것 을 발견 하 였 다.
배열 은 왜 안 되 지? 
그래서 문서 의 하 찮 은 곳 에서 다음 과 같은 설명 을 찾 았 습 니 다.

그 중에서 가장 중요 한 말 은-대상 이 응답 식 이 라면 속성 이 생 성 된 후에 도 응답 식 이 고 보기 업 데 이 트 를 촉발 하 는 것 입 니 다.이 방법 은 Vue 가 속성 이 추 가 된 제한 을 감지 하지 못 하 는 것 을 피 하 는 데 사 용 됩 니 다.
그러면 Vue 에서 속성 이 추가 되 는 것 을 감지 할 수 없 는 상황 은 무엇 일 까요?  참고 링크 에 의 하면 우 리 는 문서 에서 좋 은 설명 을 보 았 다.심층 응답 식 원리
우선,우 리 는 Vue 가 데이터 의 양 방향 연결 을 어떻게 실현 하 는 지 알 아야 합 니 다! 
일반 JavaScript 대상 을 Vue 인 스 턴 스 의 data 옵션 에 전달 합 니 다.Vue 는 이 대상 의 모든 속성 을 옮 겨 다 니 며 Object.defineProperty 를 사용 하여 이 속성 을 모두 getter/setter 로 변환 합 니 다.Object.defineProperty 는 ES5 만 지원 하고 shim 이 불가능 한 특성 입 니 다.이것 이 바로 Vue 가 IE8 과 더 낮은 버 전의 브 라 우 저 를 지원 하지 않 는 이유 입 니 다.
지식 보충:
접근 기 속성 은 데이터 값 을 포함 하지 않 습 니 다.getter 함수 와 setter 함 수 를 포함 합 니 다.접근 기 속성 을 읽 을 때 getter 함 수 를 호출 합 니 다.이 함 수 는 유효한 값 을 되 돌려 줍 니 다.기록 접근 기 속성 은 setter 함 수 를 호출 하고 새 값 을 입력 합 니 다.이 함 수 는 데 이 터 를 어떻게 처리 할 지 결정 합 니 다.
접근 기 속성 은 직접 정의 할 수 없습니다.Object.defineProperty()로 정의 해 야 합 니 다.
다음은 하나의 예 이다.

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>vue</title>
</head>
<body>
 <script>
  var book={
    _year:2004,
    edition:1
  };
  Object.defineProperty(book,"year",{
    get:function(){
      return this._year;
    },
    set:function(newValue){
      if(newValue>2004){
        this._year=newValue;
        this.edition+=newValue-2004;
      }
    }
  });
  console.log(book.year); // 2004             get  
  book.year=2005; //              set  
  console.log(book.edition); // 2
 </script>
</body>
</html>

이 예 는 접근 기 속성 을 잘 이해 할 수 있 을 것 이다.
따라서 대상 아래 의 방문 기 속성 값 이 바 뀌 면(vue 는 속성 을 모두 방문 기 속성 으로 바 꿉 니 다.전에 언급 했 습 니 다)set 함 수 를 호출 합 니 다.이때 vue 는 이 set 함 수 를 통 해 변 화 를 추적 하고 관련 함 수 를 호출 하여 view 그림 의 업 데 이 트 를 실현 할 수 있 습 니 다.
모든 구성 요소 인 스 턴 스 는 해당 하 는 watcher 인 스 턴 스 대상 이 있 습 니 다.구성 요소 가 렌 더 링 하 는 과정 에서 속성 을 의존 으로 기록 한 다음 의존 항목 의 setter 가 호출 될 때 watcher 에 게 다시 계산 하 라 고 알려 주 고 관련 구성 요 소 를 업데이트 합 니 다.

즉,렌 더 링 과정 에서 대상 속성의 getter 함 수 를 호출 한 다음 에 getter 함 수 는 wather 대상 에 게 이 성명 을 의존 으로 알 리 고 의존 한 후에 대상 속성 이 변화 하면 setter 함 수 를 호출 하여 watcher 에 게 알 리 고 watcher 는 구성 요 소 를 다시 렌 더 링 하여 업 데 이 트 를 완성 합 니 다.
OK!원 리 를 알 게 된 이상 우 리 는 왜 이전 배열 의 문제 가 발생 했 는 지 더 알 수 있 을 것 이다!
변화 검출 문제
현대 자 바스 크 립 트 브 라 우 저의 제한 을 받 았 습 니 다.사실은 Object.observe()방법 이 지원 되 지 않 아서 Vue 에서 대상 의 추가 나 삭 제 를 감지 할 수 없습니다.그러나 Vue 는 인 스 턴 스 를 초기 화 할 때 속성 에 대해 setter/getter 전환 과정 을 실 행 했 기 때문에 속성 은 대상 에 있어 야 Vue 를 전환 시 킬 수 있 습 니 다.
그래서 앞의 예 에 대해 이해 할 수 없습니다.-배열 에서 index 는 모두 속성 이 라 고 볼 수 있 습 니 다.우리 가 속성 을 추가 하고 값 을 부여 할 때 Vue 는 대상 의 속성 추가 나 삭 제 를 감지 할 수 없 지만 추가 하거나 삭 제 했 기 때문에 우 리 는 console 을 통 해 변 화 를 볼 수 있 기 때문에 응답 식 을 할 수 없습니다.두 번 째 예 에서 우 리 는 기 존의 속성 을 바탕 으로 수정 한 것 이다.이런 속성 들 은 처음에 Vue 에 의 해 인 스 턴 스 를 초기 화 할 때 setter/getter 의 전환 과정 을 실 행 했 기 때문에 그들의 수정 은 효과 적 이 고 model 의 데 이 터 는 실시 간 으로 view 층 에서 해당 된다.
보충 지식:Object.observe()가 무엇 입 니까?
소개 하기 전에 이 방법 은 일부 브 라 우 저 에서 실 행 될 수 있 지만 사실은 이 방법 이 폐기 되 었 다 고 잔인하게 말 할 수 밖 에 없습니다!
개요:이 방법 은 대상 의 수정 을 비동기 적 으로 감시 하 는 데 사 용 됩 니 다.대상 의 속성 이 수정 되 었 을 때 방법의 리 셋 함 수 는 질서정연 한 수정 흐름 을 제공 합 니 다.그러나 이 인 터 페 이 스 는 각 브 라 우 저 에서 제거 되 었 습 니 다.일반적인proxy 개체을 사용 할 수 있 습 니 다.      
방법:

Object.observe(obj, callback[, acceptList])
그 중에서 obj 는 감 시 된 대상 입 니 다.callback 은 리 셋 함수 입 니 다.그 중의 매개 변 수 는 changes 와 acceptList 를 포함 합 니 다.
changes 하나의 배열 입 니 다.그 안에 포 함 된 모든 대상 은 수정 행 위 를 대표 합 니 다.모든 변경 행위 의 대상 은 다음 과 같 습 니 다:
  • name:수 정 된 속성 이름 입 니 다.
  • object:수 정 된 대상 의 값 입 니 다.
  • type:이 대상 에 대해 어떤 종류의 수정 을 했 는 지,가능 한 값 은"add","update",or"delete"입 니 다.
  • oldValue:대상 이 수정 하기 전의 값 입 니 다.이 값 은"update"와"delete"에서 만 유효 합 니 다.
  • acceptList 는 주어진 대상 에서 반전 에서 감시 할 변화 유형 목록 을 지정 합 니 다.생략 하면["add","update","delete","reconfigure","setPrototype","preventExtensions"]가 사 용 됩 니 다.
    
    var obj = {
     foo: 0,
     bar: 1
    };
    
    Object.observe(obj, function(changes) {
     console.log(changes);
    });
    
    obj.baz = 2;
    // [{name: 'baz', object: <obj>, type: 'add'}]
    
    obj.foo = 'hello';
    // [{name: 'foo', object: <obj>, type: 'update', oldValue: 0}]
    
    delete obj.baz;
    // [{name: 'baz', object: <obj>, type: 'delete', oldValue: 2}]
    
    위 와 같이 chrome 도 지원 되 지 않 습 니 다.브 라 우 저의 호환성 은 다음 과 같 습 니 다.

    참고 문서:Object.ovserve()
    추천 읽 기 글:Object.observe()데이터 귀속 혁명 폭발
    해결 방법
    Vue.set(object,key,value)방법 을 사용 하여 응답 속성 을 끼 워 넣 은 대상 에 추가 합 니 다.vm.$set 인 스 턴 스 방법 도 사용 할 수 있 습 니 다.전역 Vue.set 방법의 별명 이기 도 합 니 다.
    예 3
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
     <meta charset="UTF-8">
     <title>vue</title>
     <script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
     <style>
      li:hover {
       cursor: pointer;
      }
     </style>
    </head>
    <body>
     <div class="wrap">
      <ul>
       <li v-for="item,index in items" v-on:click="handle(index)">
        <span>{{item.name}}</span>
        <span>{{numbers[index]}}</span>
       </li>
      </ul>
     </div>
     <script>
      var vm = new Vue({
       el: ".wrap",
       data: {
        numbers: [],
        items: [
         {name: 'jjj'},
         {name: 'kkk'},
         {name: 'lll'},
        ]
       },
       methods: {
        handle: function (index) {
         // WHY:     ,view    ,   console               
          if (typeof(this.numbers[index]) === "undefined" ) {
           this.$set(this.numbers, index, 1);
          } else {
           this.$set(this.numbers, index, ++this.numbers[index]);
          }
        }
       }
      });
     </script>
    </body>
    </html>
    
    
    이렇게 하면 우 리 는 최종 목적 을 실현 할 수 있다!
    제2 부분
    위의 부분 은 data 아래 에 있 는 배열 을 말 합 니 다.store 에 있 는 배열 이 라면 보통 이렇게 할 수 있 습 니 다.
    
    [ADD_ONE] (state, index) {
       if ( typeof state.numbers[index] == "undefined") {
        Vue.set(state.numbers, index, 1)
       } else {
        Vue.set(state.numbers, index, ++state.numbers[index])
       }
      }
    Vue.set()방식 으로 바 꾸 고 늘 리 는 것 이다.
    메모:index 의 증가 와 감 소 를 확인 하기 위해 Vue.set()방식 을 사용 합 니 다.
    줄 이 는 방식 은 다음 과 같다.
    
     [REMOVE_ONE] (state, index) {
       Vue.set(state.numbers, index, --state.numbers[index]);
      }
    제4 부분
    store 의 actions 에서 stroe 의 배열 을 채 워 야 한다 면 방법 은 다음 과 같 습 니 다.
    state 내용:
    
    kindnames: []
    돌연변이 내용:
    
      [ADD_KIND_NAME] (state, name) {
       state.kindnames.push(name);
      } 
    메모:여 기 는 push 방식 을 직접 사용 합 니 다.
    물론 push 를 제외 하고 우 리 는 shift 등 여러 가지 방식 도 할 수 있다. 
    actions 의 내용:
    
    commit(ADD_KIND_NAME, state.items[index++].name);
    여기,state.items[index++].name 에서 가 져 온 문자열 입 니 다.
    마지막 으로 데 이 터 를 보면 다음 과 같다.

    주:마찬가지 로 문 서 를 참고 할 수 있 습 니 다---세부 사항 과 최선 의 실천
    이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

    좋은 웹페이지 즐겨찾기