最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue3響應式對象數組不能實時DOM更新問題解決辦法

 更新時間:2024年07月11日 09:02:43   作者:阿姨給我倒一杯卡布奇諾  
在寫大文件上傳時,碰到關于 vue2 跟 vue3 對在循環(huán)中使用異步,并動態(tài)把普通對象添加進響應式數據,在異步前后修改該普通對象的某個屬性,導致 vue2 跟 vue3 的視圖更新不一致,引發(fā)一系列的思考,所以本文介紹了Vue3響應式對象數組不能實時DOM更新問題解決辦法

前言

之所以寫該文章是在自己寫大文件上傳時,碰到關于 vue2vue3在循環(huán)中使用異步,并動態(tài)把普通對象添加進響應式數據,在異步前后修改該普通對象的某個屬性,導致 vue2 跟 vue3 的視圖更新不一致,引發(fā)一系列的思考。

forEach 中使用異步

forEach() 期望的是一個同步函數,它不會等待 Promise 兌現(xiàn)。在使用 Promise(或異步函數)作為 forEach 回調時,請確保你意識到這一點可能帶來的影響。

以上解釋是 MDN 關于對 forEach 的部分解釋,這里要注意的是,在 forEach 中使用異步是不會等待異步而暫停。所以如果不了解的小伙伴要注意一下,那就讓我們做個測試。

我們先定義一個異步回調函數:

// 延時回調函數
const asyncFunc = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('執(zhí)行延遲:', new Date())
      resolve()
    }, 1000)
  })
}

再定義一個關于 forEach 的函數并執(zhí)行

const forEachFunc = () => {
  let arr = new Array(5).fill({ test: 'test' })
  arr.forEach(async (item, i) => {
    console.log(`異步前${i}:`,new Date())
    await asyncFunc()
   console.log(`異步后${i}:`,new Date())
  })
  console.log('forEach外部:',new Date())
}
forEachFunc()

讓我們看看最終的打印結果

根據輸出結果可以看到:有五次循環(huán),但五次循環(huán)基本是按順序同步執(zhí)行,在每次循環(huán)遇到異步后,并不會阻塞 forEach 外部代碼執(zhí)行,而是把每次循環(huán)單獨處理異步,在內部等待異步完成后處理邏輯。

for 中使用異步

for 循環(huán)是會阻塞下一個循環(huán)并等待本次異步完后再處理下一個循環(huán),等待全部循環(huán)完后再執(zhí)行 for 循環(huán)下面的代碼。

那讓我們再驗證以上的 for 循環(huán)異步理論是否正確:

const forFunc = async () => {
  let arr = new Array(5).fill({ test: 'test' })
  for (let i = 0; i < arr.length; i++) {
    console.log(`異步前${i}:`, new Date())
    await asyncFunc()
    console.log(`異步后${i}:`, new Date())
  }
  console.log('for外部:', new Date())
}
forFunc()

根據控制臺輸出可以看到,通過打印的 i 跟時間可以判斷:先執(zhí)行完當前循環(huán)的異步后再執(zhí)行一下循環(huán),且等所有循環(huán)處理完再執(zhí)行 for 循環(huán)外部的代碼

需求

因為在大文件上傳中涉及到文件上傳狀態(tài)的更變,現(xiàn)在需求是:需要在循環(huán)中把一個普通對象 push 到響應式數組中,并修改該對象的 state 屬性,在等待一個異步回調后,再去修改 state 值,并要在頁面視圖中展現(xiàn)改變。

vue2 代碼實現(xiàn)

在模板代碼中,直接在視圖展示全部數組,并用 v-for 遍歷

<template>
  <div>
    數組數據:
    <div>
      {{ testArr }}
    </div>
    <div style="margin-top: 50px">
      <div v-for="item in testArr" :key="item.id">
        {{ item.state }}
      </div>
    </div>
  </div>
</template>

在script 中,定義響應式數組,以及一個異步回調函數,并分別定義用 for 循環(huán)跟 forEach 處理異步修改狀態(tài)的方法,并在 mounted 生命周期里分別執(zhí)行這兩個方法

<script>
export default {
  data() {
    return {
      testArr: [],
    }
  },
  mounted() {
    this.forFunc()
    // this.forEachFunc()
  },
  methods: {
    asyncFunc() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log('執(zhí)行延遲:', new Date())
          resolve('延遲成功')
        }, 1000)
      })
    },
    // for循環(huán)
    async forFunc() {
      let arr = new Array(5).fill({ test: 'test' })

      for (let i = 0; i < arr.length; i++) {
        let obj = {
          id: i,
          state: 'state' + i,
        }
        this.testArr.push(obj)
        obj.state = 'before前的name'
        await this.asyncFunc()
        obj.state = 'after后的name'
      }
      console.log(this.testArr, 'this.testArr')
    },
    // forEach循環(huán)
    forEachFunc() {
      let arr = new Array(5).fill({ test: 'test' })
      arr.forEach(async (item, i) => {
        let obj = {
          id: i,
          state: 'state' + i,
        }
        this.testArr.push(obj)
        obj.state = 'before前的name'
        await this.asyncFunc()
        obj.state = 'after的name'
      })
      console.log(this.testArr, 'this.testArr')
    },
  },
}
</script>

1. forEach 循環(huán)效果

可以看到刷新頁面后,在一秒延遲后數組內所有對象的 state 屬性同步變化

2. for 循環(huán)效果展示

可以看到在 Vue2 中 DOM 視圖是正常更新,且用 for 循環(huán)是先執(zhí)行完當前循環(huán)的異步后再執(zhí)行一下循環(huán),且等所有循環(huán)處理完再執(zhí)行 for 循環(huán)外部的代碼

3. 小結

在 vue2 中在循環(huán)中使用異步,并動態(tài)把普通對象添加進響應式數組,在異步前后修改該普通對象的某個屬性,修改的是該數組具體對象某一屬性,且視圖能正常更新。

vue3 代碼實現(xiàn)

模板代碼中,直接在視圖展示全部數組,并用 v-for 遍歷

<template>
  <div>
    數組數據:
    <div>
      {{ testArr }}
    </div>
    <div style="margin-top: 50px">
      <div v-for="item in testArr" :key="item.id">
        {{ item.state }}
      </div>
    </div>
  </div>
</template>

在script 中,定義響應式數組,以及一個異步回調函數,并分別定義用 for 循環(huán)跟 forEach 處理異步修改狀態(tài)的方法,并在 mounted 生命周期里分別執(zhí)行這兩個方法

<script setup>
import { ref, onMounted, reactive } from 'vue'
const testArr = ref([])
  // 延時回調
const asyncFunc = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('執(zhí)行延遲:', new Date())
      resolve()
    }, 1000)
  })
}

  // for-正常push進去后直接修改obj
const forFunc = async () => {
  let arr = new Array(5).fill({ test: 'test' })

  for (let i = 0; i < arr.length; i++) {
    let obj = {
      id: i,
      state: 'state' + i,
    }
    testArr.value.push(obj)
    obj.state = 'before前的name'
    await asyncFunc()
    obj.state = 'after的name'
  }
  console.log(testArr.value, 'testArr.value')
}

  // forEach-正常push進去后直接修改obj
const forEachFunc = () => {
  let arr = new Array(5).fill({ test: 'test' })
  arr.forEach(async (item, i) => {
    let obj = {
      id: i,
      state: 'state' + i,
    }
    testArr.value.push(obj)
    obj.state = 'before前的name'
    await asyncFunc()
    obj.state = 'after的name'
  })
  console.log(testArr.value, 'testArr.value')
}

  onMounted(() => {
  // forFunc()
  forEachFunc()

})

</script>

1. forEach 循環(huán)效果

!可以看到,在異步后面的 state 修改并沒有生效,但是為什么在控制臺console.log的值卻又改變了?

關于console.log

這里為什么要說 console.log 呢,可能很多人沒注意在控制臺用 console 打印對象時,是會隨著值變化也不斷更新的。所以你在最后中看到的值并不是當時打印的值,要注意!

以下是 MDN 的部分解釋

所以這就是解釋了以上現(xiàn)象,為什么最終在打印的數組,是改變后的。但為什么視圖沒有更新呢?讓我們再使用 for 循環(huán)+ await 測試看看會發(fā)生什么

2. for 循環(huán)效果

onMounted(() => {
  // forFunc()
  forEachFunc()
})

在頁面中可以看到,for 循環(huán)是按順序異步更新的,但是最后一個 item 在視圖并沒有更新,控制臺打印的最終值確實更新了的

那到底是什么原因呢?初步判斷:vue3 的響應式監(jiān)聽的是代理對象,因為在循環(huán)中使用異步,對普通對象的修改可能不能及時監(jiān)聽到,而 vue2 生效的原因是在于它本身就是在原對象的 get set 上操作的。

至于為什么 for 循環(huán)+異步會生效,而最后一個未更新,因為在每個 item 循環(huán)中,push 觸發(fā)了數組改變,從而導致視圖更新,但在最后循環(huán)中,在 await 后面并沒有更改數組。

那就讓我們多做幾個實驗測試一下

3. 用reactive創(chuàng)建對象

// for-用reactive創(chuàng)建對象
const forFunc2 = async () => {
  let arr = new Array(5).fill({ test: 'test' })

  for (let i = 0; i < arr.length; i++) {
    let obj = reactive({
      id: i,
      state: 'state' + i,
    })
    testArr.value.push(obj)
    obj.state = 'before前的name'
    await asyncFunc()
    obj.state = 'after的name'
  }
  console.log(testArr.value, 'testArr.value')
}

// forEach-用reactive創(chuàng)建對象
const forEachFunc2 = () => {
  let arr = new Array(5).fill({ test: 'test' })
  arr.forEach(async (item, i) => {
    let obj = reactive({
      id: i,
      state: 'state' + i,
    })
    testArr.value.push(obj)
    obj.state = 'before前的name'
    await asyncFunc()
    obj.state = 'after的name'
  })
  console.log(testArr.value, 'testArr.value')
}

那讓我們來分別看一下這兩個函數執(zhí)行的效果

for 循環(huán):

可以看到用 reactive 創(chuàng)建的代理對象會被Vue跟蹤到,且視圖進行了實時更新

forEach 循環(huán):

最終結果也是能正常更新

4. 直接取數組下標對象修改

直接通過 testArr.value[i].state = 'after的name'去修改。

// for-直接取數組下標對象修改
const forFunc3 = async () => {
  let arr = new Array(5).fill({ test: 'test' })

  for (let i = 0; i < arr.length; i++) {
    let obj = reactive({
      id: i,
      state: 'state' + i,
    })
    testArr.value.push(obj)
    testArr.value[i].state = 'before前的name'
    await asyncFunc()
    testArr.value[i].state = 'after的name'
  }
  console.log(testArr.value, 'testArr.value')
}

// forEach-直接取數組下標對象修改
const forEachFunc3 = () => {
  let arr = new Array(5).fill({ test: 'test' })
  arr.forEach(async (item, i) => {
    let obj = {
      id: i,
      state: 'state' + i,
    }
    testArr.value.push(obj)
    testArr.value[i].state = 'before前的name'
    await asyncFunc()
    testArr.value[i].state = 'after的name'
  })
  console.log(testArr.value, 'testArr.value')
}

for 循環(huán):

forEach 循環(huán):

通過取數組下標對象修改是能實時更新的,因為相當于直接修改響應式對象的某一個值,這樣Vue3也能正常監(jiān)聽到并視圖更新

5. 重新賦值對象引用地址

通過 obj = testArr.value[i]方式去修改。

// for-重新賦值對象引用
const forFunc4 = async () => {
  let arr = new Array(5).fill({ test: 'test' })

  for (let i = 0; i < arr.length; i++) {
    let obj = reactive({
      id: i,
      state: 'state' + i,
    })
    testArr.value.push(obj)
    obj = testArr.value[i]
    obj.state = 'before前的name'
    await asyncFunc()
    obj.state = 'after的name'
  }
  console.log(testArr.value, 'testArr.value')
}

// forEach-重新賦值對象引用
const forEachFunc4 = () => {
  let arr = new Array(5).fill({ test: 'test' })
  arr.forEach(async (item, i) => {
    let obj = {
      id: i,
      state: 'state' + i,
    }
    testArr.value.push(obj)
    obj = testArr.value[i]
    obj.state = 'before前的name'
    await asyncFunc()
    obj.state = 'after的name'
  })
  console.log(testArr.value, 'testArr.value')
}

for 循環(huán):

forEach 循環(huán):

通過引用響應式數據對象地址是能實時更新的,同樣的效果,這是因為兩個對象引用的是同一個對象地址,從而實現(xiàn)被Vue3追蹤到并進行視圖更新

小結

根據這幾種測試可以得出一個結論:在vue3中,若是在循環(huán)中并動態(tài)把普通對象添加(push)進響應式數據,在異步前后修改直接該普通對象的某個屬性,不一定被Vue追蹤到這個變化,并在需要時更新 DOM。

所以如果想要實現(xiàn)DOM實時更新,應該 1.用 reactive 去創(chuàng)建該對象;2.直接使用該數組指定下標的對象修改屬性;3.使用對象賦值(=)的方式直接引用響應式數據的地址

溫馨提示:就算用Vue2的寫法直接放在Vue3版本的項目中,最終效果也是同Vue3寫法一樣,無論是vite創(chuàng)建還是vue-cli創(chuàng)建的Vue3項目。

以上就是Vue3響應式對象數組不能實時DOM更新問題解決辦法的詳細內容,更多關于Vue3數組不能實時DOM更新的資料請關注腳本之家其它相關文章!

相關文章

  • Vue?Router?返回后記住滾動條位置的實現(xiàn)方法

    Vue?Router?返回后記住滾動條位置的實現(xiàn)方法

    使用?Vue?router?創(chuàng)建?SPA(Single?Page?App),往往有這種需求:首頁是列表頁,點擊列表項進入詳情頁,在詳情頁點擊返回首頁后,希望看到的是,首頁不刷新,并且滾動條停留在之前的位置,這篇文章主要介紹了Vue?Router?返回后記住滾動條位置的實現(xiàn)方法,需要的朋友可以參考下
    2023-09-09
  • vue在自定義組件上使用v-model和.sync的方法實例

    vue在自定義組件上使用v-model和.sync的方法實例

    自定義組件的v-model和.sync修飾符其實本質上都是vue的語法糖,用于實現(xiàn)父子組件的"數據"雙向綁定,下面這篇文章主要給大家介紹了關于vue在自定義組件上使用v-model和.sync的相關資料,需要的朋友可以參考下
    2022-07-07
  • Vue中keyup.enter和blur事件沖突的問題及解決

    Vue中keyup.enter和blur事件沖突的問題及解決

    這篇文章主要介紹了Vue中keyup.enter和blur事件沖突的問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • 淺談vue限制文本框輸入數字的正確姿勢

    淺談vue限制文本框輸入數字的正確姿勢

    這篇文章主要介紹了vue限制文本框輸入數字的正確姿勢,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-09-09
  • Vue項目使用px2rem方法示例詳解

    Vue項目使用px2rem方法示例詳解

    這篇文章主要為大家介紹了Vue項目使用px2rem的方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-07-07
  • vue+F2生成折線圖的方法

    vue+F2生成折線圖的方法

    這篇文章主要為大家詳細介紹了vue+F2生成折線圖的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • vue--點擊當前增加class,其他刪除class的方法

    vue--點擊當前增加class,其他刪除class的方法

    今天小編就為大家分享一篇vue--點擊當前增加class,其他刪除class的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • vue-cli3.0使用及部分配置詳解

    vue-cli3.0使用及部分配置詳解

    這篇文章主要介紹了vue-cli3.0使用及部分配置詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08
  • vue+elementUI實現(xiàn)當渲染文本超出一定字數時顯示省略號

    vue+elementUI實現(xiàn)當渲染文本超出一定字數時顯示省略號

    這篇文章主要介紹了vue+elementUI實現(xiàn)當渲染文本超出一定字數時顯示省略號,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • vue2.0 使用element-ui里的upload組件實現(xiàn)圖片預覽效果方法

    vue2.0 使用element-ui里的upload組件實現(xiàn)圖片預覽效果方法

    今天小編就為大家分享一篇vue2.0 使用element-ui里的upload組件實現(xiàn)圖片預覽效果方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09

最新評論

军事| 望江县| 城固县| 苍南县| 赣州市| 金华市| 高尔夫| 共和县| 商南县| 广安市| 巫溪县| 娄底市| 兴安县| 永泰县| 金湖县| 泊头市| 抚松县| 临海市| 邵东县| 安龙县| 蒲城县| 宝丰县| 胶南市| 九龙坡区| 清新县| 涟源市| 通海县| 突泉县| 大足县| 宾川县| 延川县| 乌拉特前旗| 巩留县| 工布江达县| 化德县| 上虞市| 长汀县| 桂阳县| 大厂| 新密市| 昆山市|