vue開發黑科技--利用引用類型的值處理複雜數據的編輯

如今前端框架和以前的前端開發方式有一個重要的區別————基於數據驅動。咱們不須要再去關注dom自己,而是將主要精力放在如何操做數據上面。實際開發中,能夠抽象成css

既然所有在完數據, 數據類型、算法就跑不掉了。html

本片介紹一個基於引用類型的vue黑科技, 在使用vue開發的時候能夠更加方便。前端

引用類型

首先, 抄一段別人的博客vue

www.cnblogs.com/leiting/p/8…react

1.JavaScript中的變量類型有哪些?

(1)值類型:字符串(string)、數值(number)、布爾值(boolean)、none、undefined

(2)引用類型:對象(Object)、數組(Array)、函數(Function)

2.值類型和引用類型的區別
    (1)值類型:一、佔用空間固定,保存在棧中(當一個方法執行時,每一個方法都會創建本身的內存棧,在這個方法內定義的變量將會逐個放入這塊棧內存裏,隨着方法的執行結束,這個方法的內存棧也將天然銷燬了。所以,全部在方法中定義的變量都是放在棧內存中的;棧中存儲的是基礎變量以及一些對象的引用變量,基礎變量的值是存儲在棧中,而引用變量存儲在棧中的是指向堆中的數組或者對象的地址,這就是爲什麼修改引用類型總會影響到其餘指向這個地址的引用變量。)
    二、保存與複製的是值自己
    三、使用typeof檢測數據的類型
    四、基本類型數據是值類型

(2)引用類型:
    一、佔用空間不固定,保存在堆中(當咱們在程序中建立一個對象時,這個對象將被保存到運行時數據區中,以便反覆利用(由於對象的建立成本一般較大),這個運行時數據區就是堆內存。堆內存中的對象不會隨方法的結束而銷燬,即便方法結束後,這個對象還可能被另外一個引用變量所引用(方法的參數傳遞時很常見),則這個對象依然不會被銷燬,只有當一個對象沒有任何引用變量引用它時,系統的垃圾回收機制纔會在覈實的時候回收它。)
    二、保存與複製的是指向對象的一個指針
    三、使用instanceof檢測數據類型
    四、使用new()方法構造出的對象是引用型
複製代碼

基礎知識就是這些了。 舉個小栗子。算法

總之記住一句話, 值類型傳值, 引用類型傳址

vue中的數據傳遞

在vue中的數據傳遞過程當中, 是不涉及深拷貝的。 是經過props、vuex、v-bind等方法傳遞的引用類型都是傳遞的內存指針 在來個小栗子vuex

<template>
  <div class="content">
    <ul>
      <li v-for="(item, index) in list" :key="index" >
        <span @click="changeItemValue(item)">{{item.title}}</span>
        <span @click="deleteItem(index, list)">刪除</span>
      </li>
      <li @click="addItem(list)">添加item</li>
    </ul>
  </div>
</template>

<script>

export default {
  name: 'index',
  data () {
    return {
      list: [
        {
          title: 'index0',
        },
        {
          title: 'index1'
        },
        {
          title: 'index2'
        },
        {
          title: 'index3'
        },
        {
          title: 'index4'
        }
      ]
    }
  },
  methods: {
    changeItemValue(item) {
      item.title = item.title += '--|'
    },
    deleteItem(index, list) {
      list.splice(index, 1)
    },
    addItem(list) {
      list.push({
        title: 'index' + list.length
      })
    }
  }
}
</script>

<style lang="scss" scoped>
  ul {
    width: 500px;
    margin: 200px auto;
    li {
      width: 200px;
      height: 50px;
      line-height: 50px;
      border: 1px solid pink;
      span:nth-child(2){
        margin-left: 10px;
        color:red;
      }
    }
  }
</style>

複製代碼

在上面的例子中, 咱們都沒有使用this.list[index]的方式來獲取須要修改的對象, 實際上, 方法裏面傳入的item就是this.list裏對應的item的引用
這樣書寫會比經過傳入索引--> 經過索引在list尋找該對象--> 修改該對象要方便的多。 特別是在數據層級比較深的時候。經過索引來查找可能會出現小程序

changeItemValue(itemAIndex, itemBIndex, itemCindex, itemDIndex, value) {
      this.list[itemAIndex].childList[itemBIndex].childList[itemCindex].childList[itemDIndex].title = value
    }
複製代碼

這酸爽~微信小程序

咱們舉一個引用的小場景數組

頁面的列表中, 每個item有一個開關, 須要在保存的時候取出所有所有選中的

<template>
  <div class="content">
    <ul>
      <li v-for="(item, index) in list" :key="index" >
        <span>{{item.title}}</span>
        <span @click="changeItemValue(item)">{{item.isSelect ? '選中' : '未選中'}}</span>
      </li>
      <li @click="save">保存</li>
    </ul>
  </div>
</template>

<script>

export default {
  name: 'index',
  data () {
    return {
      list: [
        {
          title: 'index0',
          isSelect: false
        },
        {
          title: 'index1',
          isSelect: false
        },
        {
          title: 'index2',
          isSelect: false
        },
        {
          title: 'index3',
          isSelect: false
        },
        {
          title: 'index4',
          isSelect: false
        }
      ]
    }
  },
  methods: {
    changeItemValue(item) {
      item.isSelect = !item.isSelect
    },
    save() {
      const data = this.list.filter(_ => _.isSelect)
      console.log(data)
    }
  }
}
</script>

複製代碼

這得益於vue的訪問劫持方法, 在修改對象的時候, 能夠直接觸發對象的觀察者, 觸發數據的更新和各類watch、computed、UI。 而vue的數組類型則是由vue特殊處理過的,才能實現對push、splice等方法的更新,這部分能夠翻翻vue源碼。

接下來咱們講一講經過props的方式向子組件傳遞的狀況, 衆所周知,vue是不容許在組件內修改經過props傳入的值的。實際中呢:

若是傳入的數據是值類型的, 那麼不容許修改這個值 例如 this.string = ''
若是傳入的數據是引用類型, 那麼不容許修改這個數據的內存地址,反之呢,咱們能夠修改這個數據中的子數據
複製代碼

感受上這種操做是違反vue的單向數據流思想的, 可是實在是在開發中太好用了, 因此我只能說這是一種黑科技 來個例子, 咱們修改一下上面的代碼, 將li做爲一個組件來管理一個對象

<template>
  <div class="content">
    <ul>
      <Item v-for="(item, index) in list" :key="index" :item="item" />
      <li @click="save">保存</li>
    </ul>
  </div>
</template>

<script>

import Item from './Item'

export default {
  name: 'index',
  components: { Item },
  data () {
    return {
      list: [
        {
          title: 'index0',
          isSelect: false
        },
        {
          title: 'index1',
          isSelect: false
        },
        {
          title: 'index2',
          isSelect: false
        },
        {
          title: 'index3',
          isSelect: false
        },
        {
          title: 'index4',
          isSelect: false
        }
      ]
    }
  },
  methods: {
    save() {
      const data = this.list.filter(_ => _.isSelect)
      console.log(data)
    }
  }
}
</script>
複製代碼
<template>
  <li>
    <span>{{item.title}}</span>
    <span @click="changeItemValue">{{item.isSelect ? '選中' : '未選中'}}</span>
  </li>
</template>
<script>
export default {
  name: 'Item',
  props: ['item'],
  methods: {
    changeItemValue() {
      this.item.isSelect = !this.item.isSelect
      // 注意 如上面所說 在這裏直接修改item就會報錯, 反之 只修改item下面的值並不會
    }
  }
}
</script>
複製代碼

運行起來, 和以前並無差別, 實際上props傳進去的也是這個對象的引用, 修改的時候父組件的值也被同步修改了。這樣咱們能夠在子組件裏面修改對應的值, 而不須要$emit到父組件去修改。在處理複雜數據的時候, 能夠減小不少負擔 基於這種模式, 咱們在處理一個複雜數據的編輯的時候, 就能夠將每一塊相對獨立的子數據分別用組件去維護。 並且子組件的數據相對對立, 層級淺的時候, 咱們還能夠方便的使用computed計算屬性來實現一些數據的校驗,UI的處理。

在使用vuex的時候

在使用vudex作狀態管理的時候, 狀況和pros差很少。

若是綁定的的數據是值類型的, 那麼不容許修改這個值 例如 this.string = ''
若是綁定的的數據是引用類型, 那麼不容許修改這個數據的內存地址,反之呢,咱們能夠修改這個數據中的子數據
複製代碼

可是有一點, 在使用vue-devtools工具的中會有點差別,簡單來講經過這種方式修改了state中的值,在vue-devtools工具的vuex部分是不會更新的, 可是實際上數據是已經改變了。。 依舊是先前的那個例子, 咱們將數據源從data改成vuex

computed: {
    list() {
      return this.$store.state.list
    }
  }
複製代碼

咱們經過這種方式改變值以後,

在組建視圖, 咱們能看到組建內的isSelect值已經更新了,

父組件的計算屬性中 第一個對象的值也更新了

可是在vuex視圖中 這個值沒有被更新, 打印出來的值也是更新了的。。 若是有強迫症的話, 能夠手動更新一下

mutations: {
    changeList(state, list) {
      state.list = list
    }
  },
  
   this.$store.commit('changeList', this.$store.state.list)
複製代碼

應用

基於這種方法, 咱們在處理複雜數據的時候, 能夠將相對獨立的數據塊分割出來用一個單獨的vue組件來維護和修改。最後的修改結果均可以在原有的數據樹中體現,在提交的時候對這個跟數據進行處理就好。而不用每一次修改都emit到父組件中處理。

注意事項

  • 基於這種值引用的形式, 在子組件修改相應值的時候, 初始值其實已經被污染了, 因此有須要的話要作數據的深拷貝
  • 在處理數組的時候, vue底層對響應的數組操做都有特殊處理過, 因此只要不直接修改數組的引用地址, 均可以觸發數據的更新, 可是不能使用tihs.list = this.list.map(cb)相似的方法, 由於他們都會返回一個新的數組
  • 計算屬性和vuex的getter返回的值不能這樣處理, 準確的說, 這兩個值自己就不能修改, 可是經過計算屬性返回vuex的值例外
  • 在直接修改對象子值的時候, watch會有異常, 沒法正確的得到oldVal的值。
  • 在react中也能夠相似的實現, 可是react不是基於數據訪問劫持的, 因此修改以後還要手動state一次, 微信小程序同理
  • 在微信小程序中, 寫在模板中的函數是不能傳參的,經過data寫在dom上的值不能這麼操做, 組建傳入的值也是至關於深拷貝的, 不能這麼玩了

如上圖, 計算屬性的值正常更新了, 經過deep watch的值, 兩個都是新的值, 沒法取得oldVal, 而不用deep的時候, 這個watch根本不會觸發。

寫在最後

經過這種方式, 在處理比較複雜的數據的時候有奇效, 可是隱隱約約仍是有些怪異,表面穩如老狗,實際慌得不行。 也請大佬解惑

  • 這樣處理是否是違反了單向數據流的思想
  • 會不會有其餘的未知的隱患

10/16 第一次更新

你們都在說複雜的引用類型難以維護的狀況,我不得不吐槽一下了。。 咱們常常拿到的數據是這樣的

[
  {
    "id": 592,
    "catalogueCode": "catalogueCode",
    "catalogueRule": "catalogueRule",
    "catalogueName": "catalogueName",
    "days": "3",
    "expectedDate": null,
    "groups": [
      {
        "groupName": "groupName",
        "subsets": [
          {
            "items": [
              {
                "catalogueRule": "catalogueRule",
                "ruleId": 1,
                "ruleName": "ruleName",
                "catalogueCode": "catalogueCode",
                "ruleScore": 2
              },
              {
                "catalogueRule": "catalogueRule",
                "ruleId": 77,
                "ruleName": "ruleName",
                "catalogueCode": "catalogueCode",
                "ruleScore": 2
              }
            ]
          }
        ]
      }
    ],
    "goals": [
      {
        "goalId": 642,
        "catalogueName": "catalogueName",
        "catalogueRule": "catalogueRule",
        "catalogueCode": "catalogueCode",
        "remark": null,
        "resultId": 592,
        "sortNum": null,
        "measures": [
          {
            "measureId": 2541,
            "catalogueCode": "catalogueCode",
            "catalogueRule": "catalogueRule",
            "catalogueName": "catalogueName",
            "customizeId": null,
            "sort": 0,
            "checked": true,
            "shortActivityMap": {
              "key1": [
                {
                  "catalogueName": "catalogueName",
                  "catalogueRule": "catalogueRule",
                  "catalogueCode": "catalogueCode",
                  "resultId": 2541,
                  "longActivityList": [],
                  "specialSecondType": "3",
                  "frequencyName": "bid",
                  "executionTime": "08:00,16:00",
                  "shortActivityId": 82
                }
              ],
              "key2": [
                {
                  "catalogueName": "catalogueName",
                  "catalogueRule": "catalogueRule",
                  "catalogueCode": "catalogueCode",
                  "resultId": 2541,
                  "longActivityList": [],
                  "specialSecondType": "3",
                  "frequencyName": "bid",
                  "executionTime": "08:00,16:00",
                  "shortActivityId": 82
                }
              ]
            }
          }
        ],
        "appraisals": [
          {
            "appraisalId": 2048,
            "catalogueName": "catalogueName",
            "catalogueRule": "catalogueRule",
            "catalogueCode": "catalogueCode",
            "remark": null,
            "resultId": null,
            "targetCode": "targetCode",
            "sortNum": null
          }
        ]
      }
    ],
    "totalScore": 4
  },
]
複製代碼

這個層級相對仍是比較少的, 最深數據接近6層。關鍵在於每個數據節點都是有對應的增刪改的編輯需求, 而且不能分佈提交。 那麼這個頁面的編輯。怎麼處理?

  • 方案一: 徹底不組件化, 一把撈

這個組件裏面維護的狀態, 已經不忍直視了。。代碼就更不用說, 幾千行一把撈

  • 方案二: 按照數據節點拆分紅子組件, 經過props傳遞數據,每個修改動做都一路emit到根組件進行。
    乍一看起來徹底OK, 可是位於第六層的數據要向上傳遞多少次呢。 對應的索引有多少? 根組件每個層級的數據都要寫一個增刪改方法, 根組件又亂掉了
  • 方案三: 將所有數據維護到vuex處理, 全部修改數據的方法使用commit在vuex中修改
    這種方法,其實是吧方案二的根組件數據處理搬到的vuex中, 而且省下了emit和props, 綜合起來算是比較好的方案了。 很遺憾的是, 同事不接受。。
  • 方案四: 就是上文提到的方法。
    關於維護: 在vue的谷歌開發插件中是能夠完整的看到數據的流向、改變的
    每一個一個組件維護了什麼,修改了什麼是和數據結構徹底對應,並在當前清晰可見,維護起來起來應該還ok?

不過如評論所說, 若是vue認爲修改props內部數據是缺陷或者是BUG,之後會修復的話那,那毫無疑問我要加班了。。

第二次寫在最後

我用的這種方法, 我以爲不踏實,我寫出來和你們一塊兒討論,拋磚引玉, 但願有大佬能指點或者分享經驗就是個人目的, 評論一上來就開罵是咋回事。

相關文章
相關標籤/搜索