在Vue中,爲何我更新了數據,視圖卻沒法更新渲染?

1.更新了數據,視圖卻沒法更新渲染的緣由?

使用Vue的小夥伴,都不免會遇到這麼一個「坑」vue

爲何我更新了數據,視圖卻沒法渲染?vue-cli

舉個例子api

<!--使用vue-cli腳手架-->
<template>
  <div>
    <button @click="change">改變對象屬性</button>
    <button @click="add">添加對象屬性</button>
    <div v-for="(val, key) in obj" :key="key">
      {{ val }}
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      obj: {
        a: '1',
        b: '2',
        c: '3'
      }
    }
  },
  methods: {
    change () {
      this.obj.a = '0'
    },
    add () {
      this.obj.d = '4'
    }
  }
}
</script>
複製代碼

當我點擊了 「改變對象屬性」 按鈕,視圖發生了更新數組

而我點擊了 「添加對象屬性」 按鈕,視圖而不更新markdown

而在打開 vue-devtools 工具工具

咱們能夠發現其實數據是已經更新了!oop

只是視圖沒有更新! ui

那這是爲何呢?this

其實這就是涉及的Vue的響應式原理spa

在Vue2.x中,響應式的核心api是 Object.defineProperty

下面用一段js代碼,模擬一下vue響應式源碼

 // 定義空對象
  const propsObj = {}
  
  // 定義監聽對象
  const obj = {
    a: '1',
    b: '2',
    c: '3'
  }
  
  // 模擬更新視圖
  const updateView = (key, oldVal, newVal) => {
    console.log(`視圖更新了~`)
    console.log(`key值${key}更新,從 ${oldVal} 更新到 ${newVal} `)
  }
  
  // 定義監聽響應式方法
  const  defineReactive = (target, key, value) => {
   // 核心api
    Object.defineProperty(target, key, {
      get() {
        return value
      },
      set(newValue) {
        updateView(key, value, newValue)
        value = newValue
      }
    })
  }
  
  // 遍歷設置監聽
  for (const key of Object.keys(obj)) {
    defineReactive(propsObj, key, obj[key])
  }
  
  // 更新數據
  propsObj.a = '0' // 觸發響應式
  propsObj.d = '4' // 添加對象屬性,不觸發響應式
  delete  propsObj.a // 刪除對象屬性,不觸發響應式
複製代碼

輸出結果:

可見

若是想要觸發響應式(視圖)更新

你要確保對象的屬性觸發響應式

而在第一個例子中,點擊了 「添加對象屬性」 按鈕,視圖而不更新

由於他在對象作了一個添加屬性的操做

固然!還要注意的是,刪除操做也是沒法觸發響應式的!

總結一下:

1.確保改變對象屬性是對象已經有的屬性,才能監聽到響應式

2.作添加、刪除操做對象屬性,響應式沒法監聽到

其實,在Vue官網有明確說明

數組也是大同小異的

數組的「小異」在於:

Vue重寫了這些數組方法,讓這些方法能夠觸發視圖更新

2.解決辦法?

①使用Vue.set / Vue.delete

使用Vue專門解決此類問題的api

<!--使用vue-cli腳手架-->
<template>
  <div>
    <button @click="change">改變對象屬性</button>
    <button @click="add">添加對象屬性</button>
    <div v-for="(val, key) in obj" :key="key">
      {{ val }}
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      obj: {
        a: '1',
        b: '2',
        c: '3'
      }
    }
  },
  methods: {
    change () {
      this.obj.a = '0'
    },
    add () {
      // 添加對象方法
      this.$set(this.obj, 'd''4')
    }
  }
}
</script>
複製代碼

固然刪除也是相似的,再也不贅述

不過要注意Vue版本: 2.20+

②使用vm.$forceUpdate

使用強制更新視圖

<!--使用vue-cli腳手架-->
<template>
  <div>
    <button @click="change">改變對象屬性</button>
    <button @click="add">添加對象屬性</button>
    <div v-for="(val, key) in obj" :key="key">
      {{ val }}
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      obj: {
        a: '1',
        b: '2',
        c: '3'
      }
    }
  },
  methods: {
    change () {
      this.obj.a = '0'
    },
    add () {
      this.obj.d = '4'
      // 對視圖進行強制更新
      this.$forceUpdate()
    }
  }
}
</script>
複製代碼

③使用深拷貝,從新賦值替換

對於對象,深拷貝能夠使用 JSON.parse(JSON.stringify(obj))

只要改變對象不是同一個對象便可

由於要從新賦值,不該該爲同一對象

這樣才能觸發響應式

舉個例子

<!--使用vue-cli腳手架-->
<template>
  <div>
    <button @click="change">改變對象屬性</button>
    <button @click="add">添加對象屬性</button>
    <div v-for="(val, key) in obj" :key="key">
      {{ val }}
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      obj: {
        a: '1',
        b: '2',
        c: '3'
      }
    }
  },
  methods: {
    change () {
      this.obj.a = '0'
    },
    add () {
      // 不可用 const obj = this.obj
      // 緣由: 他們是恆等的 指向同一個對象
      // 即 conso;e.log(obj === this.obj) // true
      // 使用序列、反序列化進行對象深拷貝,對其從新賦值
      const obj = JSON.parse(JSON.stringify(this.obj))
      obj.d = '4'
      this.obj = obj
    }
  }
}
</script>
複製代碼

而對於數組,能夠使用slice、concat等方法

感謝閱讀

相關文章
相關標籤/搜索